SQL Driver

This commit is contained in:
Haiping Chen 2024-09-10 17:12:17 -05:00
parent 89a8bf4dff
commit 35e322c336
20 changed files with 130 additions and 221 deletions

View file

@ -14,6 +14,7 @@ public static class HookEmitter
{ {
try try
{ {
logger.LogInformation($"Emit hook action on {action.Method.Name}({hook.GetType().Name})");
action(hook); action(hook);
} }
catch (Exception ex) catch (Exception ex)

View file

@ -12,12 +12,13 @@
<ItemGroup> <ItemGroup>
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\agent.json" /> <None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\agent.json" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_primary_stage.json" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_secondary_stage.json" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_summary.json" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\instructions\instruction.liquid" /> <None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\instructions\instruction.liquid" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.1st.next.liquid" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.2nd.plan.liquid" /> <None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.2nd.plan.liquid" />
<None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.summarize.liquid" /> <None Remove="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.summarize.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_primary_stage.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_secondary_stage.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_summary.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\planner_prompt.two_stage.1st.plan.liquid" /> <None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\planner_prompt.two_stage.1st.plan.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\plan_primary_stage.fn.liquid" /> <None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\plan_primary_stage.fn.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\plan_secondary_stage.fn.liquid" /> <None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\plan_secondary_stage.fn.liquid" />
@ -28,27 +29,30 @@
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\agent.json"> <Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\agent.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_primary_stage.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_secondary_stage.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\functions\plan_summary.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\instructions\instruction.liquid"> <Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\instructions\instruction.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.1st.next.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.2nd.plan.liquid"> <Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.2nd.plan.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.summarize.liquid"> <Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.summarize.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_secondary_stage.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_primary_stage.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.1st.plan.liquid"> <Content Include="data\agents\282a7128-69a1-44b0-878c-a9159b88f3b9\templates\two_stage.1st.plan.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\plan_summary.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\plan_secondary_stage.fn.liquid"> <Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\plan_secondary_stage.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>

View file

@ -1,7 +1,6 @@
using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Planning; using BotSharp.Abstraction.Routing.Planning;
using BotSharp.Core.Routing.Planning; using BotSharp.Core.Routing.Planning;
using Microsoft.EntityFrameworkCore;
namespace BotSharp.Plugin.Planner.TwoStaging; namespace BotSharp.Plugin.Planner.TwoStaging;
@ -10,12 +9,6 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner
private readonly IServiceProvider _services; private readonly IServiceProvider _services;
private readonly ILogger _logger; private readonly ILogger _logger;
public int MaxLoopCount => 10; public int MaxLoopCount => 10;
private bool _isTaskCompleted;
private Queue<FirstStagePlan> _plan1st = new Queue<FirstStagePlan>();
private Queue<SecondStagePlan> _plan2nd = new Queue<SecondStagePlan>();
private List<string> _executionContext = new List<string>();
public TwoStageTaskPlanner(IServiceProvider services, ILogger<TwoStageTaskPlanner> logger) public TwoStageTaskPlanner(IServiceProvider services, ILogger<TwoStageTaskPlanner> logger)
{ {
@ -25,78 +18,36 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner
public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs) public async Task<FunctionCallFromLlm> GetNextInstruction(Agent router, string messageId, List<RoleDialogModel> dialogs)
{ {
// push agent to routing context var nextStepPrompt = await GetNextStepPrompt(router);
var routing = _services.GetRequiredService<IRoutingService>(); var inst = new FunctionCallFromLlm();
routing.Context.Push(BuiltInAgentId.Planner, "Make plan in TwoStage planner");
return new FunctionCallFromLlm // chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: router?.LlmConfig?.Provider,
model: router?.LlmConfig?.Model);
// text completion
dialogs = new List<RoleDialogModel>
{ {
AgentName = router.Name, new RoleDialogModel(AgentRole.User, nextStepPrompt)
Response = dialogs.Last().Content,
Function = "route_to_agent"
};
/*FirstStagePlan[] items = await GetFirstStagePlanAsync(router, messageId, dialogs);
foreach (var item in items)
{
_plan1st.Enqueue(item);
};
// Get Second Stage Plan
if (_plan2nd.IsNullOrEmpty())
{
var plan1 = _plan1st.Dequeue();
if (plan1.ContainMultipleSteps)
{ {
SecondStagePlan[] items = await GetSecondStagePlanAsync(router, messageId, plan1, dialogs); FunctionName = nameof(TwoStageTaskPlanner),
MessageId = messageId
foreach (var item in items)
{
_plan2nd.Enqueue(item);
}
} }
else
{
_plan2nd.Enqueue(new SecondStagePlan
{
Description = plan1.Task,
Tables = plan1.Tables,
Parameters = plan1.Parameters,
Results = plan1.Results,
});
}
}
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; inst = response.Content.JsonContent<FunctionCallFromLlm>();
_isTaskCompleted = _plan1st.IsNullOrEmpty() && _plan2nd.IsNullOrEmpty();
return inst;*/ // Fix LLM malformed response
PlannerHelper.FixMalformedResponse(_services, inst);
return inst;
} }
public List<RoleDialogModel> BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs) public List<RoleDialogModel> BeforeHandleContext(FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{ {
var question = inst.Response; 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<RoleDialogModel> var taskAgentDialogs = new List<RoleDialogModel>
{ {
@ -113,14 +64,14 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner
{ {
dialogs.AddRange(taskAgentDialogs.Skip(1)); dialogs.AddRange(taskAgentDialogs.Skip(1));
// Keep execution context
_executionContext.Add(taskAgentDialogs.Last().Content);
return true; return true;
} }
public async Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs) public async Task<bool> AgentExecuting(Agent router, FunctionCallFromLlm inst, RoleDialogModel message, List<RoleDialogModel> dialogs)
{ {
// Set user content as Planner's question
message.FunctionName = inst.Function;
message.FunctionArgs = inst.Arguments == null ? "{}" : JsonSerializer.Serialize(inst.Arguments);
return true; return true;
} }
@ -128,7 +79,7 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner
{ {
var context = _services.GetRequiredService<IRoutingContext>(); var context = _services.GetRequiredService<IRoutingContext>();
if (message.StopCompletion || _isTaskCompleted) if (message.StopCompletion)
{ {
context.Empty(reason: $"Agent queue is cleared by {nameof(TwoStageTaskPlanner)}"); context.Empty(reason: $"Agent queue is cleared by {nameof(TwoStageTaskPlanner)}");
return false; return false;
@ -145,16 +96,6 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner
return true; return true;
} }
public string GetContext()
{
var content = "";
foreach (var c in _executionContext)
{
content += $"* {c}\r\n";
}
return content;
}
private async Task<string> GetFirstStagePlanPrompt(Agent router) private async Task<string> GetFirstStagePlanPrompt(Agent router)
{ {
var template = router.Templates.First(x => x.Name == "two_stage.1st.plan").Content; var template = router.Templates.First(x => x.Name == "two_stage.1st.plan").Content;
@ -180,63 +121,20 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner
}); });
} }
private string GetFirstStageNextPrompt(Agent router) private async Task<string> GetNextStepPrompt(Agent router)
{ {
var template = router.Templates.First(x => x.Name == "first_stage.next").Content; var agentService = _services.GetRequiredService<IAgentService>();
var responseFormat = JsonSerializer.Serialize(new FirstStagePlan var planner = await agentService.LoadAgent(BuiltInAgentId.Planner);
{ var template = planner.Templates.First(x => x.Name == "two_stage.1st.next").Content;
}); var states = _services.GetRequiredService<IConversationStateService>();
var render = _services.GetRequiredService<ITemplateRender>(); var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object> return render.Render(template, new Dictionary<string, object>
{ {
{ "response_format", responseFormat }, { StateConst.EXPECTED_ACTION_AGENT, states.GetState(StateConst.EXPECTED_ACTION_AGENT) },
{ StateConst.EXPECTED_GOAL_AGENT, states.GetState(StateConst.EXPECTED_GOAL_AGENT) }
}); });
} }
private async Task<SecondStagePlan[]> GetSecondStagePlanAsync(Agent router, string messageId, FirstStagePlan plan1st, List<RoleDialogModel> dialogs)
{
var secondStagePrompt = GetSecondStagePlanPrompt(router, plan1st);
var firstStageSystemPrompt = await GetFirstStagePlanPrompt(router);
var plan = new SecondStagePlan[0];
var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
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,
});
try
{
var response = await completion.GetChatCompletions(new Agent
{
Id = router.Id,
Name = nameof(TwoStageTaskPlanner),
Instruction = firstStageSystemPrompt
}, conversations);
text = response.Content;
plan = response.Content.JsonArrayContent<SecondStagePlan>();
}
catch (Exception ex)
{
_logger.LogError($"{ex.Message}: {text}");
}
return plan;
}
private string GetSecondStageTaskPrompt(Agent router, SecondStagePlan plan) private string GetSecondStageTaskPrompt(Agent router, SecondStagePlan plan)
{ {
var template = router.Templates.First(x => x.Name == "planner_prompt.two_stage.2nd.task").Content; var template = router.Templates.First(x => x.Name == "planner_prompt.two_stage.2nd.task").Content;
@ -249,22 +147,4 @@ public partial class TwoStageTaskPlanner : IRoutingPlaner
{ "output_results", JsonSerializer.Serialize(plan.Results) }, { "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<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
{
{ "task_description", plan.Task },
{ "response_format", responseFormat }
});
}
} }

