Merge pull request #196 from hchen2020/master

Clean code.
This commit is contained in:
Haiping 2023-10-31 20:51:12 -05:00 committed by GitHub
commit 0aa52c15d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 143 additions and 181 deletions

View file

@ -1,3 +1,5 @@
using System.Text.Json;
namespace BotSharp.Abstraction.Conversations;
/// <summary>
@ -11,6 +13,7 @@ public interface IConversationStateService
bool ContainsState(string name);
ConversationState GetStates();
IConversationStateService SetState<T>(string name, T value);
void SaveStateByArgs(JsonDocument args);
void CleanState();
void Save();
}

View file

@ -1,13 +0,0 @@
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.Abstraction.Routing;
public interface IRouterInstance
{
string AgentId { get; }
Agent Router { get; }
RoutingItem[] GetRoutingItems();
List<RoutingHandlerDef> GetHandlers();
IRouterInstance Load();
RoutingRule[] GetRulesByName(string name);
}

View file

@ -1,8 +1,14 @@
using BotSharp.Abstraction.Routing.Models;
namespace BotSharp.Abstraction.Routing;
public interface IRoutingService
{
Agent Router { get; }
RoutingItem[] GetRoutingItems();
RoutingRule[] GetRulesByName(string name);
RoutingRule[] GetRulesByAgentId(string id);
List<RoutingHandlerDef> GetHandlers();
void ResetRecursiveCounter();
Task<bool> InvokeAgent(string agentId, List<RoleDialogModel> dialogs);
Task<RoleDialogModel> InstructLoop(RoleDialogModel message);

View file

@ -81,7 +81,6 @@ public static class BotSharpServiceCollectionExtensions
});
services.AddScoped<IExecutor, InstructExecutor>();
services.AddScoped<IRouterInstance, RouterInstance>();
services.AddScoped<IRoutingService, RoutingService>();
if (myDatabaseSettings.Default == "FileRepository")

View file

@ -128,4 +128,23 @@ public class ConversationStateService : IConversationStateService, IDisposable
{
return _states.ContainsKey(name) && !string.IsNullOrEmpty(_states[name]);
}
public void SaveStateByArgs(JsonDocument args)
{
if (args == null)
{
return;
}
if (args.RootElement is JsonElement root)
{
foreach (JsonProperty property in root.EnumerateObject())
{
if (!string.IsNullOrEmpty(property.Value.ToString()))
{
SetState(property.Name, property.Value);
}
}
}
}
}

View file

@ -3,7 +3,6 @@ using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Planning;
@ -36,13 +35,14 @@ public class HFPlanner : IPlaner
{
try
{
response = completion.GetChatCompletions(router, new List<RoleDialogModel>
var dialogs = new List<RoleDialogModel>
{
new RoleDialogModel(AgentRole.User, next)
{
MessageId = messageId
}
});
};
response = completion.GetChatCompletions(router, dialogs);
inst = response.Content.JsonContent<FunctionCallFromLlm>();
break;
@ -80,18 +80,15 @@ public class HFPlanner : IPlaner
public async Task<bool> AgentExecuted(FunctionCallFromLlm inst, RoleDialogModel message)
{
var context = _services.GetRequiredService<RoutingContext>();
context.Pop();
context.Empty();
return true;
}
private string GetNextStepPrompt(Agent router)
{
var template = router.Templates.First(x => x.Name == "next_step_prompt").Content;
var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
{
});
var prompt = render.Render(template, router.TemplateDict);
return prompt.Trim();
}
}

View file

@ -3,7 +3,6 @@ using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Templating;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
namespace BotSharp.Core.Planning;
@ -24,14 +23,15 @@ public class NaivePlanner : IPlaner
var inst = new FunctionCallFromLlm();
var agentService = _services.GetRequiredService<IAgentService>();
// text completion
/*var agentService = _services.GetRequiredService<IAgentService>();
var instruction = agentService.RenderedInstruction(router);
var content = $"{instruction}\r\n###\r\n{next}";
// text completion
content = content + "\r\nResponse: ";
var completion = CompletionProvider.GetTextCompletion(_services);*/
var completion = CompletionProvider.GetTextCompletion(_services);
// chat completion
var completion = CompletionProvider.GetChatCompletion(_services);
int retryCount = 0;
while (retryCount < 3)
@ -39,11 +39,17 @@ public class NaivePlanner : IPlaner
string text = string.Empty;
try
{
text = await completion.GetCompletion(content, router.Id, messageId);
var response = new RoleDialogModel(AgentRole.Assistant, text)
// text completion
// text = await completion.GetCompletion(content, router.Id, messageId);
var dialogs = new List<RoleDialogModel>
{
MessageId = messageId
new RoleDialogModel(AgentRole.User, next)
{
MessageId = messageId
}
};
var response = completion.GetChatCompletions(router, dialogs);
inst = response.Content.JsonContent<FunctionCallFromLlm>();
break;
}

View file

