From 879142cb574f727ab2639c94c6ad8aa7e2b10db9 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Thu, 5 Sep 2024 16:44:15 -0500 Subject: [PATCH 1/5] more hold-on --- .../Controllers/TwilioVoiceController.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs index c820e40cc..4f085605b 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs @@ -151,6 +151,8 @@ public async Task ReplyCallerMessage([FromRoute] string conversatio var completion = CompletionProvider.GetAudioCompletion(_services, "openai", "tts-1"); var data = await completion.GenerateAudioFromTextAsync(seg); + // add hold-on + speechPaths.Add($"twilio/hold-on-short-{Random.Shared.Next(1, 7)}.mp3"); var fileName = $"indication_{seqNum}_{segIndex}.mp3"; fileStorage.SaveSpeechFile(conversationId, fileName, data); speechPaths.Add($"twilio/voice/speeches/{conversationId}/{fileName}"); @@ -166,7 +168,7 @@ public async Task ReplyCallerMessage([FromRoute] string conversatio { response = twilio.ReturnInstructions(new List { - $"twilio/hold-on-{Random.Shared.Next(1, 6)}.mp3", + $"twilio/hold-on-long-{Random.Shared.Next(1, 9)}.mp3", $"twilio/typing-{Random.Shared.Next(1, 4)}.mp3" }, $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true); } From de4d740fc223f70eead55c19029fd5aad992bde4 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Fri, 6 Sep 2024 09:59:33 -0500 Subject: [PATCH 2/5] make wating time longer --- .../BotSharp.Plugin.Twilio/Services/TwilioService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs index 285d33214..4af517f51 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs @@ -66,7 +66,7 @@ public VoiceResponse ReturnInstructions(string message) return response; } - public VoiceResponse ReturnInstructions(List speechPaths, string callbackPath, bool actionOnEmptyResult, int timeout = 3) + public VoiceResponse ReturnInstructions(List speechPaths, string callbackPath, bool actionOnEmptyResult, int timeout = 5) { var response = new VoiceResponse(); var gather = new Gather() @@ -78,8 +78,8 @@ public VoiceResponse ReturnInstructions(List speechPaths, string callbac }, Action = new Uri($"{_settings.CallbackHost}/{callbackPath}"), SpeechModel = Gather.SpeechModelEnum.PhoneCall, - SpeechTimeout = "auto", // timeout > 0 ? timeout.ToString() : "3", - Timeout = timeout > 0 ? timeout : 3, + SpeechTimeout = "auto", // timeout > 0 ? timeout.ToString() : "5", + Timeout = timeout > 0 ? timeout : 5, ActionOnEmptyResult = actionOnEmptyResult, Hints = "Yes, No, Correct" }; From fd8b2477f8cacc991e69c41b1b6ec416928622ed Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 9 Sep 2024 14:22:11 -0500 Subject: [PATCH 3/5] GetNextTroubleShootingQuestionFn.Indication --- .../Controllers/TwilioVoiceController.cs | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs index 733dcd430..75dce61e9 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs @@ -13,12 +13,14 @@ public class TwilioVoiceController : TwilioController private readonly TwilioSetting _settings; private readonly IServiceProvider _services; private readonly IHttpContextAccessor _context; + private readonly ILogger _logger; - public TwilioVoiceController(TwilioSetting settings, IServiceProvider services, IHttpContextAccessor context) + public TwilioVoiceController(TwilioSetting settings, IServiceProvider services, IHttpContextAccessor context, ILogger logger) { _settings = settings; _services = services; _context = context; + _logger = logger; } /// @@ -135,6 +137,7 @@ public async Task ReplyCallerMessage([FromRoute] string conversatio var indication = await sessionManager.GetReplyIndicationAsync(conversationId, seqNum); if (indication != null) { + _logger.LogWarning($"Indication: {indication}"); var speechPaths = new List(); int segIndex = 0; foreach (var text in indication.Split('|')) @@ -150,12 +153,22 @@ public async Task ReplyCallerMessage([FromRoute] string conversatio var data = await completion.GenerateAudioFromTextAsync(seg); // add hold-on - speechPaths.Add($"twilio/hold-on-short-{Random.Shared.Next(1, 7)}.mp3"); + var holdOnIndex = Random.Shared.Next(1, 10); + if (holdOnIndex < 7) + { + speechPaths.Add($"twilio/hold-on-short-{holdOnIndex}.mp3"); + } + var fileName = $"indication_{seqNum}_{segIndex}.mp3"; fileStorage.SaveSpeechFile(conversationId, fileName, data); speechPaths.Add($"twilio/voice/speeches/{conversationId}/{fileName}"); + // add typing - speechPaths.Add($"twilio/typing-{Random.Shared.Next(1, 4)}.mp3"); + var typingIndex = Random.Shared.Next(1, 7); + if (typingIndex < 4) + { + speechPaths.Add($"twilio/typing-{typingIndex}.mp3"); + } segIndex++; } } @@ -164,11 +177,25 @@ public async Task ReplyCallerMessage([FromRoute] string conversatio } else { - response = twilio.ReturnInstructions(new List + var instructions = new List { - $"twilio/hold-on-long-{Random.Shared.Next(1, 9)}.mp3", - $"twilio/typing-{Random.Shared.Next(1, 4)}.mp3" - }, $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true); + }; + + // add hold-on + var holdOnIndex = Random.Shared.Next(1, 15); + if (holdOnIndex < 9) + { + instructions.Add($"twilio/hold-on-long-{holdOnIndex}.mp3"); + } + + // add typing + var typingIndex = Random.Shared.Next(1, 7); + if (typingIndex < 4) + { + instructions.Add($"twilio/typing-{typingIndex}.mp3"); + } + + response = twilio.ReturnInstructions(instructions, $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true); } } else From 89a8bf4dff607b807d108c0ba262eb1247cb2586 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 9 Sep 2024 16:43:59 -0500 Subject: [PATCH 4/5] Hints. --- .../Services/TwilioMessageQueueService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs index cd5643f1b..6543e22c1 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs @@ -126,7 +126,7 @@ private async Task ProcessUserMessageAsync(CallerMessage message) break; } } - reply.Hints = string.Join(',', hints); + reply.Hints = string.Join(", ", hints.Select(x => x.ToLower()).Distinct().Reverse()); reply.Content = null; await sessionManager.SetAssistantReplyAsync(message.ConversationId, message.SeqNumber, reply); } From 35e322c3362e93fb3d5cabc09f588be7846716ff Mon Sep 17 00:00:00 2001 From: Haiping Chen <101423@smsassist.com> Date: Tue, 10 Sep 2024 17:12:17 -0500 Subject: [PATCH 5/5] SQL Driver --- .../Infrastructures/HookEmitter.cs | 1 + .../BotSharp.Plugin.Planner.csproj | 24 ++- .../TwoStaging/TwoStageTaskPlanner.cs | 178 +++--------------- .../agent.json | 7 +- .../functions/plan_primary_stage.json | 0 .../functions/plan_secondary_stage.json | 0 .../functions/plan_summary.json | 0 .../instructions/instruction.liquid | 3 +- .../templates/two_stage.1st.next.liquid | 13 ++ .../BotSharp.Plugin.SqlDriver.csproj | 8 + .../Functions/GetTableDefinitionFn.cs | 8 +- .../Functions/SqlSelect.cs | 44 ++--- .../Hooks/SqlExecutorHook.cs | 3 +- .../Models/SqlStatement.cs | 13 +- .../Settings/SqlDriverSetting.cs | 7 +- .../functions/get_table_definition.json | 10 - .../functions/sql_select.json | 4 + .../templates/get_table_definition.fn.liquid | 1 + .../templates/sql_executor.fn.liquid | 2 +- .../agent.json | 17 +- 20 files changed, 126 insertions(+), 217 deletions(-) rename src/Plugins/BotSharp.Plugin.Planner/data/agents/{6745151e-6d46-4a02-8de4-1c4f21c7da95 => 282a7128-69a1-44b0-878c-a9159b88f3b9}/functions/plan_primary_stage.json (100%) rename src/Plugins/BotSharp.Plugin.Planner/data/agents/{6745151e-6d46-4a02-8de4-1c4f21c7da95 => 282a7128-69a1-44b0-878c-a9159b88f3b9}/functions/plan_secondary_stage.json (100%) rename src/Plugins/BotSharp.Plugin.Planner/data/agents/{6745151e-6d46-4a02-8de4-1c4f21c7da95 => 282a7128-69a1-44b0-878c-a9159b88f3b9}/functions/plan_summary.json (100%) create mode 100644 src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.1st.next.liquid create mode 100644 src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/get_table_definition.fn.liquid diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/HookEmitter.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/HookEmitter.cs index 988863ac2..2863ff02c 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/HookEmitter.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/HookEmitter.cs @@ -14,6 +14,7 @@ public static async Task Emit(IServiceProvider services, A { try { + logger.LogInformation($"Emit hook action on {action.Method.Name}({hook.GetType().Name})"); action(hook); } catch (Exception ex) diff --git a/src/Plugins/BotSharp.Plugin.Planner/BotSharp.Plugin.Planner.csproj b/src/Plugins/BotSharp.Plugin.Planner/BotSharp.Plugin.Planner.csproj index 63a66aac8..5d06846f7 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/BotSharp.Plugin.Planner.csproj +++ b/src/Plugins/BotSharp.Plugin.Planner/BotSharp.Plugin.Planner.csproj @@ -12,12 +12,13 @@ + + + + - - - @@ -28,25 +29,28 @@ PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + + PreserveNewest + + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs index 555432306..5694a39f2 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs +++ b/src/Plugins/BotSharp.Plugin.Planner/TwoStaging/TwoStageTaskPlanner.cs @@ -1,7 +1,6 @@ -using BotSharp.Abstraction.MLTasks; +using BotSharp.Abstraction.Infrastructures.Enums; using BotSharp.Abstraction.Routing.Planning; using BotSharp.Core.Routing.Planning; -using Microsoft.EntityFrameworkCore; namespace BotSharp.Plugin.Planner.TwoStaging; @@ -10,12 +9,6 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner private readonly IServiceProvider _services; private readonly ILogger _logger; public int MaxLoopCount => 10; - private bool _isTaskCompleted; - - private Queue _plan1st = new Queue(); - private Queue _plan2nd = new Queue(); - - private List _executionContext = new List(); public TwoStageTaskPlanner(IServiceProvider services, ILogger logger) { @@ -25,78 +18,36 @@ public TwoStageTaskPlanner(IServiceProvider services, ILogger GetNextInstruction(Agent router, string messageId, List dialogs) { - // push agent to routing context - var routing = _services.GetRequiredService(); - routing.Context.Push(BuiltInAgentId.Planner, "Make plan in TwoStage planner"); - return new FunctionCallFromLlm - { - AgentName = router.Name, - Response = dialogs.Last().Content, - Function = "route_to_agent" - }; - - - /*FirstStagePlan[] items = await GetFirstStagePlanAsync(router, messageId, dialogs); + var nextStepPrompt = await GetNextStepPrompt(router); + var inst = new FunctionCallFromLlm(); - foreach (var item in items) - { - _plan1st.Enqueue(item); - }; + // chat completion + var completion = CompletionProvider.GetChatCompletion(_services, + provider: router?.LlmConfig?.Provider, + model: router?.LlmConfig?.Model); - // Get Second Stage Plan - if (_plan2nd.IsNullOrEmpty()) + // text completion + dialogs = new List { - var plan1 = _plan1st.Dequeue(); - - if (plan1.ContainMultipleSteps) - { - SecondStagePlan[] items = await GetSecondStagePlanAsync(router, messageId, plan1, dialogs); - - foreach (var item in items) - { - _plan2nd.Enqueue(item); - } - } - else + new RoleDialogModel(AgentRole.User, nextStepPrompt) { - _plan2nd.Enqueue(new SecondStagePlan - { - Description = plan1.Task, - Tables = plan1.Tables, - Parameters = plan1.Parameters, - Results = plan1.Results, - }); + FunctionName = nameof(TwoStageTaskPlanner), + MessageId = messageId } - } - - var plan2 = _plan2nd.Dequeue(); - - var secondStagePrompt = GetSecondStageTaskPrompt(router, plan2); - var inst = new FunctionCallFromLlm - { - AgentName = "SQL Driver", - Response = secondStagePrompt, - Function = "route_to_agent" }; + var response = await completion.GetChatCompletions(router, dialogs); - inst.HandleDialogsByPlanner = true; - _isTaskCompleted = _plan1st.IsNullOrEmpty() && _plan2nd.IsNullOrEmpty(); + inst = response.Content.JsonContent(); - return inst;*/ + // Fix LLM malformed response + PlannerHelper.FixMalformedResponse(_services, inst); + + return inst; } public List BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) { var question = inst.Response; - if (_executionContext.Count > 0) - { - var content = GetContext(); - question = $"CONTEXT:\r\n{content}\r\n" + inst.Response; - } - else - { - question = $"CONTEXT:\r\n{question}"; - } var taskAgentDialogs = new List { @@ -113,14 +64,14 @@ public bool AfterHandleContext(List dialogs, List AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List dialogs) { + // Set user content as Planner's question + message.FunctionName = inst.Function; + message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments); return true; } @@ -128,7 +79,7 @@ public async Task AgentExecuted(Agent router, FunctionCallFromLlm inst, Ro { var context = _services.GetRequiredService(); - if (message.StopCompletion || _isTaskCompleted) + if (message.StopCompletion) { context.Empty(reason: $"Agent queue is cleared by {nameof(TwoStageTaskPlanner)}"); return false; @@ -145,16 +96,6 @@ public async Task AgentExecuted(Agent router, FunctionCallFromLlm inst, Ro return true; } - public string GetContext() - { - var content = ""; - foreach (var c in _executionContext) - { - content += $"* {c}\r\n"; - } - return content; - } - private async Task GetFirstStagePlanPrompt(Agent router) { var template = router.Templates.First(x => x.Name == "two_stage.1st.plan").Content; @@ -180,61 +121,18 @@ private async Task GetFirstStagePlanPrompt(Agent router) }); } - private string GetFirstStageNextPrompt(Agent router) + private async Task GetNextStepPrompt(Agent router) { - var template = router.Templates.First(x => x.Name == "first_stage.next").Content; - var responseFormat = JsonSerializer.Serialize(new FirstStagePlan - { - }); + var agentService = _services.GetRequiredService(); + var planner = await agentService.LoadAgent(BuiltInAgentId.Planner); + var template = planner.Templates.First(x => x.Name == "two_stage.1st.next").Content; + var states = _services.GetRequiredService(); var render = _services.GetRequiredService(); return render.Render(template, new Dictionary { - { "response_format", responseFormat }, - }); - } - - private async Task GetSecondStagePlanAsync(Agent router, string messageId, FirstStagePlan plan1st, List dialogs) - { - var secondStagePrompt = GetSecondStagePlanPrompt(router, plan1st); - var firstStageSystemPrompt = await GetFirstStagePlanPrompt(router); - - var plan = new SecondStagePlan[0]; - - var llmProviderService = _services.GetRequiredService(); - var model = llmProviderService.GetProviderModel("azure-openai", "gpt-4"); - - // chat completion - var completion = CompletionProvider.GetChatCompletion(_services, - provider: "azure-openai", - model: model.Name); - - string text = string.Empty; - - var conversations = dialogs.Where(x => x.Role != AgentRole.Function).ToList(); - conversations.Add(new RoleDialogModel(AgentRole.User, secondStagePrompt) - { - CurrentAgentId = router.Id, - MessageId = messageId, + { StateConst.EXPECTED_ACTION_AGENT, states.GetState(StateConst.EXPECTED_ACTION_AGENT) }, + { StateConst.EXPECTED_GOAL_AGENT, states.GetState(StateConst.EXPECTED_GOAL_AGENT) } }); - - try - { - var response = await completion.GetChatCompletions(new Agent - { - Id = router.Id, - Name = nameof(TwoStageTaskPlanner), - Instruction = firstStageSystemPrompt - }, conversations); - - text = response.Content; - plan = response.Content.JsonArrayContent(); - } - catch (Exception ex) - { - _logger.LogError($"{ex.Message}: {text}"); - } - - return plan; } private string GetSecondStageTaskPrompt(Agent router, SecondStagePlan plan) @@ -249,22 +147,4 @@ private string GetSecondStageTaskPrompt(Agent router, SecondStagePlan plan) { "output_results", JsonSerializer.Serialize(plan.Results) }, }); } - - private string GetSecondStagePlanPrompt(Agent router, FirstStagePlan plan) - { - var template = router.Templates.First(x => x.Name == "planner_prompt.two_stage.2nd.plan").Content; - var responseFormat = JsonSerializer.Serialize(new SecondStagePlan - { - Tool = "tool name if task solution provided", - Parameters = new JsonDocument[] { JsonDocument.Parse("{}") }, - Results = new string[] { "" } - }); - var context = GetContext(); - var render = _services.GetRequiredService(); - return render.Render(template, new Dictionary - { - { "task_description", plan.Task }, - { "response_format", responseFormat } - }); - } } diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json index aeabfb9ae..5cb384f7d 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/agent.json @@ -1,7 +1,7 @@ { "id": "282a7128-69a1-44b0-878c-a9159b88f3b9", "name": "Planner", - "description": "Plan feasible implementation steps for complex problems", + "description": "Plan feasible implementation steps for user task request", "type": "task", "createdDateTime": "2023-08-27T10:39:00Z", "updatedDateTime": "2023-08-27T14:39:00Z", @@ -11,7 +11,8 @@ "profiles": [ "planning" ], "utilities": [ "two-stage-planner" ], "llmConfig": { - "provider": "openai", - "model": "gpt-4o-mini" + "provider": "anthropic", + "model": "claude-3-5-sonnet-20240620", + "max_recursion_depth": 10 } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/plan_primary_stage.json b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/functions/plan_primary_stage.json similarity index 100% rename from src/Plugins/BotSharp.Plugin.Planner/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/plan_primary_stage.json rename to src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/functions/plan_primary_stage.json diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/plan_secondary_stage.json b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/functions/plan_secondary_stage.json similarity index 100% rename from src/Plugins/BotSharp.Plugin.Planner/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/plan_secondary_stage.json rename to src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/functions/plan_secondary_stage.json diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/plan_summary.json b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/functions/plan_summary.json similarity index 100% rename from src/Plugins/BotSharp.Plugin.Planner/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/plan_summary.json rename to src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/functions/plan_summary.json diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid index e5013463d..b1c6b511f 100644 --- a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/instructions/instruction.liquid @@ -1,6 +1,7 @@ Use the TwoStagePlanner approach to plan the overall implementation steps, call plan_primary_stage. If need_additional_information is true, call plan_secondary_stage for the specific primary stage. -You must Call plan_summary as the last step to summarize the final planning steps. +You must call plan_summary as the last planning step to summarize the final query. +You must generate the final sql statement from function of plan_summary. {% if global_knowledges != empty -%} ===== diff --git a/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.1st.next.liquid b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.1st.next.liquid new file mode 100644 index 000000000..48ae2b39d --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Planner/data/agents/282a7128-69a1-44b0-878c-a9159b88f3b9/templates/two_stage.1st.next.liquid @@ -0,0 +1,13 @@ +What is the next step based on the CONVERSATION? +Route to the last handling agent in priority. +{% if expected_next_action_agent != empty -%} +Expected next action agent is {{ expected_next_action_agent }}. +{%- else -%} +Next action agent is inferred based on user lastest response. +{%- endif %} +{% if expected_user_goal_agent != empty -%} +Expected user goal agent is {{ expected_user_goal_agent }}. +{%- else -%} +User goal agent is inferred based on user initial request. +{%- endif %} +Always route to planner first. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj index 266abbecb..4e03bbfaa 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj @@ -11,7 +11,9 @@ + + @@ -23,6 +25,12 @@ + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs index a9fb50052..f98b0da48 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/GetTableDefinitionFn.cs @@ -1,6 +1,6 @@ +using BotSharp.Plugin.SqlDriver.Models; using Microsoft.Extensions.Logging; using MySqlConnector; -using static Dapper.SqlMapper; namespace BotSharp.Plugin.SqlDriver.Functions; @@ -20,16 +20,16 @@ public GetTableDefinitionFn( public async Task Execute(RoleDialogModel message) { + var args = JsonSerializer.Deserialize(message.FunctionArgs); + var tables = new string[] { args.Table }; var agentService = _services.GetRequiredService(); var sqlDriver = _services.GetRequiredService(); var settings = _services.GetRequiredService(); // Get table DDL from database - var tables = message.Data as IEnumerable; - if (tables.IsNullOrEmpty()) return false; var tableDdls = new List(); - using var connection = new MySqlConnection(settings.MySqlConnectionString); + using var connection = new MySqlConnection(settings.MySqlExecutionConnectionString); connection.Open(); foreach (var table in tables) diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs index b51a146ce..87731dd83 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs @@ -17,39 +17,33 @@ public SqlSelect(IServiceProvider services) public async Task Execute(RoleDialogModel message) { var args = JsonSerializer.Deserialize(message.FunctionArgs); - var sqlDriver = _services.GetRequiredService(); + + if (args.GeneratedWithoutTableDefinition) + { + message.Content = $"Get the table definition first."; + return false; + } // check if need to instantely - var execNow = !args.Parameters.Any(x => x.Value.StartsWith("@")); - if (execNow) + var settings = _services.GetRequiredService(); + using var connection = new MySqlConnection(settings.MySqlExecutionConnectionString); + var dictionary = new Dictionary(); + foreach(var p in args.Parameters) { - var settings = _services.GetRequiredService(); - using var connection = new MySqlConnection(settings.MySqlConnectionString); - var dictionary = new Dictionary(); - foreach(var p in args.Parameters) - { - dictionary["@" + p.Name] = p.Value; - } - var result = connection.QueryFirstOrDefault(args.Statement, dictionary); + dictionary["@" + p.Name] = p.Value; + } + var result = connection.Query(args.Statement, dictionary); - if (result == null) - { - message.Content = "Record not found"; - } - else - { - message.Content = JsonSerializer.Serialize(result); - args.Return.Value = message.Content; - } - - sqlDriver.Enqueue(args); + if (result == null) + { + message.Content = "Record not found"; } else { - sqlDriver.Enqueue(args); - message.Content = $"The {args.Return.Name} is saved to @{args.Return.Alias}"; + message.Content = JsonSerializer.Serialize(result); + args.Return.Value = message.Content; } - + return true; } } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlExecutorHook.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlExecutorHook.cs index 34da9ab58..ecb4e2920 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlExecutorHook.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Hooks/SqlExecutorHook.cs @@ -10,7 +10,8 @@ public class SqlExecutorHook : AgentHookBase, IAgentHook private const string SQL_EXECUTOR_TEMPLATE = "sql_executor.fn"; private IEnumerable _targetSqlExecutorFunctions = new List { - "sql_select" + "sql_select", + "get_table_definition", }; public override string SelfId => string.Empty; diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Models/SqlStatement.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Models/SqlStatement.cs index 090208263..926129228 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Models/SqlStatement.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Models/SqlStatement.cs @@ -5,19 +5,22 @@ namespace BotSharp.Plugin.SqlDriver.Models; public class SqlStatement { [JsonPropertyName("sql_statement")] - public string Statement { get; set; } + public string Statement { get; set; } = null!; [JsonPropertyName("reason")] - public string Reason { get; set; } + public string Reason { get; set; } = null!; [JsonPropertyName("table")] - public string Table { get; set; } + public string Table { get; set; } = null!; [JsonPropertyName("parameters")] - public SqlParameter[] Parameters { get; set; } = new SqlParameter[0]; + public SqlParameter[] Parameters { get; set; } = []; [JsonPropertyName("return_field")] - public SqlReturn Return { get; set; } + public SqlReturn Return { get; set; } = new SqlReturn(); + + [JsonPropertyName("generated_without_table_definition")] + public bool GeneratedWithoutTableDefinition { get; set; } public override string ToString() { diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs index ed7a7fe1a..8f4d4d953 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs @@ -2,7 +2,8 @@ namespace BotSharp.Plugin.SqlHero.Settings; public class SqlDriverSetting { - public string MySqlConnectionString { get; set; } - public string SqlServerConnectionString { get; set; } - public string SqlLiteConnectionString { get; set; } + public string MySqlConnectionString { get; set; } = null!; + public string MySqlExecutionConnectionString { get; set; } = null!; + public string SqlServerConnectionString { get; set; } = null!; + public string SqlLiteConnectionString { get; set; } = null!; } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/get_table_definition.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/get_table_definition.json index 0848099f2..52a747fe7 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/get_table_definition.json +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/get_table_definition.json @@ -7,16 +7,6 @@ "table": { "type": "string", "description": "table need to check" - }, - "return_field": { - "type": "object", - "description": "the name and alias for the return field", - "properties": { - "ddl": { - "type": "string", - "description": "DDL for the table" - } - } } }, "required": [ "table" ] diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/sql_select.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/sql_select.json index 956af5efe..1bcf76a32 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/sql_select.json +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/sql_select.json @@ -16,6 +16,10 @@ "type": "string", "description": "related table" }, + "generated_without_table_definition": { + "type": "boolean", + "description": "sql is generated before retrieving table definition" + }, "parameters": { "type": "array", "description": "data criteria for the query", diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/get_table_definition.fn.liquid b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/get_table_definition.fn.liquid new file mode 100644 index 000000000..e659e0125 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/get_table_definition.fn.liquid @@ -0,0 +1 @@ +Call get_table_definition to get the table definition of the table you want to query. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/sql_executor.fn.liquid b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/sql_executor.fn.liquid index 30fa8124a..edcb4befd 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/sql_executor.fn.liquid +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/sql_executor.fn.liquid @@ -1,4 +1,4 @@ -You are connecting to {{ db_type }} database. Please generate SQL statements following {{ db_type }} rules. +You are connecting to {{ db_type }} database. You can run provided SQL statements by following {{ db_type }} rules. Please call function sql_select if user wants to get or retrieve data from data tables. If there are any parameters, please add them in the WHERE clause, each of which starts with "@". diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json index e75c47094..fbd6b99b6 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json @@ -1,7 +1,7 @@ { "id": "beda4c12-e1ec-4b4b-b328-3df4a6687c4f", "name": "SQL Driver", - "description": "Convert the requirements into corresponding SQL statements according to the table structure.", + "description": "Runs the specified SQL statement and returns the result.", "type": "task", "createdDateTime": "2023-11-15T13:49:00Z", "updatedDateTime": "2023-11-15T13:49:00Z", @@ -9,8 +9,15 @@ "isPublic": true, "profiles": [ "database" ], "llmConfig": { - "model": "gpt-4-0125", - "model3": "gpt-35-turbo-1106", - "max_recursion_depth": 10 - } + "provider": "openai", + "model": "gpt-4o-mini" + }, + "routingRules": [ + { + "field": "sql_statement", + "required": true, + "field_type": "string", + "description": "SQL statement" + } + ] } \ No newline at end of file