View file

@ -1,7 +1,7 @@
{ {
"id": "282a7128-69a1-44b0-878c-a9159b88f3b9", "id": "282a7128-69a1-44b0-878c-a9159b88f3b9",
"name": "Planner", "name": "Planner",
"description": "Plan feasible implementation steps for complex problems", "description": "Plan feasible implementation steps for user task request",
"type": "task", "type": "task",
"createdDateTime": "2023-08-27T10:39:00Z", "createdDateTime": "2023-08-27T10:39:00Z",
"updatedDateTime": "2023-08-27T14:39:00Z", "updatedDateTime": "2023-08-27T14:39:00Z",
@ -11,7 +11,8 @@
"profiles": [ "planning" ], "profiles": [ "planning" ],
"utilities": [ "two-stage-planner" ], "utilities": [ "two-stage-planner" ],
"llmConfig": { "llmConfig": {
"provider": "openai", "provider": "anthropic",
"model": "gpt-4o-mini" "model": "claude-3-5-sonnet-20240620",
"max_recursion_depth": 10
} }
} }

View file

@ -1,6 +1,7 @@
Use the TwoStagePlanner approach to plan the overall implementation steps, call plan_primary_stage. 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. 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 -%} {% if global_knowledges != empty -%}
===== =====

View file

@ -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.