@ -81,9 +81,9 @@ public class RouteToAgentFn : IFunctionCallback
private bool HasMissingRequiredField(RoleDialogModel message, out string agentId)
{
var args = JsonSerializer.Deserialize<RoutingArgs>(message.FunctionArgs);
var router = _services.GetRequiredService<IRouterInstance>();
var routing = _services.GetRequiredService<IRoutingService>();
var routingRules = router.GetRulesByName(args.AgentName);
var routingRules = routing.GetRulesByName(args.AgentName);
if (routingRules == null || !routingRules.Any())
{

View file

@ -19,9 +19,9 @@ public class RoutingAgentHook : AgentHookBase
{
dict["router"] = _agent;
var router = _services.GetRequiredService<IRouterInstance>();
dict["routing_agents"] = router.GetRoutingItems();
dict["routing_handlers"] = router.GetHandlers();
var routing = _services.GetRequiredService<IRoutingService>();
dict["routing_agents"] = routing.GetRoutingItems();
dict["routing_handlers"] = routing.GetHandlers();
return base.OnInstructionLoaded(template, dict);
}

View file

@ -1,123 +0,0 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Settings;
namespace BotSharp.Core.Routing;
public class RouterInstance : IRouterInstance
{
protected readonly IServiceProvider _services;
protected readonly ILogger _logger;
protected readonly RoutingSettings _settings;
private Agent _router;
public Agent Router => _router;
public virtual string AgentId => _router.Id;
public RouterInstance(IServiceProvider services,
ILogger<RouterInstance> logger,
RoutingSettings settings)
{
_services = services;
_logger = logger;
_settings = settings;
}
public IRouterInstance Load()
{
var agentService = _services.GetRequiredService<IAgentService>();
_router = agentService.LoadAgent(_settings.RouterId).Result;
return this;
}
public List<RoutingHandlerDef> GetHandlers()
{
var planer = _services.GetRequiredService<IPlaner>();
return _services.GetServices<IRoutingHandler>()
.Where(x => x.Planers == null || x.Planers.Contains(planer.GetType().Name))
.Where(x => !string.IsNullOrEmpty(x.Description))
.Select((x, i) => new RoutingHandlerDef
{
Name = x.Name,
Description = x.Description,
Parameters = x.Parameters
}).ToList();
}
#if !DEBUG
[MemoryCache(10 * 60)]
#endif
protected RoutingRule[] GetRoutingRecords()
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agents = db.GetAgents(disabled: false, allowRouting: true);
var records = agents.SelectMany(x =>
{
x.RoutingRules.ForEach(r =>
{
r.AgentId = x.Id;
r.AgentName = x.Name;
});
return x.RoutingRules;
}).ToArray();
// Filter agents by profile
var state = _services.GetRequiredService<IConversationStateService>();
var name = state.GetState("channel");
var specifiedProfile = agents.FirstOrDefault(x => x.Profiles.Contains(name));
if (specifiedProfile != null)
{
records = records.Where(x => specifiedProfile.Profiles.Contains(name)).ToArray();
}
return records;
}
#if !DEBUG
[MemoryCache(10 * 60)]
#endif
public RoutingItem[] GetRoutingItems()
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agents = db.GetAgents(disabled: false, allowRouting: true);
return agents.Select(x => new RoutingItem
{
AgentId = x.Id,
Description = x.Description,
Name = x.Name,
RequiredFields = x.RoutingRules
.Where(p => p.Required)
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type)
{
Required = p.Required
}).ToList(),
OptionalFields = x.RoutingRules
.Where(p => !p.Required)
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type)
{
Required = p.Required
}).ToList()
}).ToArray();
}
public RoutingRule[] GetRulesByName(string name)
{
return GetRoutingRecords()
.Where(x => x.AgentName.ToLower() == name.ToLower())
.ToArray();
}
public RoutingRule[] GetRulesByAgentId(string id)
{
return GetRoutingRecords()
.Where(x => x.AgentId == id)
.ToArray();
}
}

View file

