Skip to content

Add sql dictionary lookup utility #656

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public async Task<bool> Execute(RoleDialogModel message)
{
var list = await knowledgeService.SearchVectorKnowledge(question, collectionName, new VectorSearchOptions
{
Confidence = 0.2f
Confidence = 0.4f
});
knowledges.Add(string.Join("\r\n\r\n=====\r\n", list.Select(x => x.ToQuestionAnswer())));

Expand All @@ -56,7 +56,10 @@ public async Task<bool> Execute(RoleDialogModel message)
LlmConfig = currentAgent.LlmConfig
};
var response = await GetAiResponse(plannerAgent);
message.Content = response.Content;
message.Content = response.Content;

var states = _services.GetRequiredService<IConversationStateService>();
states.SetState("planning_result", response.Content);

return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public async Task<bool> Execute(RoleDialogModel message)

var knowledges = await knowledgeService.SearchVectorKnowledge(item.Task, collectionName, new VectorSearchOptions
{
Confidence = 0.5f
Confidence = 0.6f
});
message.Content += string.Join("\r\n\r\n=====\r\n", knowledges.Select(x => x.ToQuestionAnswer()));
}
Expand All @@ -63,6 +63,9 @@ public async Task<bool> Execute(RoleDialogModel message)
var response = await GetAiResponse(plannerAgent);
message.Content = response.Content;
_logger.LogInformation(response.Content);

var states = _services.GetRequiredService<IConversationStateService>();
states.SetState("planning_result", response.Content);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ public async Task<bool> Execute(RoleDialogModel message)
var taskRequirement = state.GetState("requirement_detail");

// Get table names
var steps = message.Content.JsonArrayContent<SecondStagePlan>();
var states = _services.GetRequiredService<IConversationStateService>();
var steps = states.GetState("planning_result").JsonArrayContent<SecondStagePlan>();
var allTables = new List<string>();
var ddlStatements = "";
var relevantKnowledge = message.Content;
var relevantKnowledge = states.GetState("planning_result");
relevantKnowledge += states.GetState("dictionary_items");

