diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs index 07b713c9c..eeb39f731 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationStateService.cs @@ -1,3 +1,5 @@ +using System.Text.Json; + namespace BotSharp.Abstraction.Conversations; /// @@ -11,6 +13,7 @@ public interface IConversationStateService bool ContainsState(string name); ConversationState GetStates(); IConversationStateService SetState(string name, T value); + void SaveStateByArgs(JsonDocument args); void CleanState(); void Save(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRouterInstance.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRouterInstance.cs deleted file mode 100644 index 4e51e18ae..000000000 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRouterInstance.cs +++ /dev/null @@ -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 GetHandlers(); - IRouterInstance Load(); - RoutingRule[] GetRulesByName(string name); -} diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs index eee7c942e..5a2801727 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/IRoutingService.cs @@ -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 GetHandlers(); void ResetRecursiveCounter(); Task InvokeAgent(string agentId, List dialogs); Task InstructLoop(RoleDialogModel message); diff --git a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs index e36e9a705..52c704887 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs @@ -81,7 +81,6 @@ public static IServiceCollection AddBotSharp(this IServiceCollection services, I }); services.AddScoped(); - services.AddScoped(); services.AddScoped(); if (myDatabaseSettings.Default == "FileRepository") diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index 7004b929a..cd4fe9c6a 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -128,4 +128,23 @@ public bool ContainsState(string name) { 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); + } + } + } + } } diff --git a/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs index c618ce4c4..73a2a2422 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/HFPlanner.cs @@ -3,7 +3,6 @@ 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 async Task GetNextInstruction(Agent router, string m { try { - response = completion.GetChatCompletions(router, new List + var dialogs = new List { new RoleDialogModel(AgentRole.User, next) { MessageId = messageId } - }); + }; + response = completion.GetChatCompletions(router, dialogs); inst = response.Content.JsonContent(); break; @@ -80,18 +80,15 @@ public async Task AgentExecuting(FunctionCallFromLlm inst, RoleDialogModel public async Task AgentExecuted(FunctionCallFromLlm inst, RoleDialogModel message) { var context = _services.GetRequiredService(); - 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(); - return render.Render(template, new Dictionary - { - }); + var prompt = render.Render(template, router.TemplateDict); + return prompt.Trim(); } } diff --git a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs index 45737fc5f..249f9ec1a 100644 --- a/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs +++ b/src/Infrastructure/BotSharp.Core/Planning/NaivePlanner.cs @@ -3,7 +3,6 @@ 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 async Task GetNextInstruction(Agent router, string m var inst = new FunctionCallFromLlm(); - var agentService = _services.GetRequiredService(); + // text completion + /*var agentService = _services.GetRequiredService(); 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 async Task GetNextInstruction(Agent router, string m 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 { - MessageId = messageId + new RoleDialogModel(AgentRole.User, next) + { + MessageId = messageId + } }; + var response = completion.GetChatCompletions(router, dialogs); + inst = response.Content.JsonContent(); break; } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs index fd916b127..751dd156a 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Functions/RouteToAgentFn.cs @@ -81,9 +81,9 @@ public async Task Execute(RoleDialogModel message) private bool HasMissingRequiredField(RoleDialogModel message, out string agentId) { var args = JsonSerializer.Deserialize(message.FunctionArgs); - var router = _services.GetRequiredService(); + var routing = _services.GetRequiredService(); - var routingRules = router.GetRulesByName(args.AgentName); + var routingRules = routing.GetRulesByName(args.AgentName); if (routingRules == null || !routingRules.Any()) { diff --git a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs index 2a860267e..07647e10f 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Hooks/RoutingAgentHook.cs @@ -19,9 +19,9 @@ public override bool OnInstructionLoaded(string template, Dictionary(); - dict["routing_agents"] = router.GetRoutingItems(); - dict["routing_handlers"] = router.GetHandlers(); + var routing = _services.GetRequiredService(); + dict["routing_agents"] = routing.GetRoutingItems(); + dict["routing_handlers"] = routing.GetHandlers(); return base.OnInstructionLoaded(template, dict); } diff --git a/src/Infrastructure/BotSharp.Core/Routing/RouterInstance.cs b/src/Infrastructure/BotSharp.Core/Routing/RouterInstance.cs deleted file mode 100644 index 216c354fb..000000000 --- a/src/Infrastructure/BotSharp.Core/Routing/RouterInstance.cs +++ /dev/null @@ -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 logger, - RoutingSettings settings) - { - _services = services; - _logger = logger; - _settings = settings; - } - - public IRouterInstance Load() - { - var agentService = _services.GetRequiredService(); - _router = agentService.LoadAgent(_settings.RouterId).Result; - return this; - } - - public List GetHandlers() - { - var planer = _services.GetRequiredService(); - - return _services.GetServices() - .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(); - - 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(); - 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(); - - 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(); - } -} diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs index e861b2bb1..7cf24a23e 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeAgent.cs @@ -41,7 +41,8 @@ private async Task InvokeFunction(Agent agent, RoleDialogModel message, Li { // execute function // Save states - SaveStateByArgs(JsonSerializer.Deserialize(message.FunctionArgs)); + var states = _services.GetRequiredService(); + states.SaveStateByArgs(message.FunctionArgs?.JsonContent()); var conversationService = _services.GetRequiredService(); // Call functions diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs index ed316ec43..bba2c30c7 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.cs @@ -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 void ResetRecursiveCounter() public RoutingService(IServiceProvider services, RoutingSettings settings, - ILogger logger, - IRouterInstance routerInstance) + ILogger logger) { _services = services; _settings = settings; _logger = logger; - _routerInstance = routerInstance; } public async Task ExecuteOnce(Agent agent, RoleDialogModel message) @@ -60,11 +58,12 @@ public async Task ExecuteOnce(Agent agent, RoleDialogModel mess public async Task InstructLoop(RoleDialogModel message) { - _router = _routerInstance.Load() - .Router; + var agentService = _services.GetRequiredService(); + _router = await agentService.LoadAgent(_settings.RouterId); RoleDialogModel response = default; + var states = _services.GetRequiredService(); var conv = _services.GetRequiredService(); var dialogs = conv.GetDialogHistory(); @@ -81,12 +80,13 @@ public async Task InstructLoop(RoleDialogModel message) 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 async Task InstructLoop(RoleDialogModel message) return response; } - protected void SaveStateByArgs(JsonDocument args) + public List GetHandlers() { - if (args == null) + var planer = _services.GetRequiredService(); + + return _services.GetServices() + .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(); + + 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(); + var name = state.GetState("channel"); + var specifiedProfile = agents.FirstOrDefault(x => x.Profiles.Contains(name)); + if (specifiedProfile != null) { - return; + records = records.Where(x => specifiedProfile.Profiles.Contains(name)).ToArray(); } - var stateService = _services.GetRequiredService(); - if (args.RootElement is JsonElement root) + return records; + } + +#if !DEBUG + [MemoryCache(10 * 60)] +#endif + public RoutingItem[] GetRoutingItems() + { + var db = _services.GetRequiredService(); + + 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(); } } diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index 1a756c2ef..f2c78be2f 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -282,7 +282,7 @@ private string GetPrompt(ChatCompletionsOptions chatCompletionsOptions) { 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 @@ private string GetPrompt(ChatCompletionsOptions chatCompletionsOptions) $"{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)