Merge pull request #314 from hchen2020/master

Improve SQL Driver.
This commit is contained in:
C. Oceania 2024-02-21 17:26:17 -06:00 committed by GitHub
commit d96542f562
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 88 additions and 100 deletions

View file

@ -231,6 +231,10 @@ public class ChatCompletionProvider : IChatCompletion
{
if (message.Role == ChatRole.Function)
{
chatCompletionsOptions.Messages.Add(new ChatRequestAssistantMessage(string.Empty)
{
FunctionCall = new FunctionCall(message.FunctionName, message.FunctionArgs),
});
chatCompletionsOptions.Messages.Add(new ChatRequestFunctionMessage(message.FunctionName, message.Content));
}
else if (message.Role == ChatRole.User)
@ -287,7 +291,7 @@ public class ChatCompletionProvider : IChatCompletion
if (x.Role == ChatRole.Function)
{
var m = x as ChatRequestFunctionMessage;
return $"{m.Role}: {m.Name} => {m.Content}";
return $"{m.Role}: {m.Content}";
}
else if (x.Role == ChatRole.User)
{
@ -299,7 +303,9 @@ public class ChatCompletionProvider : IChatCompletion
else if (x.Role == ChatRole.Assistant)
{
var m = x as ChatRequestAssistantMessage;
return $"{m.Role}: {m.Content}";
return m.FunctionCall != null ?
$"{m.Role}: Call function {m.FunctionCall.Name}({m.FunctionCall.Arguments})" :
$"{m.Role}: {m.Content}";
}
else
{

View file

@ -1,9 +1,7 @@
using BotSharp.Abstraction.Loggers.Models;
using BotSharp.Abstraction.Messaging;
using BotSharp.Abstraction.Messaging.Enums;
using BotSharp.Abstraction.Messaging.JsonConverters;
using BotSharp.Abstraction.Messaging.Models.RichContent;
using BotSharp.Abstraction.Repositories;
using Microsoft.AspNetCore.SignalR;
namespace BotSharp.Plugin.ChatHub.Hooks;

View file

@ -1,14 +1,11 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.Functions.Models;
using BotSharp.Abstraction.Loggers;
using BotSharp.Abstraction.Loggers.Models;
using BotSharp.Abstraction.Messaging.Models.RichContent;
using BotSharp.Abstraction.Messaging;
using BotSharp.Abstraction.Repositories;
using BotSharp.Core.Agents.Services;
using BotSharp.Abstraction.Repositories.Filters;
using BotSharp.Abstraction.Routing.Settings;
using Microsoft.AspNetCore.SignalR;
using Microsoft.VisualBasic;
namespace BotSharp.Plugin.ChatHub.Hooks;
@ -82,6 +79,17 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook
var conversationId = _state.GetConversationId();
var agent = await agentService.LoadAgent(message.CurrentAgentId);
// Log routing output
try
{
var inst = message.Content.JsonContent<FunctionCallFromLlm>();
await _chatHub.Clients.User(_user.Id).SendAsync("OnConversationContentLogGenerated", BuildContentLog(conversationId, agent?.Name, message.Content, message));
}
catch
{
// ignore
}
string log;
if (message.Role == AgentRole.Function)
{

View file

@ -17,10 +17,10 @@ public class GetTableColumnsFn : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<GetTableColumnsArgs>(message.FunctionArgs);
message.Content = $"Success. Columns of table '{args.Table}':\r\n\r\n";
message.Content = "";
var dbSettings = _services.GetRequiredService<BotSharpDatabaseSettings>();
var dir = Path.Combine(dbSettings.FileRepository, "agents", "ec46f15b-8790-400f-a37f-1e7995b7d6e2", "schemas");
var dir = Path.Combine(dbSettings.FileRepository, "agents", "beda4c12-e1ec-4b4b-b328-3df4a6687c4f", "schemas");
// Search related document by message.Content + args.Description
var files = Directory.GetFiles(dir);

View file

@ -16,13 +16,8 @@ public class SqlInsertFn : IFunctionCallback
{
var args = JsonSerializer.Deserialize<SqlStatement>(message.FunctionArgs);
var sqlDriver = _services.GetRequiredService<SqlDriverService>();
if (sqlDriver.Statements.Exists(x => x.Statement == args.Statement))
{
message.Content = "Skipped duplicated statement.";
return false;
}
sqlDriver.Enqueue(args);
message.Content = $"Inserted new record {JsonSerializer.Serialize(args.Parameters)} successfully";
message.Content = $"Inserted new record successfully.";
if (args.Return != null)
{
/*sqlDriver.Enqueue(new SqlStatement

View file

@ -17,6 +17,8 @@ public class SqlSelect : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message)
{
var args = JsonSerializer.Deserialize<SqlStatement>(message.FunctionArgs);
var sqlDriver = _services.GetRequiredService<SqlDriverService>();
// check if need to instantely
var execNow = !args.Parameters.Any(x => x.Value.StartsWith("@"));
if (execNow)
@ -28,24 +30,24 @@ public class SqlSelect : IFunctionCallback
{
dictionary["@" + p.Name] = p.Value;
}
var result = connection.QueryFirst<string>(args.Statement, dictionary);
var result = connection.QueryFirstOrDefault(args.Statement, dictionary);
if (args.IsCheckExistence)
if (result == null)
{
message.Content = result == null ?
$"The record does not exist" :
$"The record already exists";
message.Content = "Record not found";
}
else
{
message.Content = $"Retrieved result is {result} ({args.Reason})";
message.Content = JsonSerializer.Serialize(result);
args.Return.Value = message.Content;
}
sqlDriver.Enqueue(args);
}
else
{
var sqlDriver = _services.GetRequiredService<SqlDriverService>();
sqlDriver.Enqueue(args);
message.Content = $"Success.";
message.Content = $"The {args.Return.Name} is saved to @{args.Return.Alias}";
}
return true;

View file

@ -1,32 +0,0 @@
using BotSharp.Abstraction.Loggers;
using BotSharp.Abstraction.Repositories;
using System.IO;
namespace BotSharp.Plugin.SqlDriver.Hooks;
public class SqlDriverContentGeneratingHook : IContentGeneratingHook
{
private readonly IServiceProvider _services;
public SqlDriverContentGeneratingHook(IServiceProvider services)
{
_services = services;
}
/// <summary>
/// Inject useful variables generated by previous SQL query.
/// </summary>
/// <param name="agent"></param>
/// <param name="conversations"></param>
/// <returns></returns>
public async Task BeforeGenerating(Agent agent, List<RoleDialogModel> conversations)
{
if (agent.Id != "beda4c12-e1ec-4b4b-b328-3df4a6687c4f")
{
return;
}
var sqlDriver = _services.GetRequiredService<SqlDriverService>();
agent.TemplateDict["return_variables"] = sqlDriver.Statements.Select(x => x.Return.Alias).ToArray();
await Task.CompletedTask;
}
}

View file

@ -2,7 +2,7 @@ using System.Text.Json.Serialization;
namespace BotSharp.Plugin.SqlDriver.Models;
public class SqlParamater
public class SqlParameter
{
[JsonPropertyName("name")]
public string Name { get; set; }

View file

@ -10,6 +10,8 @@ public class SqlReturn
[JsonPropertyName("alias")]
public string Alias { get; set; }
public string? Value { get; set; }
public override string ToString()
{
return $"{Alias} - {Name}";

View file

@ -13,11 +13,8 @@ public class SqlStatement
[JsonPropertyName("table")]
public string Table { get; set; }
[JsonPropertyName("is_check_existence")]
public bool IsCheckExistence { get; set; }
[JsonPropertyName("parameters")]
public SqlParamater[] Parameters { get; set; } = new SqlParamater[0];
public SqlParameter[] Parameters { get; set; } = new SqlParameter[0];
[JsonPropertyName("return_field")]
public SqlReturn Return { get; set; }

View file

@ -23,20 +23,24 @@ public class SqlDriverService
{
Console.WriteLine();
Console.Write($"Reason: ");
Console.WriteLine($"{sql.Reason}", Color.Green);
Console.WriteLine($"{sql.Reason}");
Console.Write($"Statement: ");
Console.WriteLine(sql.Statement, Color.Green);
Console.WriteLine(sql.Statement, Color.Yellow);
foreach (var p in sql.Parameters)
{
Console.Write($"@{p.Name}: ");
Console.WriteLine($"{p.Value}", Color.Green);
Console.WriteLine($"@{p.Name} = '{p.Value}'", Color.Green);
}
if (sql.Return != null)
{
Console.Write($"Return: ");
Console.WriteLine($"{sql.Return.Name} as @{sql.Return.Alias}", Color.Green);
if (!string.IsNullOrEmpty(sql.Return.Value))
{
Console.WriteLine($" {sql.Return.Value}", Color.Red);
}
else
{
Console.WriteLine($"{sql.Return.Name} as @{sql.Return.Alias}", Color.Green);
}
}
}
}

View file

@ -1,5 +1,3 @@
using BotSharp.Abstraction.Loggers;
namespace BotSharp.Plugin.SqlDriver;
public class SqlDriverPlugin : IBotSharpPlugin
@ -19,6 +17,5 @@ public class SqlDriverPlugin : IBotSharpPlugin
services.AddScoped<SqlDriverService>();
services.AddScoped<IKnowledgeHook, SqlDriverKnowledgeHook>();
services.AddScoped<IContentGeneratingHook, SqlDriverContentGeneratingHook>();
}
}

View file

@ -1,4 +1,18 @@
[
{
"name": "get_table_columns",
"description": "Get related table columns and foreign key informations",
"parameters": {
"type": "object",
"properties": {
"table": {
"type": "string",
"description": "table name"
}
},
"required": [ "table" ]
}
},
{
"name": "sql_insert",
"description": "Insert query is generated if the record doesn't exist.",
@ -13,12 +27,16 @@
"type": "string",
"description": "reason"
},
"table": {
"type": "string",
"description": "related table"
},
"parameters": {
"type": "array",
"description": "parameters for the sql",
"description": "a list of parameters in the statement match with the variables",
"items": {
"type": "object",
"description": "the name and value for the parameter",
"description": "{name:'', value:''}",
"properties": {
"name": {
"type": "string",
@ -28,7 +46,8 @@
"type": "string",
"description": "real value inferred by the context"
}
}
},
"required": [ "name", "value" ]
}
},
"return_field": {
@ -43,10 +62,11 @@
"type": "string",
"description": "meaningful field alias"
}
}
},
"required": [ "name", "alias" ]
}
},
"required": [ "sql_statement", "reason", "parameters", "return_field" ]
"required": [ "sql_statement", "reason", "table", "parameters", "return_field" ]
}
},
{
@ -63,9 +83,9 @@
"type": "string",
"description": "reason"
},
"is_check_existence": {
"type": "boolean",
"description": "check record existence"
"table": {
"type": "string",
"description": "related table"
},
"parameters": {
"type": "array",
@ -82,7 +102,8 @@
"type": "string",
"description": "real value inferred by the context"
}
}
},
"required": [ "name", "value" ]
}
},
"return_field": {
@ -97,10 +118,11 @@
"type": "string",
"description": "meaningful field alias"
}
}
},
"required": [ "name", "value" ]
}
},
"required": [ "sql_statement", "reason", "is_check_existence", "parameters", "return_field" ]
"required": [ "sql_statement", "reason", "table", "parameters", "return_field" ]
}
}
]

View file

@ -1,22 +1,11 @@
You're a SQL driver who knows how to translate text into SQL query.
Analyze the user requirement, think step by step, breakdown complex task into multiple steps.
Think step by step, analyze the user requirement, you must get table schema first, breakdown into multiple sql statements if user need to insert mulitple records.
Output the next step smartly.
Your response must meet below requirements:
* Walk through the provided information, don't run query if there is already related information;
* DO NOT generate duplicated sql statements;
* The return field alias should be meaningful, it can be similar name of reference table column;
* Double check if the fields in the SQL query are correct;
* Make sure the SELECT and WHERE fields are in corresponding table schema definition;
* Use "Unique Index" to help check record existence;
{% if return_variables and return_variables != empty -%}
=====
Below variables can be used by subsequent SQL:
{% for v in return_variables %}
- @{{ v }}
{% endfor %}
{%- endif %}
{% if table_definition -%}
=====
Related table {{ related_table }} definition:
{{ table_definition }}
{%- endif %}
* For INSERT statement with mutliple records, should return in different meaningful alias;