Sync with upstream

This commit is contained in:
Haiping Chen 2024-10-01 21:34:23 -05:00
commit 89205bd169
21 changed files with 133 additions and 71 deletions

View file

@ -36,4 +36,6 @@ public class BuiltInAgentId
/// Plan feasible implementation steps for complex problems
/// </summary>
public const string Planner = "282a7128-69a1-44b0-878c-a9159b88f3b9";
public const string SqlDriver = "beda4c12-e1ec-4b4b-b328-3df4a6687c4f";
}

View file

@ -74,6 +74,12 @@ public partial class ConversationService
// Routing with reasoning
var settings = _services.GetRequiredService<RoutingSettings>();
// reload agent in case it has been changed by hook
if (message.CurrentAgentId != agent.Id)
{
agent = await agentService.LoadAgent(message.CurrentAgentId);
}
if (agent.Type == AgentType.Routing)
{
response = await routing.InstructLoop(message, dialogs);

View file

@ -17,16 +17,14 @@ public class SecondaryStagePlanFn : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message)
{
var fn = _services.GetRequiredService<IRoutingService>();
var agentService = _services.GetRequiredService<IAgentService>();
var knowledgeService = _services.GetRequiredService<IKnowledgeService>();
var knowledgeSettings = _services.GetRequiredService<KnowledgeBaseSettings>();
var states = _services.GetRequiredService<IConversationStateService>();
var msgSecondary = RoleDialogModel.From(message);
var collectionName = knowledgeSettings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp;
var collectionName = knowledgeSettings.Default.CollectionName;
var planPrimary = states.GetState("planning_result");
var taskPrimary = states.GetState("requirement_detail");
var taskSecondary = JsonSerializer.Deserialize<SecondaryBreakdownTask>(msgSecondary.FunctionArgs);
@ -35,8 +33,8 @@ public class SecondaryStagePlanFn : IFunctionCallback
{
Confidence = 0.6f
});
var knowledgeResults = "";
knowledgeResults = string.Join("\r\n\r\n=====\r\n", knowledges.Select(x => x.ToQuestionAnswer()));
var knowledgeResults = string.Join("\r\n\r\n=====\r\n", knowledges.Select(x => x.ToQuestionAnswer()));
// Get second stage planning prompt
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
@ -45,7 +43,7 @@ public class SecondaryStagePlanFn : IFunctionCallback
var plannerAgent = new Agent
{
Id = string.Empty,
Id = BuiltInAgentId.Planner,
Name = "planning_2nd",
Instruction = secondPlanningPrompt,
TemplateDict = new Dictionary<string, object>(),

View file

@ -33,7 +33,7 @@ public class SummaryPlanFn : IFunctionCallback
var states = _services.GetRequiredService<IConversationStateService>();
var steps = states.GetState("planning_result").JsonArrayContent<SecondStagePlan>();
var allTables = new List<string>();
var ddlStatements = "";
var ddlStatements = string.Empty;
var relevantKnowledge = states.GetState("planning_result");
var dictionaryItems = states.GetState("dictionary_items");
@ -42,6 +42,7 @@ public class SummaryPlanFn : IFunctionCallback
allTables.AddRange(step.Tables);
}
var distinctTables = allTables.Distinct().ToList();
foreach (var table in distinctTables)
{
var msgCopy = RoleDialogModel.From(message);

View file

@ -11,7 +11,7 @@
"profiles": [ "planning" ],
"utilities": [ "two-stage-planner" ],
"llmConfig": {
"provider": "azure-openai",
"provider": "openai",
"model": "gpt-4o",
"max_recursion_depth": 10
}

View file

@ -74,7 +74,7 @@ namespace BotSharp.Plugin.SemanticKernel
{
resultTexts.Add(new VectorCollectionData
{
Data = new Dictionary<string, string> { { "text", record.Metadata.Text } },
Data = new Dictionary<string, object> { { "text", record.Metadata.Text } },
Score = score,
Vector = withVector ? record.Embedding.ToArray() : null
});
@ -83,7 +83,7 @@ namespace BotSharp.Plugin.SemanticKernel
return resultTexts;
}
public async Task<bool> Upsert(string collectionName, Guid id, float[] vector, string text, Dictionary<string, string>? payload)
public async Task<bool> Upsert(string collectionName, Guid id, float[] vector, string text, Dictionary<string, object>? payload)
{
#pragma warning disable SKEXP0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
await _memoryStore.UpsertAsync(collectionName, MemoryRecord.LocalRecord(id.ToString(), text, null, vector));

View file

@ -29,6 +29,7 @@
<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\query_result_formatting.liquid" />
</ItemGroup>
<ItemGroup>
@ -68,6 +69,9 @@
<Content Include="data\agents\6745151e-6d46-4a02-8de4-1c4f21c7da95\templates\sql_executor.fn.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\query_result_formatting.liquid">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
@ -80,8 +84,4 @@
<ProjectReference Include="..\..\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="data\agents\beda4c12-e1ec-4b4b-b328-3df4a6687c4f\templates\" />
</ItemGroup>
</Project>

View file

@ -19,6 +19,6 @@ public class SqlDriverController : ControllerBase
public async Task<bool> ImportDbKnowledge(ImportDbKnowledgeRequest request)
{
var dbKnowledge = _services.GetRequiredService<DbKnowledgeService>();
return await dbKnowledge.Import(request.Provider ?? "openai", request.Model ?? "gpt-4o", request.Schema);
return await dbKnowledge.Import(request);
}
}

View file

@ -1,3 +1,5 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Core.Infrastructures;
using BotSharp.Plugin.SqlDriver.Models;
using Dapper;
using Microsoft.Data.SqlClient;
@ -32,10 +34,31 @@ public class ExecuteQueryFn : IFunctionCallback
if (results.Count() == 0)
{
message.Content = "No record found";
return true;
}
else
{
message.Content = JsonSerializer.Serialize(results);
if (args.FormattingResult)
{
var conv = _services.GetRequiredService<IConversationService>();
var sqlAgent = await _services.GetRequiredService<IAgentService>().LoadAgent(BuiltInAgentId.SqlDriver);
var prompt = sqlAgent.Templates.FirstOrDefault(x => x.Name == "query_result_formatting");
var completion = CompletionProvider.GetChatCompletion(_services,
provider: sqlAgent.LlmConfig.Provider,
model: sqlAgent.LlmConfig.Model);
var result = await completion.GetChatCompletions(new Agent
{
Id = sqlAgent.Id,
Instruction = prompt.Content,
}, new List<RoleDialogModel>
{
new RoleDialogModel(AgentRole.User, message.Content)
});
message.Content = result.Content;
}
return true;
@ -52,7 +75,6 @@ public class ExecuteQueryFn : IFunctionCallback
{
var settings = _services.GetRequiredService<SqlDriverSetting>();
using var connection = new SqlConnection(settings.SqlServerExecutionConnectionString ?? settings.SqlServerConnectionString);
var dictionary = new Dictionary<string, object>();
return connection.Query(string.Join("\r\n", sqlTexts));
}
}

View file

@ -1,5 +1,4 @@
using BotSharp.Plugin.SqlDriver.Models;
using Fluid.Ast.BinaryExpressions;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Logging;
using MySqlConnector;
@ -37,10 +36,6 @@ public class GetTableDefinitionFn : IFunctionCallback
};
message.Content = string.Join("\r\n\r\n", tableDdls);
//var states = _services.GetRequiredService<IConversationStateService>();
//states.SetState($"table_definition_{args.Table}", message.Content);
return true;
}
@ -48,7 +43,7 @@ public class GetTableDefinitionFn : IFunctionCallback
{
var settings = _services.GetRequiredService<SqlDriverSetting>();
var tableDdls = new List<string>();
using var connection = new MySqlConnection(settings.MySqlExecutionConnectionString);
using var connection = new MySqlConnection(settings.MySqlExecutionConnectionString ?? settings.MySqlConnectionString);
connection.Open();
foreach (var table in tables)
@ -76,7 +71,6 @@ public class GetTableDefinitionFn : IFunctionCallback
}
connection.Close();
return tableDdls;
}
@ -129,7 +123,6 @@ SELECT @SQL;";
}
connection.Close();
return tableDdls;
}
}