View file

@ -11,7 +11,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\get_table_definition.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_select.json" /> <None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_select.json" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\get_table_definition.fn.liquid" />
<None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_executor.fn.liquid" /> <None Remove="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_executor.fn.liquid" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json" /> <None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\get_table_columns.json" /> <None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\get_table_columns.json" />
@ -23,6 +25,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\get_table_definition.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\get_table_definition.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json"> <Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\agent.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>

View file

@ -1,6 +1,6 @@
using BotSharp.Plugin.SqlDriver.Models;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MySqlConnector; using MySqlConnector;
using static Dapper.SqlMapper;
namespace BotSharp.Plugin.SqlDriver.Functions; namespace BotSharp.Plugin.SqlDriver.Functions;
@ -20,16 +20,16 @@ public class GetTableDefinitionFn : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message) public async Task<bool> Execute(RoleDialogModel message)
{ {
var args = JsonSerializer.Deserialize<SqlStatement>(message.FunctionArgs);
var tables = new string[] { args.Table };
var agentService = _services.GetRequiredService<IAgentService>(); var agentService = _services.GetRequiredService<IAgentService>();
var sqlDriver = _services.GetRequiredService<SqlDriverService>(); var sqlDriver = _services.GetRequiredService<SqlDriverService>();
var settings = _services.GetRequiredService<SqlDriverSetting>(); var settings = _services.GetRequiredService<SqlDriverSetting>();
// Get table DDL from database // Get table DDL from database
var tables = message.Data as IEnumerable<string>;
if (tables.IsNullOrEmpty()) return false;
var tableDdls = new List<string>(); var tableDdls = new List<string>();
using var connection = new MySqlConnection(settings.MySqlConnectionString); using var connection = new MySqlConnection(settings.MySqlExecutionConnectionString);
connection.Open(); connection.Open();
foreach (var table in tables) foreach (var table in tables)

View file

@ -17,39 +17,33 @@ public class SqlSelect : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message) public async Task<bool> Execute(RoleDialogModel message)
{ {
var args = JsonSerializer.Deserialize<SqlStatement>(message.FunctionArgs); var args = JsonSerializer.Deserialize<SqlStatement>(message.FunctionArgs);
var sqlDriver = _services.GetRequiredService<SqlDriverService>();
if (args.GeneratedWithoutTableDefinition)
{
message.Content = $"Get the table definition first.";
return false;
}
// check if need to instantely // check if need to instantely
var execNow = !args.Parameters.Any(x => x.Value.StartsWith("@")); var settings = _services.GetRequiredService<SqlDriverSetting>();
if (execNow) using var connection = new MySqlConnection(settings.MySqlExecutionConnectionString);
var dictionary = new Dictionary<string, object>();
foreach(var p in args.Parameters)
{ {
var settings = _services.GetRequiredService<SqlDriverSetting>(); dictionary["@" + p.Name] = p.Value;
using var connection = new MySqlConnection(settings.MySqlConnectionString); }
var dictionary = new Dictionary<string, object>(); var result = connection.Query(args.Statement, dictionary);
foreach(var p in args.Parameters)
{
dictionary["@" + p.Name] = p.Value;
}
var result = connection.QueryFirstOrDefault(args.Statement, dictionary);
if (result == null) if (result == null)
{ {
message.Content = "Record not found"; message.Content = "Record not found";
}
else
{
message.Content = JsonSerializer.Serialize(result);
args.Return.Value = message.Content;
}
sqlDriver.Enqueue(args);
} }
else else
{ {
sqlDriver.Enqueue(args); message.Content = JsonSerializer.Serialize(result);
message.Content = $"The {args.Return.Name} is saved to @{args.Return.Alias}"; args.Return.Value = message.Content;
} }
return true; return true;
} }
} }