foreach (var step in steps)
{
allTables.AddRange(step.Tables);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ public class SecondaryBreakdownTask

[JsonPropertyName("solution_search_question")]
public string SolutionQuestion { get; set; } = null!;

[JsonPropertyName("need_lookup_dictionary")]
public bool NeedLookupDictionary { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@
"parameters": {
"type": "object",
"properties": {
"related_tables": {
"type": "array",
"description": "table name in planning steps",
"items": {
"type": "string",
"description": "table name"
}
}
},
"required": []
"required": [ "related_tables" ]
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
Use the TwoStagePlanner approach to plan the overall implementation steps, follow the below steps strictly.
1. Call plan_primary_stage to generate the primary plan.
2. If need_additional_information is true, call plan_secondary_stage for the specific primary stage.
3. You must call plan_summary to generate final planned steps.
4. If you can't generate the final accurate planning steps due to missing some specific informations, please ask user for more information.
3. Repeat step 2 until you processed all the primary stages.
4. If need_lookup_dictionary is true, call sql_dictionary_lookup to verify or get the enum/term/dictionary value. Pull id and name.
If you no items retured, you can pull all the list and find the match.
5. You must call plan_summary for you final planned output.

*** IMPORTANT ***
Don't run the planning process repeatedly if you have already got the result of user's request.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Thinking process:
- If there is extra knowledge or relationship needed between steps, set the need_additional_information to true for both steps.
- If the solution mentioned "related solutions" is needed, set the need_additional_information to true.
- You should find the relationships between data structure based on the task knowledge strictly. If lack of information, set the need_additional_information to true.
- If you need to verify or get the enum/term/dictionary value, set the need_additional_information to true.
3. Input argument must reference to corresponding variable name that retrieved by previous steps, variable name must start with '@';
4. Output all the subtasks as much detail as possible in JSON: [{{ response_format }}]
5. You can NOT generate the final query before calling function plan_summary.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Reference to "Primary Planning" and the additional knowledge included. Breakdown
* The parameters can be extracted from the original task.
* You need to list all the steps in detail. Finding relationships should also be a step.
* When generate the steps, you should find the relationships between data structure based on the provided knowledge strictly.
* If need_lookup_dictionary is true, call sql_dictionary_lookup to verify or get the enum/term/dictionary value. Pull id and name/code.
* Output all the steps as much detail as possible in JSON: [{{ response_format }}]


Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
For every primary step, if need_additional_information is true, you have to call plan_secondary_stage to plan the detail steps to complete the primary step.
For every primary step, if need_additional_information is true, you have to call plan_secondary_stage to plan the detail steps to complete the primary step.
if need_lookup_dictionary is true, you have to call sql_dictionary_lookup to verify or get the enum/term/dictionary value. Pull id and name/code.
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,27 @@

<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_dictionary_lookup.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_dictionary_lookup.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\functions\execute_sql.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\lookup_dictionary.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_insert.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_select.json" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\instructions\instruction.liquid" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\lookup_dictionary.liquid" />
<None Remove="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\sql_dictionary_lookup.liquid" />
</ItemGroup>

<ItemGroup>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\functions\sql_dictionary_lookup.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_dictionary_lookup.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\get_table_definition.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Expand All @@ -46,10 +54,7 @@
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\instructions\instruction.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\lookup_dictionary.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\lookup_dictionary.json">
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\sql_dictionary_lookup.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\functions\sql_insert.json">
Expand Down
1 change: 1 addition & 0 deletions src/Plugins/BotSharp.Plugin.SqlDriver/Enum/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ namespace BotSharp.Plugin.SqlDriver.Enum;
public class Utility
{
public const string SqlExecutor = "sql-executor";
public const string SqlDictionaryLookup = "sql-dictionary-lookup";
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Azure;
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Core.Infrastructures;
Expand All @@ -9,7 +10,7 @@ namespace BotSharp.Plugin.SqlDriver.Functions;

public class LookupDictionaryFn : IFunctionCallback
{
public string Name => "lookup_dictionary";
public string Name => "sql_dictionary_lookup";
private readonly IServiceProvider _services;

public LookupDictionaryFn(IServiceProvider services)
Expand All @@ -21,58 +22,24 @@ public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<LookupDictionary>(message.FunctionArgs);

// check if need to instantely
var settings = _services.GetRequiredService<SqlDriverSetting>();
using var connection = new MySqlConnection(settings.MySqlConnectionString);
var dictionary = new Dictionary<string, object>();
var results = connection.Query($"SELECT * FROM {args.Table} LIMIT 10");
var items = new List<string>();
foreach(var item in results)
{
items.Add(JsonSerializer.Serialize(item));
}

var agentService = _services.GetRequiredService<IAgentService>();
var agent = await agentService.LoadAgent(message.CurrentAgentId);
var prompt = GetPrompt(agent, items, args.Keyword);

// Ask LLM which one is the best
var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
var model = llmProviderService.GetProviderModel("azure-openai", "gpt-35-turbo");
using var connection = new MySqlConnection(settings.MySqlExecutionConnectionString);
var result = connection.Query(args.SqlStatement);

// chat completion
var completion = CompletionProvider.GetChatCompletion(_services,
provider: "azure-openai",
model: model.Name);

var conversations = new List<RoleDialogModel>
if (result == null)
{
new RoleDialogModel(AgentRole.User, prompt)
{
CurrentAgentId = message.CurrentAgentId,
MessageId = message.MessageId,
}
};

var response = await completion.GetChatCompletions(new Agent
message.Content = "Record not found";
}
else
{
Id = message.CurrentAgentId,
Instruction = ""
}, conversations);

message.Content = response.Content;
message.Content = JsonSerializer.Serialize(result);
}
var states = _services.GetRequiredService<IConversationStateService>();
var dictionaryItems = states.GetState("dictionary_items", "");
dictionaryItems += "\r\n\r\n" + args.Reason + ":\r\n" + message.Content + "\r\n";
states.SetState("dictionary_items", dictionaryItems);

return true;
}

private string GetPrompt(Agent agent, List<string> task, string keyword)
{
var template = agent.Templates.First(x => x.Name == "lookup_dictionary").Content;

var render = _services.GetRequiredService<ITemplateRender>();
return render.Render(template, new Dictionary<string, object>
{
{ "items", task },
{ "keyword", keyword }
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Settings;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Repositories;
using System.Collections.Generic;

namespace BotSharp.Plugin.SqlDriver.Hooks;

public class SqlDictionaryLookupHook : AgentHookBase, IAgentHook
{
private const string SQL_EXECUTOR_TEMPLATE = "sql_dictionary_lookup.fn";
private IEnumerable<string> _targetSqlExecutorFunctions = new List<string>
{
"sql_dictionary_lookup",
};

public override string SelfId => BuiltInAgentId.Planner;

public SqlDictionaryLookupHook(IServiceProvider services, AgentSettings settings) : base(services, settings)
{
}

public override void OnAgentLoaded(Agent agent)
{
var conv = _services.GetRequiredService<IConversationService>();
var isConvMode = conv.IsConversationMode();
var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(Utility.SqlDictionaryLookup);

if (isConvMode && isEnabled)
{
var (prompt, fns) = GetPromptAndFunctions();
if (!fns.IsNullOrEmpty())
{
if (!string.IsNullOrWhiteSpace(prompt))
{
agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n";
}

if (agent.Functions == null)
{
agent.Functions = fns;
}
else
{
agent.Functions.AddRange(fns);
}
}
}

base.OnAgentLoaded(agent);
}

private (string, List<FunctionDef>?) GetPromptAndFunctions()
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant);
var fns = agent?.Functions?.Where(x => _targetSqlExecutorFunctions.Contains(x.Name))?.ToList();

var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(SQL_EXECUTOR_TEMPLATE))?.Content ?? string.Empty;
var dbType = GetDatabaseType();
var render = _services.GetRequiredService<ITemplateRender>();
prompt = render.Render(prompt, new Dictionary<string, object>
{
{ "db_type", dbType }
});

return (prompt, fns);
}

private string GetDatabaseType()
{
var settings = _services.GetRequiredService<SqlDriverSetting>();
var dbType = "MySQL";

if (!string.IsNullOrWhiteSpace(settings?.SqlServerConnectionString))
{
dbType = "SQL Server";
}
else if (!string.IsNullOrWhiteSpace(settings?.SqlLiteConnectionString))
{
dbType = "SQL Lite";
}
return dbType;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
namespace BotSharp.Plugin.SqlDriver.Hooks;

public class SqlExecutorUtilityHook : IAgentUtilityHook
public class SqlUtilityHook : IAgentUtilityHook
{
public void AddUtilities(List<string> utilities)
{
utilities.Add(Utility.SqlExecutor);
utilities.Add(Utility.SqlDictionaryLookup);
}
}
11 changes: 4 additions & 7 deletions src/Plugins/BotSharp.Plugin.SqlDriver/Models/LookupDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ namespace BotSharp.Plugin.SqlDriver.Models;

public class LookupDictionary
{
[JsonPropertyName("table")]
public string Table { get; set; }

[JsonPropertyName("keyword")]
public string Keyword { get; set; }
[JsonPropertyName("sql_statement")]
public string SqlStatement { get; set; }

[JsonPropertyName("reason")]
public string Reason { get; set; }

[JsonPropertyName("columns")]
public string[] Columns { get; set; }
[JsonPropertyName("table")]
public string Table { get; set; }
}
3 changes: 2 additions & 1 deletion src/Plugins/BotSharp.Plugin.SqlDriver/SqlDriverPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
services.AddScoped<DbKnowledgeService>();
services.AddScoped<IKnowledgeHook, SqlDriverKnowledgeHook>();
services.AddScoped<IAgentHook, SqlExecutorHook>();
services.AddScoped<IAgentUtilityHook, SqlExecutorUtilityHook>();
services.AddScoped<IAgentUtilityHook, SqlUtilityHook>();
services.AddScoped<IPlanningHook, SqlDriverPlanningHook>();
services.AddScoped<IAgentHook, SqlDictionaryLookupHook>();
}
}
Loading