View file

@ -1,14 +1,9 @@
using Azure;
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Routing;
using BotSharp.Core.Agents.Services;
using BotSharp.Core.Infrastructures;
using BotSharp.Plugin.SqlDriver.Models;
using MySqlConnector;
using System.Text.RegularExpressions;
using static Dapper.SqlMapper;
using static System.Net.Mime.MediaTypeNames;
namespace BotSharp.Plugin.SqlDriver.Functions;
@ -35,15 +30,16 @@ public class LookupDictionaryFn : IFunctionCallback
var agentService = _services.GetRequiredService<IAgentService>();
var currentAgent = await agentService.LoadAgent(message.CurrentAgentId);
var dictionarySqlPrompt = await GetDictionarySQLPrompt(args.SqlStatement, msgCopy.Content);
var plannerAgent = new Agent
var agent = new Agent
{
Id = string.Empty,
Id = message.CurrentAgentId ?? string.Empty,
Name = "sqlDriver_DictionarySearch",
Instruction = dictionarySqlPrompt,
TemplateDict = new Dictionary<string, object>(),
LlmConfig = currentAgent.LlmConfig
};
var response = await GetAiResponse(plannerAgent);
var response = await GetAiResponse(agent);
args = JsonSerializer.Deserialize<LookupDictionary>(response.Content);
// check if need to instantely
@ -59,13 +55,16 @@ public class LookupDictionaryFn : IFunctionCallback
{
message.Content = JsonSerializer.Serialize(result);
}
var states = _services.GetRequiredService<IConversationStateService>();
var dictionaryItems = states.GetState("dictionary_items", "");
dictionaryItems += "\r\n\r\n" + args.Table + ":\r\n" + args.Reason + ":\r\n" + message.Content + "\r\n";
var newItem = BuildDictionaryItem(args.Table, args.Reason, message.Content);
dictionaryItems += !string.IsNullOrWhiteSpace(newItem) ? $"\r\n{newItem}\r\n" : string.Empty;
states.SetState("dictionary_items", dictionaryItems);
return true;
}
private async Task<string> GetDictionarySQLPrompt(string originalSql, string tableStructure)
{
var agentService = _services.GetRequiredService<IAgentService>();
@ -83,15 +82,45 @@ public class LookupDictionaryFn : IFunctionCallback
{ "response_format", responseFormat }
});
}
private async Task<RoleDialogModel> GetAiResponse(Agent plannerAgent)
private async Task<RoleDialogModel> GetAiResponse(Agent agent)
{
var text = "Check and correct the SQL statement.";
var message = new RoleDialogModel(AgentRole.User, text);
var completion = CompletionProvider.GetChatCompletion(_services,
provider: plannerAgent.LlmConfig.Provider,
model: plannerAgent.LlmConfig.Model);
provider: agent.LlmConfig.Provider,
model: agent.LlmConfig.Model);
return await completion.GetChatCompletions(plannerAgent, new List<RoleDialogModel> { message });
return await completion.GetChatCompletions(agent, new List<RoleDialogModel> { message });
}
private string BuildDictionaryItem(string? table, string? reason, string? result)
{
var res = string.Empty;
if (!string.IsNullOrWhiteSpace(table))
{
res += $"Table: {table}";
}
if (!string.IsNullOrWhiteSpace(reason))
{
if (!string.IsNullOrWhiteSpace(res))
{
res += "\r\n";
}
res += $"Reason: {reason}";
}
if (!string.IsNullOrWhiteSpace(result))
{
if (!string.IsNullOrWhiteSpace(res))
{
res += "\r\n";
}
res += $"Result: {result}";
}
return res;
}
}