View file

@ -10,7 +10,8 @@ public class SqlExecutorHook : AgentHookBase, IAgentHook
private const string SQL_EXECUTOR_TEMPLATE = "sql_executor.fn"; private const string SQL_EXECUTOR_TEMPLATE = "sql_executor.fn";
private IEnumerable<string> _targetSqlExecutorFunctions = new List<string> private IEnumerable<string> _targetSqlExecutorFunctions = new List<string>
{ {
"sql_select" "sql_select",
"get_table_definition",
}; };
public override string SelfId => string.Empty; public override string SelfId => string.Empty;

View file

@ -5,19 +5,22 @@ namespace BotSharp.Plugin.SqlDriver.Models;
public class SqlStatement public class SqlStatement
{ {
[JsonPropertyName("sql_statement")] [JsonPropertyName("sql_statement")]
public string Statement { get; set; } public string Statement { get; set; } = null!;
[JsonPropertyName("reason")] [JsonPropertyName("reason")]
public string Reason { get; set; } public string Reason { get; set; } = null!;
[JsonPropertyName("table")] [JsonPropertyName("table")]
public string Table { get; set; } public string Table { get; set; } = null!;
[JsonPropertyName("parameters")] [JsonPropertyName("parameters")]
public SqlParameter[] Parameters { get; set; } = new SqlParameter[0]; public SqlParameter[] Parameters { get; set; } = [];
[JsonPropertyName("return_field")] [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() public override string ToString()
{ {

View file

@ -2,7 +2,8 @@ namespace BotSharp.Plugin.SqlHero.Settings;
public class SqlDriverSetting public class SqlDriverSetting
{ {
public string MySqlConnectionString { get; set; } public string MySqlConnectionString { get; set; } = null!;
public string SqlServerConnectionString { get; set; } public string MySqlExecutionConnectionString { get; set; } = null!;
public string SqlLiteConnectionString { get; set; } public string SqlServerConnectionString { get; set; } = null!;
public string SqlLiteConnectionString { get; set; } = null!;
} }

View file

@ -7,16 +7,6 @@
"table": { "table": {
"type": "string", "type": "string",
"description": "table need to check" "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" ] "required": [ "table" ]

View file

@ -16,6 +16,10 @@
"type": "string", "type": "string",
"description": "related table" "description": "related table"
}, },
"generated_without_table_definition": {
"type": "boolean",
"description": "sql is generated before retrieving table definition"
},
"parameters": { "parameters": {
"type": "array", "type": "array",
"description": "data criteria for the query", "description": "data criteria for the query",

View file

@ -0,0 +1 @@
Call get_table_definition to get the table definition of the table you want to query.

View file

@ -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. 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 "@". If there are any parameters, please add them in the WHERE clause, each of which starts with "@".

View file

@ -1,7 +1,7 @@
{ {
"id": "beda4c12-e1ec-4b4b-b328-3df4a6687c4f", "id": "beda4c12-e1ec-4b4b-b328-3df4a6687c4f",
"name": "SQL Driver", "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", "type": "task",
"createdDateTime": "2023-11-15T13:49:00Z", "createdDateTime": "2023-11-15T13:49:00Z",
"updatedDateTime": "2023-11-15T13:49:00Z", "updatedDateTime": "2023-11-15T13:49:00Z",
@ -9,8 +9,15 @@
"isPublic": true, "isPublic": true,
"profiles": [ "database" ], "profiles": [ "database" ],
"llmConfig": { "llmConfig": {
"model": "gpt-4-0125", "provider": "openai",
"model3": "gpt-35-turbo-1106", "model": "gpt-4o-mini"
"max_recursion_depth": 10 },
} "routingRules": [
{
"field": "sql_statement",
"required": true,
"field_type": "string",
"description": "SQL statement"
}
]
} }