@ -41,7 +41,8 @@ public partial class RoutingService
{
// execute function
// Save states
SaveStateByArgs(JsonSerializer.Deserialize<JsonDocument>(message.FunctionArgs));
var states = _services.GetRequiredService<IConversationStateService>();
states.SaveStateByArgs(message.FunctionArgs?.JsonContent<JsonDocument>());
var conversationService = _services.GetRequiredService<IConversationService>();
// Call functions

View file

@ -1,6 +1,7 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Routing.Settings;
@ -12,7 +13,6 @@ public partial class RoutingService : IRoutingService
{
private readonly IServiceProvider _services;
private readonly RoutingSettings _settings;
private readonly IRouterInstance _routerInstance;
private readonly ILogger _logger;
private Agent _router;
public Agent Router => _router;
@ -24,13 +24,11 @@ public partial class RoutingService : IRoutingService
public RoutingService(IServiceProvider services,
RoutingSettings settings,
ILogger<RoutingService> logger,
IRouterInstance routerInstance)
ILogger<RoutingService> logger)
{
_services = services;
_settings = settings;
_logger = logger;
_routerInstance = routerInstance;
}
public async Task<RoleDialogModel> ExecuteOnce(Agent agent, RoleDialogModel message)
@ -60,11 +58,12 @@ public partial class RoutingService : IRoutingService
public async Task<RoleDialogModel> InstructLoop(RoleDialogModel message)
{
_router = _routerInstance.Load()
.Router;
var agentService = _services.GetRequiredService<IAgentService>();
_router = await agentService.LoadAgent(_settings.RouterId);
RoleDialogModel response = default;
var states = _services.GetRequiredService<IConversationStateService>();
var conv = _services.GetRequiredService<IConversationService>();
var dialogs = conv.GetDialogHistory();
@ -81,12 +80,13 @@ public partial class RoutingService : IRoutingService
var conversation = await GetConversationContent(dialogs);
_router.TemplateDict["conversation"] = conversation;
_router.TemplateDict["planner"] = _settings.Planner;
// Get instruction from Planner
var inst = await planner.GetNextInstruction(_router, message.MessageId);
// Save states
SaveStateByArgs(inst.Arguments);
states.SaveStateByArgs(inst.Arguments);
#if DEBUG
Console.WriteLine($"*** Next Instruction *** {inst}", Color.GreenYellow);
@ -104,23 +104,90 @@ public partial class RoutingService : IRoutingService
return response;
}
protected void SaveStateByArgs(JsonDocument args)
public List<RoutingHandlerDef> GetHandlers()
{
if (args == null)
var planer = _services.GetRequiredService<IPlaner>();
return _services.GetServices<IRoutingHandler>()
.Where(x => x.Planers == null || x.Planers.Contains(planer.GetType().Name))
.Where(x => !string.IsNullOrEmpty(x.Description))
.Select((x, i) => new RoutingHandlerDef
{
Name = x.Name,
Description = x.Description,
Parameters = x.Parameters
}).ToList();
}
#if !DEBUG
[MemoryCache(10 * 60)]
#endif
protected RoutingRule[] GetRoutingRecords()
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agents = db.GetAgents(disabled: false, allowRouting: true);
var records = agents.SelectMany(x =>
{
return;
x.RoutingRules.ForEach(r =>
{
r.AgentId = x.Id;
r.AgentName = x.Name;
});
return x.RoutingRules;
}).ToArray();
// Filter agents by profile
var state = _services.GetRequiredService<IConversationStateService>();
var name = state.GetState("channel");
var specifiedProfile = agents.FirstOrDefault(x => x.Profiles.Contains(name));
if (specifiedProfile != null)
{
records = records.Where(x => specifiedProfile.Profiles.Contains(name)).ToArray();
}
var stateService = _services.GetRequiredService<IConversationStateService>();
if (args.RootElement is JsonElement root)
return records;
}
#if !DEBUG
[MemoryCache(10 * 60)]
#endif
public RoutingItem[] GetRoutingItems()
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agents = db.GetAgents(disabled: false, allowRouting: true);
return agents.Select(x => new RoutingItem
{
foreach (JsonProperty property in root.EnumerateObject())
{
if (!string.IsNullOrEmpty(property.Value.ToString()))
AgentId = x.Id,
Description = x.Description,
Name = x.Name,
RequiredFields = x.RoutingRules
.Where(p => p.Required)
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type)
{
stateService.SetState(property.Name, property.Value);
}
}
}
Required = p.Required
}).ToList(),
OptionalFields = x.RoutingRules
.Where(p => !p.Required)
.Select(p => new ParameterPropertyDef(p.Field, p.Description, type: p.Type)
{
Required = p.Required
}).ToList()
}).ToArray();
}
public RoutingRule[] GetRulesByName(string name)
{
return GetRoutingRecords()
.Where(x => x.AgentName.ToLower() == name.ToLower())
.ToArray();
}
public RoutingRule[] GetRulesByAgentId(string id)
{
return GetRoutingRecords()
.Where(x => x.AgentId == id)
.ToArray();
}
}

View file

@ -282,7 +282,7 @@ public class ChatCompletionProvider : IChatCompletion
{
return $"{x.Role}: {x.Content}";
}));
prompt += $"\r\n[INSTRUCTION]\r\n{verbose}\r\n";
prompt += $"{verbose}\r\n";
verbose = string.Join("\r\n", chatCompletionsOptions.Messages
.Where(x => x.Role != AgentRole.System).Select(x =>
@ -291,7 +291,7 @@ public class ChatCompletionProvider : IChatCompletion
$"{x.Role}: {x.Name} => {x.Content}" :
$"{x.Role}: {x.Content}";
}));
prompt += $"\r\n[CONVERSATION]\r\n{verbose}\r\n";
prompt += $"\r\n{verbose}\r\n";
}
if (chatCompletionsOptions.Functions.Count > 0)