View file

@ -2,7 +2,6 @@ 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;

View file

@ -1,7 +1,6 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Routing;
using BotSharp.Core.Agents.Services;
using BotSharp.Core.Infrastructures;
namespace BotSharp.Plugin.SqlDriver.Hooks;

View file

@ -6,4 +6,9 @@ public class ExecuteQueryArgs
{
[JsonPropertyName("sql_statements")]
public string[] SqlStatements { get; set; } = [];
/// <summary>
/// Beautifying query result
/// </summary>
public bool FormattingResult { get; set; }
}

View file

@ -5,11 +5,11 @@ namespace BotSharp.Plugin.SqlDriver.Models;
public class LookupDictionary
{
[JsonPropertyName("sql_statement")]
public string SqlStatement { get; set; }
public string? SqlStatement { get; set; }
[JsonPropertyName("reason")]
public string Reason { get; set; }
public string? Reason { get; set; }
[JsonPropertyName("table")]
public string Table { get; set; }
public string? Table { get; set; }
}

View file

@ -16,4 +16,7 @@ public class ImportDbKnowledgeRequest : RequestBase
{
[JsonPropertyName("schema")]
public string Schema { get; set; }
[JsonPropertyName("knowledgebase_collection")]
public string KnowledgebaseCollection { get; set; }
}

View file

@ -3,9 +3,9 @@ using Microsoft.Extensions.Logging;
using BotSharp.Core.Infrastructures;
using MySqlConnector;
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Knowledges.Settings;
using BotSharp.Abstraction.Knowledges.Enums;
using BotSharp.Abstraction.VectorStorage.Models;
using BotSharp.Plugin.SqlDriver.Models;
namespace BotSharp.Plugin.SqlDriver.Services;
@ -22,12 +22,14 @@ public class DbKnowledgeService
_logger = logger;
}
public async Task<bool> Import(string provider, string model, string schema)
public async Task<bool> Import(ImportDbKnowledgeRequest request)
{
var sqlDriverSettings = _services.GetRequiredService<SqlDriverSetting>();
var knowledgeSettings = _services.GetRequiredService<KnowledgeBaseSettings>();
var knowledgeService = _services.GetRequiredService<IKnowledgeService>();
var collectionName = knowledgeSettings.Default.CollectionName ?? KnowledgeCollectionName.BotSharp;
var provider = request.Provider ?? "openai";
var model = request.Model ?? "gpt-4o";
var schema = request.Schema;
var collectionName = request.KnowledgebaseCollection;
var tables = new HashSet<string>();
using var connection = new MySqlConnection(sqlDriverSettings.MySqlConnectionString);

View file

@ -9,7 +9,7 @@
"isPublic": true,
"profiles": [ "database" ],
"llmConfig": {
"provider": "azure-openai",
"provider": "openai",
"model": "gpt-4o-mini"
},
"routingRules": [

View file

@ -48,7 +48,7 @@ public class TwilioVoiceController : TwilioController
[ValidateRequest]
[HttpPost("twilio/voice/{conversationId}/receive/{seqNum}")]
public async Task<TwiMLResult> ReceiveCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, [FromQuery] string states, [FromQuery] int attempts, VoiceRequest request)
public async Task<TwiMLResult> ReceiveCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, [FromQuery] string states, VoiceRequest request, [FromQuery] int attempts = 1)
{
var twilio = _services.GetRequiredService<TwilioService>();
var messageQueue = _services.GetRequiredService<TwilioMessageQueue>();

View file

@ -120,6 +120,8 @@ namespace BotSharp.Plugin.Twilio.Services
break;
}
}
// add frequency short words
hints.AddRange(["yes", "no", "correct", "right"]);
reply.Hints = string.Join(", ", hints.Select(x => x.ToLower()).Distinct().Reverse());
reply.Content = null;
await sessionManager.SetAssistantReplyAsync(message.ConversationId, message.SeqNumber, reply);