From 30f3de9bc4aa928fd218ea2eca6e4de041e824e2 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Mon, 12 Aug 2024 14:22:47 -0500 Subject: [PATCH 01/13] python interpreter --- BotSharp.sln | 14 +++++ .../Models/InterpretationRequest.cs | 10 ++++ .../BotSharp.Plugin.PythonInterpreter.csproj | 35 +++++++++++++ .../Enums/UtilityName.cs | 6 +++ .../Functions/InterpretationFn.cs | 46 +++++++++++++++++ .../Hooks/InterpreterAgentHook.cs | 51 +++++++++++++++++++ .../Hooks/InterpreterUtilityHook.cs | 9 ++++ .../InterpreterPlugin.cs | 17 +++++++ .../Using.cs | 18 +++++++ .../functions/python_interpreter.json | 19 +++++++ .../templates/python_interpreter.fn.liquid | 1 + src/WebStarter/Program.cs | 8 +++ src/WebStarter/WebStarter.csproj | 1 + src/WebStarter/appsettings.json | 3 +- 14 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/InterpretationFn.cs create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterAgentHook.cs create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/python_interpreter.json create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/python_interpreter.fn.liquid diff --git a/BotSharp.sln b/BotSharp.sln index b7545cdc..8c1030f9 100644 --- a/BotSharp.sln +++ b/BotSharp.sln @@ -105,6 +105,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FileStorages", "FileStorage EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.TencentCos", "src\Plugins\BotSharp.Plugin.TencentCos\BotSharp.Plugin.TencentCos.csproj", "{BF029B0A-768B-43A1-8D91-E70B95505716}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interpreters", "Interpreters", "{C4C59872-3C8A-450D-83D5-2BE402D610D5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.PythonInterpreter", "src\Plugins\BotSharp.Plugin.PythonInterpreter\BotSharp.Plugin.PythonInterpreter.csproj", "{05E6E405-5021-406E-8A5E-0A7CEC881F6D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -425,6 +429,14 @@ Global {BF029B0A-768B-43A1-8D91-E70B95505716}.Release|Any CPU.Build.0 = Release|Any CPU {BF029B0A-768B-43A1-8D91-E70B95505716}.Release|x64.ActiveCfg = Release|Any CPU {BF029B0A-768B-43A1-8D91-E70B95505716}.Release|x64.Build.0 = Release|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Debug|x64.ActiveCfg = Debug|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Debug|x64.Build.0 = Debug|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|Any CPU.Build.0 = Release|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|x64.ActiveCfg = Release|Any CPU + {05E6E405-5021-406E-8A5E-0A7CEC881F6D}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -475,6 +487,8 @@ Global {54E83C6F-54EE-4ADC-8D72-93C009CC4FB4} = {51AFE054-AE99-497D-A593-69BAEFB5106F} {38B37C0D-1930-4D47-BCBF-E358EC1096B1} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C} {BF029B0A-768B-43A1-8D91-E70B95505716} = {38B37C0D-1930-4D47-BCBF-E358EC1096B1} + {C4C59872-3C8A-450D-83D5-2BE402D610D5} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C} + {05E6E405-5021-406E-8A5E-0A7CEC881F6D} = {C4C59872-3C8A-450D-83D5-2BE402D610D5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19} diff --git a/src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs b/src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs new file mode 100644 index 00000000..5a8f7552 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs @@ -0,0 +1,10 @@ +namespace BotSharp.Abstraction.Interpreters.Models; + +public class InterpretationRequest +{ + [JsonPropertyName("script")] + public string Script { get; set; } = null!; + + [JsonPropertyName("language")] + public string Language { get; set; } = null!; +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj b/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj new file mode 100644 index 00000000..b3293a0f --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj @@ -0,0 +1,35 @@ + + + + $(TargetFramework) + enable + $(LangVersion) + $(BotSharpVersion) + $(GeneratePackageOnBuild) + $(GenerateDocumentationFile) + $(SolutionDir)packages + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + + + + + + + + diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs new file mode 100644 index 00000000..95a5b8a2 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Plugin.PythonInterpreter.Enums; + +public class UtilityName +{ + public const string PythonInterpreter = "python-interpreter"; +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/InterpretationFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/InterpretationFn.cs new file mode 100644 index 00000000..b12cb739 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/InterpretationFn.cs @@ -0,0 +1,46 @@ +using BotSharp.Abstraction.Conversations.Models; +using BotSharp.Abstraction.Functions; +using BotSharp.Abstraction.Interpreters.Models; +using Microsoft.Extensions.Logging; +using Python.Runtime; +using System.Text.Json; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.PythonInterpreter.Functions; + +public class InterpretationFn : IFunctionCallback +{ + public string Name => "python_interpreter"; + public string Indication => "Interpreting python code"; + + private readonly IServiceProvider _services; + private readonly ILogger _logger; + + public async Task Execute(RoleDialogModel message) + { + var args = JsonSerializer.Deserialize(message.FunctionArgs); + + using (Py.GIL()) + { + // Import necessary Python modules + dynamic sys = Py.Import("sys"); + dynamic io = Py.Import("io"); + + // Redirect standard output to capture it + dynamic stringIO = io.StringIO(); + sys.stdout = stringIO; + + // Execute a simple Python script + using var locals = new PyDict(); + PythonEngine.Exec(args.Script, null, locals); + + // Console.WriteLine($"Result from Python: {result}"); + message.Content = stringIO.getvalue(); + + // Restore the original stdout + sys.stdout = sys.__stdout__; + } + + return true; + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterAgentHook.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterAgentHook.cs new file mode 100644 index 00000000..64e0ec27 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterAgentHook.cs @@ -0,0 +1,51 @@ +namespace BotSharp.Plugin.PythonInterpreter.Hooks; + +public class InterpreterAgentHook : AgentHookBase +{ + private static string FUNCTION_NAME = "python_interpreter"; + + public override string SelfId => string.Empty; + + public InterpreterAgentHook(IServiceProvider services, AgentSettings settings) + : base(services, settings) + { + } + public override void OnAgentLoaded(Agent agent) + { + var conv = _services.GetRequiredService(); + var isConvMode = conv.IsConversationMode(); + var isEnabled = !agent.Utilities.IsNullOrEmpty() && agent.Utilities.Contains(UtilityName.PythonInterpreter); + + if (isConvMode && isEnabled) + { + var (prompt, fn) = GetPromptAndFunction(); + if (fn != null) + { + if (!string.IsNullOrWhiteSpace(prompt)) + { + agent.Instruction += $"\r\n\r\n{prompt}\r\n\r\n"; + } + + if (agent.Functions == null) + { + agent.Functions = new List { fn }; + } + else + { + agent.Functions.Add(fn); + } + } + } + + base.OnAgentLoaded(agent); + } + + private (string, FunctionDef?) GetPromptAndFunction() + { + var db = _services.GetRequiredService(); + var agent = db.GetAgent(BuiltInAgentId.UtilityAssistant); + var prompt = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo($"{FUNCTION_NAME}.fn"))?.Content ?? string.Empty; + var loadAttachmentFn = agent?.Functions?.FirstOrDefault(x => x.Name.IsEqualTo(FUNCTION_NAME)); + return (prompt, loadAttachmentFn); + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs new file mode 100644 index 00000000..be37bfa1 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Plugin.PythonInterpreter.Hooks; + +public class InterpreterUtilityHook : IAgentUtilityHook +{ + public void AddUtilities(List utilities) + { + utilities.Add(UtilityName.PythonInterpreter); + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs new file mode 100644 index 00000000..075038e1 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs @@ -0,0 +1,17 @@ +using BotSharp.Plugin.PythonInterpreter.Hooks; + +namespace BotSharp.Plugin.PythonInterpreter; + +public class InterpreterPlugin : IBotSharpPlugin +{ + public string Id => "23174e08-e866-4173-824a-cf1d97afa8d0"; + public string Name => "Python Interpreter"; + public string Description => "Python Interpreter enables AI to write and execute Python code within a secure, sandboxed environment."; + public string? IconUrl => "https://static.vecteezy.com/system/resources/previews/012/697/295/non_2x/3d-python-programming-language-logo-free-png.png"; + + public void RegisterDI(IServiceCollection services, IConfiguration config) + { + services.AddScoped(); + services.AddScoped(); + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs new file mode 100644 index 00000000..8cc31aa1 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs @@ -0,0 +1,18 @@ +global using System; +global using System.Linq; +global using System.Collections.Generic; + +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; + +global using BotSharp.Abstraction.Agents; +global using BotSharp.Abstraction.Plugins; +global using BotSharp.Abstraction.Utilities; +global using BotSharp.Abstraction.Agents.Enums; +global using BotSharp.Abstraction.Agents.Models; +global using BotSharp.Abstraction.Agents.Settings; +global using BotSharp.Abstraction.Conversations; +global using BotSharp.Abstraction.Functions.Models; +global using BotSharp.Abstraction.Repositories; + +global using BotSharp.Plugin.PythonInterpreter.Enums; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/python_interpreter.json b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/python_interpreter.json new file mode 100644 index 00000000..ac2b2a91 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/python_interpreter.json @@ -0,0 +1,19 @@ +{ + "name": "python_interpreter", + "description": "write and execute python code, print the result in Console", + "parameters": { + "type": "object", + "properties": { + "script": { + "type": "string", + "description": "python code" + }, + "language": { + "type": "string", + "enum": [ "python" ], + "description": "python code" + } + }, + "required": [ "language", "script" ] + } +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/python_interpreter.fn.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/python_interpreter.fn.liquid new file mode 100644 index 00000000..8dd2425f --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/python_interpreter.fn.liquid @@ -0,0 +1 @@ +Write and execute Python script in python_interpreter function, and use python function print(a) to output the result in stand output. \ No newline at end of file diff --git a/src/WebStarter/Program.cs b/src/WebStarter/Program.cs index ed0f8baf..a13d9d0f 100644 --- a/src/WebStarter/Program.cs +++ b/src/WebStarter/Program.cs @@ -4,6 +4,7 @@ using BotSharp.Logger; using BotSharp.Plugin.ChatHub; using Serilog; using BotSharp.Abstraction.Messaging.JsonConverters; +using Python.Runtime; var builder = WebApplication.CreateBuilder(args); @@ -41,4 +42,11 @@ app.UseBotSharp() .UseBotSharpOpenAPI(app.Environment) .UseBotSharpUI(); +Runtime.PythonDLL = @"C:\Users\xxx\AppData\Local\Programs\Python\Python311\python311.dll"; +PythonEngine.Initialize(); +PythonEngine.BeginAllowThreads(); + app.Run(); + +// Shut down the Python engine +PythonEngine.Shutdown(); \ No newline at end of file diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index 5a5b1ced..c7443dc1 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -30,6 +30,7 @@ + diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 155dae1b..e2aed92b 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -319,7 +319,8 @@ "BotSharp.Plugin.HttpHandler", "BotSharp.Plugin.FileHandler", "BotSharp.Plugin.EmailHandler", - "BotSharp.Plugin.TencentCos" + "BotSharp.Plugin.TencentCos", + "BotSharp.Plugin.PythonInterpreter" ] } } From f2c1777ea9dadd88a278666f66a739619e84251f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 13 Aug 2024 13:26:57 -0500 Subject: [PATCH 02/13] add knowledge collections --- .../Knowledges/IKnowledgeService.cs | 1 + .../Infrastructures/SettingService.cs | 1 + .../Controllers/KnowledgeBaseController.cs | 6 ++++++ .../Functions/KnowledgeRetrievalFn.cs | 2 +- .../Functions/MemorizeKnowledgeFn.cs | 2 +- .../Services/KnowledgeService.Get.cs | 14 ++++++++++++++ src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs | 13 ++++++++++--- 7 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs index 657ac42a..d2882bdc 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs @@ -2,6 +2,7 @@ namespace BotSharp.Abstraction.Knowledges; public interface IKnowledgeService { + Task> GetKnowledgeCollections(); Task> SearchKnowledge(string collectionName, KnowledgeRetrievalOptions options); Task FeedKnowledge(string collectionName, KnowledgeCreationModel model); Task> GetKnowledgeCollectionData(string collectionName, KnowledgeFilter filter); diff --git a/src/Infrastructure/BotSharp.Core/Infrastructures/SettingService.cs b/src/Infrastructure/BotSharp.Core/Infrastructures/SettingService.cs index fe3d9007..cbde5f44 100644 --- a/src/Infrastructure/BotSharp.Core/Infrastructures/SettingService.cs +++ b/src/Infrastructure/BotSharp.Core/Infrastructures/SettingService.cs @@ -31,6 +31,7 @@ public class SettingService : ISettingService var plugins = pluginService.GetPlugins(_services); var plugin = plugins.First(x => x.Module.Settings.Name == settingName); var instance = plugin.Module.GetNewSettingsInstance(); + _config.Bind(settingName, instance); if (mask) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs index e20632f5..55712aaa 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs @@ -16,6 +16,12 @@ public class KnowledgeBaseController : ControllerBase _services = services; } + [HttpGet("knowledge/collections")] + public async Task> GetKnowledgeCollections() + { + return await _knowledgeService.GetKnowledgeCollections(); + } + [HttpPost("/knowledge/{collection}/search")] public async Task> SearchKnowledge([FromRoute] string collection, [FromBody] SearchKnowledgeModel model) { diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs index ca81a4ba..62397c53 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs @@ -21,7 +21,7 @@ public class KnowledgeRetrievalFn : IFunctionCallback embedding.SetModelName(_settings.TextEmbedding.Model); var vector = await embedding.GetVectorAsync(args.Question); - var vectorDb = _services.GetRequiredService(); + var vectorDb = _services.GetServices().FirstOrDefault(x => x.Name == _settings.VectorDb); var knowledges = await vectorDb.Search(KnowledgeCollectionName.BotSharp, vector, new List { KnowledgePayloadName.Answer }); if (!knowledges.IsNullOrEmpty()) diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs index 218b33d4..b4b0111c 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs @@ -25,7 +25,7 @@ public class MemorizeKnowledgeFn : IFunctionCallback args.Question }); - var vectorDb = _services.GetRequiredService(); + var vectorDb = _services.GetServices().FirstOrDefault(x => x.Name == _settings.VectorDb); await vectorDb.CreateCollection(KnowledgeCollectionName.BotSharp, vector[0].Length); var id = Guid.NewGuid().ToString(); diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs index 96a5c139..d8c64cf3 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs @@ -2,6 +2,20 @@ namespace BotSharp.Plugin.KnowledgeBase.Services; public partial class KnowledgeService { + public async Task> GetKnowledgeCollections() + { + try + { + var db = GetVectorDb(); + return await db.GetCollections(); + } + catch (Exception ex) + { + _logger.LogWarning($"Error when getting knowledge collections. {ex.Message}\r\n{ex.InnerException}"); + return Enumerable.Empty(); + } + } + public async Task> GetKnowledgeCollectionData(string collectionName, KnowledgeFilter filter) { try diff --git a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs index f03b4ab6..02520517 100644 --- a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs +++ b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs @@ -128,10 +128,17 @@ public class QdrantDb : IVectorDb public async Task> Search(string collectionName, float[] vector, IEnumerable fields, int limit = 5, float confidence = 0.5f, bool withVector = false) { - var client = GetClient(); - var points = await client.SearchAsync(collectionName, vector, limit: (ulong)limit, scoreThreshold: confidence); - var results = new List(); + + var client = GetClient(); + var exist = await DoesCollectionExist(client, collectionName); + if (!exist) + { + return results; + } + + var points = await client.SearchAsync(collectionName, vector, limit: (ulong)limit, scoreThreshold: confidence); + foreach (var point in points) { var data = new Dictionary(); From a7bc5fca887b9e1303848eed88cadfccecde04ae Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 13 Aug 2024 13:30:06 -0500 Subject: [PATCH 03/13] add try catch --- .../Services/KnowledgeService.Get.cs | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs index d8c64cf3..75824707 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs @@ -32,20 +32,28 @@ public partial class KnowledgeService public async Task> SearchKnowledge(string collectionName, KnowledgeRetrievalOptions options) { - var textEmbedding = GetTextEmbedding(); - var vector = await textEmbedding.GetVectorAsync(options.Text); - - // Vector search - var db = GetVectorDb(); - var fields = !options.Fields.IsNullOrEmpty() ? options.Fields : new List { KnowledgePayloadName.Text, KnowledgePayloadName.Answer }; - var found = await db.Search(collectionName, vector, fields, limit: options.Limit ?? 5, confidence: options.Confidence ?? 0.5f, withVector: options.WithVector); - - var results = found.Select(x => new KnowledgeRetrievalResult + try { - Data = x.Data, - Score = x.Score, - Vector = x.Vector - }).ToList(); - return results; + var textEmbedding = GetTextEmbedding(); + var vector = await textEmbedding.GetVectorAsync(options.Text); + + // Vector search + var db = GetVectorDb(); + var fields = !options.Fields.IsNullOrEmpty() ? options.Fields : new List { KnowledgePayloadName.Text, KnowledgePayloadName.Answer }; + var found = await db.Search(collectionName, vector, fields, limit: options.Limit ?? 5, confidence: options.Confidence ?? 0.5f, withVector: options.WithVector); + + var results = found.Select(x => new KnowledgeRetrievalResult + { + Data = x.Data, + Score = x.Score, + Vector = x.Vector + }).ToList(); + return results; + } + catch (Exception ex) + { + _logger.LogWarning($"Error when searching knowledge ({collectionName}). {ex.Message}\r\n{ex.InnerException}"); + return new List(); + } } } From 8b86ea50d0b23cdea814f9cd2af667caba836ac5 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 13 Aug 2024 16:36:12 -0500 Subject: [PATCH 04/13] add agent channel instructions --- .../Agents/IAgentService.cs | 7 ++ .../Agents/Models/Agent.cs | 38 +++++----- .../Agents/Models/ChannelInstruction.cs | 7 ++ .../Models/KnowledgeSearchResult.cs | 2 +- .../Services/AgentService.CreateAgent.cs | 62 +++++++++------- .../Agents/Services/AgentService.GetAgents.cs | 24 +++++- .../Agents/Services/AgentService.LoadAgent.cs | 56 ++++++++------ .../Services/AgentService.RefreshAgents.cs | 5 +- .../Services/AgentService.UpdateAgent.cs | 8 +- .../FileRepository/FileRepository.Agent.cs | 73 ++++++++----------- .../FileRepository/FileRepository.cs | 64 ++++++++++++++-- .../Controllers/AgentController.cs | 43 +++-------- .../ViewModels/Agents/AgentCreationModel.cs | 25 ++++--- .../ViewModels/Agents/AgentUpdateModel.cs | 17 +++-- .../ViewModels/Agents/AgentViewModel.cs | 2 + .../Collections/AgentDocument.cs | 1 + .../Models/ChannelInstructionMongoElement.cs | 27 +++++++ .../Repository/MongoRepository.Agent.cs | 25 +++++-- .../Repository/MongoRepository.Transaction.cs | 6 +- 19 files changed, 310 insertions(+), 182 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Agents/Models/ChannelInstruction.cs create mode 100644 src/Plugins/BotSharp.Plugin.MongoStorage/Models/ChannelInstructionMongoElement.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index 8405d9b5..9b65b148 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -20,6 +20,13 @@ public interface IAgentService /// Task LoadAgent(string id); + /// + /// Inherit from host agent + /// + /// + /// + Task InheritAgent(Agent agent); + string RenderedInstruction(Agent agent); string RenderedTemplate(Agent agent, string templateName); diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs index 01ec2acb..43f0f5ba 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs @@ -1,6 +1,5 @@ using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Plugins.Models; -using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Tasks.Models; namespace BotSharp.Abstraction.Agents.Models; @@ -21,8 +20,7 @@ public class Agent /// Default LLM settings /// [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public AgentLlmConfig LlmConfig { get; set; } - = new AgentLlmConfig(); + public AgentLlmConfig LlmConfig { get; set; } = new(); /// /// Instruction @@ -30,33 +28,35 @@ public class Agent [JsonIgnore] public string? Instruction { get; set; } + /// + /// Channel instructions + /// + [JsonIgnore] + public List ChannelInstructions { get; set; } = new(); + /// /// Templates /// [JsonIgnore] - public List Templates { get; set; } - = new List(); + public List Templates { get; set; } = new(); /// /// Agent tasks /// [JsonIgnore] - public List Tasks { get; set; } - = new List(); + public List Tasks { get; set; } = new(); /// /// Samples /// [JsonIgnore] - public List Samples { get; set; } - = new List(); + public List Samples { get; set; } = new(); /// /// Functions /// [JsonIgnore] - public List Functions { get; set; } - = new List(); + public List Functions { get; set; } = new(); /// /// Responses @@ -93,23 +93,20 @@ public class Agent /// /// Agent utilities /// - public List Utilities { get; set; } - = new List(); + public List Utilities { get; set; } = new(); /// /// Inherit from agent /// public string? InheritAgentId { get; set; } - public List RoutingRules { get; set; } - = new List(); + public List RoutingRules { get; set; } = new(); /// /// For rendering deferral /// [JsonIgnore] - public Dictionary TemplateDict { get; set; } - = new Dictionary(); + public Dictionary TemplateDict { get; set; } = new(); public override string ToString() => $"{Name} {Id}"; @@ -124,6 +121,7 @@ public class Agent Description = agent.Description, Type = agent.Type, Instruction = agent.Instruction, + ChannelInstructions = agent.ChannelInstructions, Functions = agent.Functions, Responses = agent.Responses, Samples = agent.Samples, @@ -145,6 +143,12 @@ public class Agent return this; } + public Agent SetChannelInstructions(List instructions) + { + ChannelInstructions = instructions ?? new List(); + return this; + } + public Agent SetTemplates(List templates) { Templates = templates ?? new List(); diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/ChannelInstruction.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/ChannelInstruction.cs new file mode 100644 index 00000000..d4ff6c1f --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/ChannelInstruction.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Agents.Models; + +public class ChannelInstruction +{ + public string Channel { get; set; } + public string Instruction { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchResult.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchResult.cs index b0deaa0f..2d5d9c1f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchResult.cs @@ -2,7 +2,7 @@ namespace BotSharp.Abstraction.Knowledges.Models; public class KnowledgeSearchResult { - public IDictionary Data { get; set; } = new Dictionary(); + public Dictionary Data { get; set; } = new(); public double Score { get; set; } public float[]? Vector { get; set; } } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index 59de3096..5fb6318f 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -41,41 +41,49 @@ public partial class AgentService }); Utilities.ClearCache(); - return await Task.FromResult(agentRecord); } - private Agent FetchAgentFileByName(string agentName, string filePath) + private (string, List) FetchInstructionsFromFile(string fileDir) { - foreach (var dir in Directory.GetDirectories(filePath)) + var defaultInstruction = string.Empty; + var channelInstructions = new List(); + + var instructionDir = Path.Combine(fileDir, "instructions"); + if (!Directory.Exists(instructionDir)) { - var agentJson = File.ReadAllText(Path.Combine(dir, "agent.json")); - var agent = JsonSerializer.Deserialize(agentJson, _options); - if (agent != null && agent.Name.IsEqualTo(agentName)) - { - var functions = FetchFunctionsFromFile(dir); - var instruction = FetchInstructionFromFile(dir); - var responses = FetchResponsesFromFile(dir); - var templates = FetchTemplatesFromFile(dir); - var samples = FetchSamplesFromFile(dir); - return agent.SetInstruction(instruction) - .SetTemplates(templates) - .SetFunctions(functions) - .SetResponses(responses) - .SetSamples(samples); - } + return (defaultInstruction, channelInstructions); } - return null; - } + foreach (var file in Directory.GetFiles(instructionDir)) + { + var extension = Path.GetExtension(file).Substring(1); + if (!extension.IsEqualTo(_agentSettings.TemplateFormat)) + { + continue; + } - private string FetchInstructionFromFile(string fileDir) - { - var file = Path.Combine(fileDir, $"instruction.{_agentSettings.TemplateFormat}"); - if (!File.Exists(file)) return null; + var segments = Path.GetFileName(file).Split(".", StringSplitOptions.RemoveEmptyEntries); + if (segments.IsNullOrEmpty() || !segments[0].IsEqualTo("instruction")) + { + continue; + } - var instruction = File.ReadAllText(file); - return instruction; + if (segments.Length == 2) + { + defaultInstruction = File.ReadAllText(file); + } + else if (segments.Length == 3) + { + var item = new ChannelInstruction + { + Channel = segments[1], + Instruction = File.ReadAllText(file) + }; + channelInstructions.Add(item); + } + } + return (defaultInstruction, channelInstructions); } private List FetchTemplatesFromFile(string fileDir) @@ -86,10 +94,10 @@ public partial class AgentService foreach (var file in Directory.GetFiles(templateDir)) { - var name = Path.GetFileNameWithoutExtension(file); var extension = Path.GetExtension(file).Substring(1); if (extension.IsEqualTo(_agentSettings.TemplateFormat)) { + var name = Path.GetFileNameWithoutExtension(file); var content = File.ReadAllText(file); templates.Add(new AgentTemplate(name, content)); } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs index 15ce6eca..04b5c22d 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs @@ -53,7 +53,29 @@ public partial class AgentService } profile.Plugin = GetPlugin(profile.Id); - return profile; } + + public async Task InheritAgent(Agent agent) + { + if (string.IsNullOrWhiteSpace(agent?.InheritAgentId)) return; + + var inheritedAgent = await GetAgent(agent.InheritAgentId); + agent.Templates.AddRange(inheritedAgent.Templates + // exclude private template + .Where(x => !x.Name.StartsWith(".")) + // exclude duplicate name + .Where(x => !agent.Templates.Exists(t => t.Name == x.Name))); + + agent.Functions.AddRange(inheritedAgent.Functions + // exclude private template + .Where(x => !x.Name.StartsWith(".")) + // exclude duplicate name + .Where(x => !agent.Functions.Exists(t => t.Name == x.Name))); + + if (string.IsNullOrWhiteSpace(agent.Instruction)) + { + agent.Instruction = inheritedAgent.Instruction; + } + } } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs index 757d9937..f0a864a2 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs @@ -34,31 +34,12 @@ public partial class AgentService return null; } - if (agent.InheritAgentId != null) - { - var inheritedAgent = await GetAgent(agent.InheritAgentId); - agent.Templates.AddRange(inheritedAgent.Templates - // exclude private template - .Where(x => !x.Name.StartsWith(".")) - // exclude duplicate name - .Where(x => !agent.Templates.Exists(t => t.Name == x.Name))); - - agent.Functions.AddRange(inheritedAgent.Functions - // exclude private template - .Where(x => !x.Name.StartsWith(".")) - // exclude duplicate name - .Where(x => !agent.Functions.Exists(t => t.Name == x.Name))); - - if (agent.Instruction == null) - { - agent.Instruction = inheritedAgent.Instruction; - } - } + await InheritAgent(agent); + OverrideInstructionByChannel(agent); AddOrUpdateParameters(agent); - agent.TemplateDict = new Dictionary(); - // Populate state into dictionary + agent.TemplateDict = new Dictionary(); PopulateState(agent.TemplateDict); // After agent is loaded @@ -94,6 +75,23 @@ public partial class AgentService return agent; } + private void OverrideInstructionByChannel(Agent agent) + { + var instructions = agent.ChannelInstructions; + if (instructions.IsNullOrEmpty()) return; + + var state = _services.GetRequiredService(); + var channel = state.GetState("channel"); + + if (string.IsNullOrWhiteSpace(channel)) + { + return; + } + + var found = instructions.FirstOrDefault(x => x.Channel.IsEqualTo(channel)); + agent.Instruction = !string.IsNullOrWhiteSpace(found?.Instruction) ? found.Instruction : agent.Instruction; + } + private void PopulateState(Dictionary dict) { var conv = _services.GetRequiredService(); @@ -114,18 +112,27 @@ public partial class AgentService private void AddOrUpdateRoutesParameters(string agentId, List routingRules) { - if(!AgentParameterTypes.TryGetValue(agentId, out var parameterTypes)) parameterTypes = new(); + if(!AgentParameterTypes.TryGetValue(agentId, out var parameterTypes)) + { + parameterTypes = new(); + } + foreach (var rule in routingRules.Where(x => x.Required)) { if (string.IsNullOrEmpty(rule.FieldType)) continue; parameterTypes.TryAdd(rule.Field, rule.FieldType); } + AgentParameterTypes.TryAdd(agentId, parameterTypes); } private void AddOrUpdateFunctionsParameters(string agentId, List functions) { - if (!AgentParameterTypes.TryGetValue(agentId, out var parameterTypes)) parameterTypes = new(); + if (!AgentParameterTypes.TryGetValue(agentId, out var parameterTypes)) + { + parameterTypes = new(); + } + var parameters = functions.Select(p => p.Parameters); foreach (var param in parameters) { @@ -139,6 +146,7 @@ public partial class AgentService } } } + AgentParameterTypes.TryAdd(agentId, parameterTypes); } } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs index 0b61977e..998d64c8 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs @@ -42,12 +42,13 @@ public partial class AgentService continue; } + var (defaultInstruction, channelInstructions) = FetchInstructionsFromFile(dir); var functions = FetchFunctionsFromFile(dir); - var instruction = FetchInstructionFromFile(dir); var responses = FetchResponsesFromFile(dir); var templates = FetchTemplatesFromFile(dir); var samples = FetchSamplesFromFile(dir); - agent.SetInstruction(instruction) + agent.SetInstruction(defaultInstruction) + .SetChannelInstructions(channelInstructions) .SetTemplates(templates) .SetFunctions(functions) .SetResponses(responses) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index 6fd0f85e..cef56c02 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -28,6 +28,7 @@ public partial class AgentService record.Profiles = agent.Profiles ?? new List(); record.RoutingRules = agent.RoutingRules ?? new List(); record.Instruction = agent.Instruction ?? string.Empty; + record.ChannelInstructions = agent.ChannelInstructions ?? new List(); record.Functions = agent.Functions ?? new List(); record.Templates = agent.Templates ?? new List(); record.Responses = agent.Responses ?? new List(); @@ -41,7 +42,6 @@ public partial class AgentService _db.UpdateAgent(record, updateField); Utilities.ClearCache(); - await Task.CompletedTask; } @@ -90,6 +90,7 @@ public partial class AgentService .SetProfiles(foundAgent.Profiles) .SetRoutingRules(foundAgent.RoutingRules) .SetInstruction(foundAgent.Instruction) + .SetChannelInstructions(foundAgent.ChannelInstructions) .SetTemplates(foundAgent.Templates) .SetFunctions(foundAgent.Functions) .SetResponses(foundAgent.Responses) @@ -175,12 +176,13 @@ public partial class AgentService var agent = JsonSerializer.Deserialize(agentJson, _options); if (agent != null && agent.Id == agentId) { + var (defaultInstruction, channelInstructions) = FetchInstructionsFromFile(dir); var functions = FetchFunctionsFromFile(dir); - var instruction = FetchInstructionFromFile(dir); var responses = FetchResponsesFromFile(dir); var templates = FetchTemplatesFromFile(dir); var samples = FetchSamplesFromFile(dir); - return agent.SetInstruction(instruction) + return agent.SetInstruction(defaultInstruction) + .SetChannelInstructions(channelInstructions) .SetTemplates(templates) .SetFunctions(functions) .SetResponses(responses) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index c0c59142..950a2b2d 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -1,6 +1,6 @@ +using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Routing.Models; using System.IO; -using System.Threading; namespace BotSharp.Core.Repository { @@ -37,7 +37,7 @@ namespace BotSharp.Core.Repository UpdateAgentRoutingRules(agent.Id, agent.RoutingRules); break; case AgentField.Instruction: - UpdateAgentInstruction(agent.Id, agent.Instruction); + UpdateAgentInstructions(agent.Id, agent.Instruction, agent.ChannelInstructions); break; case AgentField.Function: UpdateAgentFunctions(agent.Id, agent.Functions); @@ -175,17 +175,30 @@ namespace BotSharp.Core.Repository File.WriteAllText(agentFile, json); } - private void UpdateAgentInstruction(string agentId, string instruction) + private void UpdateAgentInstructions(string agentId, string instruction, List channelInstructions) { if (string.IsNullOrWhiteSpace(instruction)) return; var (agent, agentFile) = GetAgentFromFile(agentId); if (agent == null) return; - var instructionFile = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, - agentId, $"{AGENT_INSTRUCTION_FILE}.{_agentSettings.TemplateFormat}"); + var instructionDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_INSTRUCTIONS_FOLDER); + DeleteBeforeCreateDirectory(instructionDir); - File.WriteAllText(instructionFile, instruction); + // Save default instructions + var instructionFile = Path.Combine(instructionDir, $"{AGENT_INSTRUCTION_FILE}.{_agentSettings.TemplateFormat}"); + File.WriteAllText(instructionFile, instruction ?? string.Empty); + Thread.Sleep(100); + + // Save channel instructions + foreach (var ci in channelInstructions) + { + if (string.IsNullOrWhiteSpace(ci.Channel)) continue; + + var file = Path.Combine(instructionDir, $"{AGENT_INSTRUCTION_FILE}.{ci.Channel}.{_agentSettings.TemplateFormat}"); + File.WriteAllText(file, ci.Instruction ?? string.Empty); + Thread.Sleep(100); + } } private void UpdateAgentFunctions(string agentId, List inputFunctions) @@ -195,14 +208,8 @@ namespace BotSharp.Core.Repository var (agent, agentFile) = GetAgentFromFile(agentId); if (agent == null) return; - var functionDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, - agentId, AGENT_FUNCTIONS_FOLDER); - - if (Directory.Exists(functionDir)) - { - Directory.Delete(functionDir, true); - } - Directory.CreateDirectory(functionDir); + var functionDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_FUNCTIONS_FOLDER); + DeleteBeforeCreateDirectory(functionDir); foreach (var func in inputFunctions) { @@ -211,7 +218,7 @@ namespace BotSharp.Core.Repository var text = JsonSerializer.Serialize(func, _options); var file = Path.Combine(functionDir, $"{func.Name}.json"); File.WriteAllText(file, text); - Thread.Sleep(200); + Thread.Sleep(100); } } @@ -223,16 +230,7 @@ namespace BotSharp.Core.Repository if (agent == null) return; var templateDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_TEMPLATES_FOLDER); - - if (!Directory.Exists(templateDir)) - { - Directory.CreateDirectory(templateDir); - } - - foreach (var file in Directory.GetFiles(templateDir)) - { - File.Delete(file); - } + DeleteBeforeCreateDirectory(templateDir); foreach (var template in templates) { @@ -249,15 +247,7 @@ namespace BotSharp.Core.Repository if (agent == null) return; var responseDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_RESPONSES_FOLDER); - if (!Directory.Exists(responseDir)) - { - Directory.CreateDirectory(responseDir); - } - - foreach (var file in Directory.GetFiles(responseDir)) - { - File.Delete(file); - } + DeleteBeforeCreateDirectory(responseDir); for (int i = 0; i < responses.Count; i++) { @@ -308,7 +298,7 @@ namespace BotSharp.Core.Repository var json = JsonSerializer.Serialize(agent, _options); File.WriteAllText(agentFile, json); - UpdateAgentInstruction(inputAgent.Id, inputAgent.Instruction); + UpdateAgentInstructions(inputAgent.Id, inputAgent.Instruction, agent.ChannelInstructions); UpdateAgentResponses(inputAgent.Id, inputAgent.Responses); UpdateAgentTemplates(inputAgent.Id, inputAgent.Templates); UpdateAgentFunctions(inputAgent.Id, inputAgent.Functions); @@ -348,12 +338,13 @@ namespace BotSharp.Core.Repository var record = JsonSerializer.Deserialize(json, _options); if (record == null) return null; - var instruction = FetchInstruction(dir); + var (defaultInstruction, channelInstructions) = FetchInstructions(dir); var functions = FetchFunctions(dir); var samples = FetchSamples(dir); var templates = FetchTemplates(dir); var responses = FetchResponses(dir); - return record.SetInstruction(instruction) + return record.SetInstruction(defaultInstruction) + .SetChannelInstructions(channelInstructions) .SetFunctions(functions) .SetTemplates(templates) .SetSamples(samples) @@ -451,13 +442,9 @@ namespace BotSharp.Core.Repository return true; } - public void BulkInsertAgents(List agents) - { - } + public void BulkInsertAgents(List agents) { } - public void BulkInsertUserAgents(List userAgents) - { - } + public void BulkInsertUserAgents(List userAgents) { } public bool DeleteAgents() { diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index 78bd5cc0..7e5b4d9d 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -34,6 +34,7 @@ public partial class FileRepository : IBotSharpRepository private const string AGENT_TASK_PREFIX = "#metadata"; private const string AGENT_TASK_SUFFIX = "/metadata"; private const string TRANSLATION_MEMORY_FILE = "memory.json"; + private const string AGENT_INSTRUCTIONS_FOLDER = "instructions"; private const string AGENT_FUNCTIONS_FOLDER = "functions"; private const string AGENT_TEMPLATES_FOLDER = "templates"; private const string AGENT_RESPONSES_FOLDER = "responses"; @@ -123,7 +124,9 @@ public partial class FileRepository : IBotSharpRepository var agent = JsonSerializer.Deserialize(json, _options); if (agent != null) { - agent = agent.SetInstruction(FetchInstruction(d)) + var (defaultInstruction, channelInstructions) = FetchInstructions(d); + agent = agent.SetInstruction(defaultInstruction) + .SetChannelInstructions(channelInstructions) .SetFunctions(FetchFunctions(d)) .SetTemplates(FetchTemplates(d)) .SetResponses(FetchResponses(d)) @@ -165,6 +168,17 @@ public partial class FileRepository : IBotSharpRepository #region Private methods + private void DeleteBeforeCreateDirectory(string dir) + { + if (string.IsNullOrWhiteSpace(dir)) return; + + if (Directory.Exists(dir)) + { + Directory.Delete(dir, true); + } + Directory.CreateDirectory(dir); + } + private string GetAgentDataDir(string agentId) { var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId); @@ -186,13 +200,46 @@ public partial class FileRepository : IBotSharpRepository return (agent, agentFile); } - private string? FetchInstruction(string fileDir) + private (string, List) FetchInstructions(string fileDir) { - var file = Path.Combine(fileDir, $"{AGENT_INSTRUCTION_FILE}.{_agentSettings.TemplateFormat}"); - if (!File.Exists(file)) return null; + var defaultInstruction = string.Empty; + var channelInstructions = new List(); - var instruction = File.ReadAllText(file); - return instruction; + var instructionDir = Path.Combine(fileDir, AGENT_INSTRUCTIONS_FOLDER); + if (!Directory.Exists(instructionDir)) + { + return (defaultInstruction, channelInstructions); + } + + foreach (var file in Directory.GetFiles(instructionDir)) + { + var extension = Path.GetExtension(file).Substring(1); + if (!extension.IsEqualTo(_agentSettings.TemplateFormat)) + { + continue; + } + + var segments = Path.GetFileName(file).Split(".", StringSplitOptions.RemoveEmptyEntries); + if (segments.IsNullOrEmpty() || !segments[0].IsEqualTo(AGENT_INSTRUCTION_FILE)) + { + continue; + } + + if (segments.Length == 2) + { + defaultInstruction = File.ReadAllText(file); + } + else if (segments.Length == 3) + { + var item = new ChannelInstruction + { + Channel = segments[1], + Instruction = File.ReadAllText(file) + }; + channelInstructions.Add(item); + } + } + return (defaultInstruction, channelInstructions); } private List FetchFunctions(string fileDir) @@ -298,13 +345,14 @@ public partial class FileRepository : IBotSharpRepository var agent = JsonSerializer.Deserialize(agentJson, _options); if (agent == null) return null; - var instruction = FetchInstruction(agentDir); + var (defaultInstruction, channelInstructions) = FetchInstructions(agentDir); var functions = FetchFunctions(agentDir); var samples = FetchSamples(agentDir); var templates = FetchTemplates(agentDir); var responses = FetchResponses(agentDir); - return agent.SetInstruction(instruction) + return agent.SetInstruction(defaultInstruction) + .SetChannelInstructions(channelInstructions) .SetFunctions(functions) .SetTemplates(templates) .SetSamples(samples) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index f2bbc2eb..15c83c89 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Users.Enums; namespace BotSharp.OpenAPI.Controllers; @@ -28,13 +27,18 @@ public class AgentController : ControllerBase [HttpGet("/agent/{id}")] public async Task GetAgent([FromRoute] string id) { - var agents = await GetAgents(new AgentFilter + var pagedAgents = await _agentService.GetAgents(new AgentFilter { AgentIds = new List { id } - }, useHook: true); + }); - var targetAgent = agents.Items.FirstOrDefault(); - if (targetAgent == null) return null; + var foundAgent = pagedAgents.Items.FirstOrDefault(); + if (foundAgent == null) return null; + + await _agentService.InheritAgent(foundAgent); + var targetAgent = AgentViewModel.FromAgent(foundAgent); + var agentSetting = _services.GetRequiredService(); + targetAgent.IsHost = targetAgent.Id == agentSetting.HostAgentId; var redirectAgentIds = targetAgent.RoutingRules .Where(x => !string.IsNullOrEmpty(x.RedirectTo)) @@ -65,39 +69,16 @@ public class AgentController : ControllerBase } [HttpGet("/agents")] - public async Task> GetAgents([FromQuery] AgentFilter filter, [FromQuery] bool useHook = false) + public async Task> GetAgents([FromQuery] AgentFilter filter) { var agentSetting = _services.GetRequiredService(); var pagedAgents = await _agentService.GetAgents(filter); - - var items = new List(); - var agents = new List(); - if (useHook) - { - // prerender agent - foreach (var agent in pagedAgents.Items) - { - var renderedAgent = await _agentService.LoadAgent(agent.Id); - items.Add(renderedAgent); - } - - // Set IsHost - agents = items.Select(x => AgentViewModel.FromAgent(x)).ToList(); - foreach (var agent in agents) - { - agent.IsHost = agentSetting.HostAgentId == agent.Id; - } - } - else - { - items = pagedAgents.Items.ToList(); - agents = items.Select(x => AgentViewModel.FromAgent(x)).ToList(); - } + var agents = pagedAgents?.Items?.Select(x => AgentViewModel.FromAgent(x))?.ToList() ?? new List(); return new PagedItems { Items = agents, - Count = pagedAgents.Count + Count = pagedAgents?.Count ?? 0 }; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs index c0fa8320..78f75833 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs @@ -1,7 +1,6 @@ using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Routing.Models; -using BotSharp.Core.Infrastructures; namespace BotSharp.OpenAPI.ViewModels.Agents; @@ -16,21 +15,26 @@ public class AgentCreationModel /// public string Instruction { get; set; } = string.Empty; + /// + /// + /// + public List ChannelInstructions { get; set; } = new(); + /// /// LLM extensible Instructions in addition to the default Instructions /// - public List Templates { get; set; } = new List(); + public List Templates { get; set; } = new(); /// /// LLM callable function definition /// - public List Functions { get; set; } = new List(); + public List Functions { get; set; } = new(); /// /// Response template /// - public List Responses { get; set; } = new List(); - public List Samples { get; set; } = new List(); + public List Responses { get; set; } = new(); + public List Samples { get; set; } = new(); public bool IsPublic { get; set; } @@ -43,9 +47,9 @@ public class AgentCreationModel /// /// Combine different Agents together to form a Profile. /// - public List Profiles { get; set; } = new List(); - public List Utilities { get; set; } = new List(); - public List RoutingRules { get; set; } = new List(); + public List Profiles { get; set; } = new(); + public List Utilities { get; set; } = new(); + public List RoutingRules { get; set; } = new(); public AgentLlmConfig? LlmConfig { get; set; } public Agent ToAgent() @@ -55,6 +59,7 @@ public class AgentCreationModel Name = Name, Description = Description, Instruction = Instruction, + ChannelInstructions = ChannelInstructions, Templates = Templates, Functions = Functions, Responses = Responses, @@ -64,9 +69,7 @@ public class AgentCreationModel Type = Type, Disabled = Disabled, Profiles = Profiles, - RoutingRules = RoutingRules? - .Select(x => RoutingRuleUpdateModel.ToDomainElement(x))? - .ToList() ?? new List(), + RoutingRules = RoutingRules?.Select(x => RoutingRuleUpdateModel.ToDomainElement(x))?.ToList() ?? new List(), LlmConfig = LlmConfig }; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs index 96cc7278..d64530c6 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs @@ -15,6 +15,12 @@ public class AgentUpdateModel /// public string Instruction { get; set; } = string.Empty; + /// + /// Channel instructions + /// + [JsonPropertyName("channel_instructions")] + public List? ChannelInstructions { get; set; } + /// /// Templates /// @@ -39,11 +45,11 @@ public class AgentUpdateModel /// Routes /// public List? Responses { get; set; } + [JsonPropertyName("is_public")] - public bool IsPublic { get; set; } - [JsonPropertyName("allow_routing")] + [JsonPropertyName("allow_routing")] public bool AllowRouting { get; set; } public bool Disabled { get; set; } @@ -52,8 +58,8 @@ public class AgentUpdateModel /// Profile by channel /// public List? Profiles { get; set; } - [JsonPropertyName("routing_rules")] + [JsonPropertyName("routing_rules")] public List? RoutingRules { get; set; } [JsonPropertyName("llm_config")] @@ -69,10 +75,9 @@ public class AgentUpdateModel Disabled = Disabled, Type = Type, Profiles = Profiles ?? new List(), - RoutingRules = RoutingRules? - .Select(x => RoutingRuleUpdateModel.ToDomainElement(x))? - .ToList() ?? new List(), + RoutingRules = RoutingRules?.Select(x => RoutingRuleUpdateModel.ToDomainElement(x))?.ToList() ?? new List(), Instruction = Instruction ?? string.Empty, + ChannelInstructions = ChannelInstructions ?? new List(), Templates = Templates ?? new List(), Functions = Functions ?? new List(), Responses = Responses ?? new List(), diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index a88b3c72..93440075 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -13,6 +13,7 @@ public class AgentViewModel public string Description { get; set; } public string Type { get; set; } = AgentType.Task; public string Instruction { get; set; } + public List ChannelInstructions { get; set; } public List Templates { get; set; } public List Functions { get; set; } public List Responses { get; set; } @@ -60,6 +61,7 @@ public class AgentViewModel Description = agent.Description, Type = agent.Type, Instruction = agent.Instruction, + ChannelInstructions = agent.ChannelInstructions, Templates = agent.Templates, Functions = agent.Functions, Responses = agent.Responses, diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs index 4f1c5194..baa0729f 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentDocument.cs @@ -8,6 +8,7 @@ public class AgentDocument : MongoBase public string? InheritAgentId { get; set; } public string? IconUrl { get; set; } public string Instruction { get; set; } + public List ChannelInstructions { get; set; } public List Templates { get; set; } public List Functions { get; set; } public List Responses { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/ChannelInstructionMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/ChannelInstructionMongoElement.cs new file mode 100644 index 00000000..884c638c --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/ChannelInstructionMongoElement.cs @@ -0,0 +1,27 @@ +using BotSharp.Abstraction.Agents.Models; + +namespace BotSharp.Plugin.MongoStorage.Models; + +public class ChannelInstructionMongoElement +{ + public string Channel { get; set; } + public string Instruction { get; set; } + + public static ChannelInstructionMongoElement ToMongoElement(ChannelInstruction instruction) + { + return new ChannelInstructionMongoElement + { + Channel = instruction.Channel, + Instruction = instruction.Instruction + }; + } + + public static ChannelInstruction ToDomainElement(ChannelInstructionMongoElement instruction) + { + return new ChannelInstruction + { + Channel = instruction.Channel, + Instruction = instruction.Instruction + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 60dcd6c3..49c8616e 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -38,7 +38,7 @@ public partial class MongoRepository UpdateAgentRoutingRules(agent.Id, agent.RoutingRules); break; case AgentField.Instruction: - UpdateAgentInstruction(agent.Id, agent.Instruction); + UpdateAgentInstructions(agent.Id, agent.Instruction, agent.ChannelInstructions); break; case AgentField.Function: UpdateAgentFunctions(agent.Id, agent.Functions); @@ -156,13 +156,17 @@ public partial class MongoRepository _dc.Agents.UpdateOne(filter, update); } - private void UpdateAgentInstruction(string agentId, string instruction) + private void UpdateAgentInstructions(string agentId, string instruction, List? channelInstructions) { - if (string.IsNullOrWhiteSpace(instruction)) return; + if (string.IsNullOrWhiteSpace(agentId)) return; + + var instructionElements = channelInstructions?.Select(x => ChannelInstructionMongoElement.ToMongoElement(x))? + .ToList() ?? new List(); var filter = Builders.Filter.Eq(x => x.Id, agentId); var update = Builders.Update .Set(x => x.Instruction, instruction) + .Set(x => x.ChannelInstructions, instructionElements) .Set(x => x.UpdatedTime, DateTime.UtcNow); _dc.Agents.UpdateOne(filter, update); @@ -253,6 +257,7 @@ public partial class MongoRepository .Set(x => x.Profiles, agent.Profiles) .Set(x => x.RoutingRules, agent.RoutingRules.Select(r => RoutingRuleMongoElement.ToMongoElement(r)).ToList()) .Set(x => x.Instruction, agent.Instruction) + .Set(x => x.ChannelInstructions, agent.ChannelInstructions.Select(i => ChannelInstructionMongoElement.ToMongoElement(i)).ToList()) .Set(x => x.Templates, agent.Templates.Select(t => AgentTemplateMongoElement.ToMongoElement(t)).ToList()) .Set(x => x.Functions, agent.Functions.Select(f => FunctionDefMongoElement.ToMongoElement(f)).ToList()) .Set(x => x.Responses, agent.Responses.Select(r => AgentResponseMongoElement.ToMongoElement(r)).ToList()) @@ -373,6 +378,9 @@ public partial class MongoRepository IconUrl = x.IconUrl, Description = x.Description, Instruction = x.Instruction, + ChannelInstructions = x.ChannelInstructions? + .Select(i => ChannelInstructionMongoElement.ToMongoElement(i))? + .ToList() ?? new List(), Templates = x.Templates? .Select(t => AgentTemplateMongoElement.ToMongoElement(t))? .ToList() ?? new List(), @@ -463,6 +471,9 @@ public partial class MongoRepository IconUrl = agentDoc.IconUrl, Description = agentDoc.Description, Instruction = agentDoc.Instruction, + ChannelInstructions = !agentDoc.ChannelInstructions.IsNullOrEmpty() ? agentDoc.ChannelInstructions + .Select(i => ChannelInstructionMongoElement.ToDomainElement(i)) + .ToList() : new List(), Templates = !agentDoc.Templates.IsNullOrEmpty() ? agentDoc.Templates .Select(t => AgentTemplateMongoElement.ToDomainElement(t)) .ToList() : new List(), @@ -472,6 +483,10 @@ public partial class MongoRepository Responses = !agentDoc.Responses.IsNullOrEmpty() ? agentDoc.Responses .Select(r => AgentResponseMongoElement.ToDomainElement(r)) .ToList() : new List(), + RoutingRules = !agentDoc.RoutingRules.IsNullOrEmpty() ? agentDoc.RoutingRules + .Select(r => RoutingRuleMongoElement.ToDomainElement(agentDoc.Id, agentDoc.Name, r)) + .ToList() : new List(), + LlmConfig = AgentLlmConfigMongoElement.ToDomainElement(agentDoc.LlmConfig), Samples = agentDoc.Samples ?? new List(), Utilities = agentDoc.Utilities ?? new List(), IsPublic = agentDoc.IsPublic, @@ -479,10 +494,6 @@ public partial class MongoRepository Type = agentDoc.Type, InheritAgentId = agentDoc.InheritAgentId, Profiles = agentDoc.Profiles, - RoutingRules = !agentDoc.RoutingRules.IsNullOrEmpty() ? agentDoc.RoutingRules - .Select(r => RoutingRuleMongoElement.ToDomainElement(agentDoc.Id, agentDoc.Name, r)) - .ToList() : new List(), - LlmConfig = AgentLlmConfigMongoElement.ToDomainElement(agentDoc.LlmConfig) }; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Transaction.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Transaction.cs index 4a4542ed..e2ffbb0e 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Transaction.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Transaction.cs @@ -37,9 +37,12 @@ public partial class MongoRepository { Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(), Name = x.Name, + IconUrl = x.IconUrl, Description = x.Description, Instruction = x.Instruction, - IconUrl = x.IconUrl, + ChannelInstructions = x.ChannelInstructions? + .Select(i => ChannelInstructionMongoElement.ToMongoElement(i))? + .ToList() ?? new List(), Templates = x.Templates? .Select(t => AgentTemplateMongoElement.ToMongoElement(t))? .ToList() ?? new List(), @@ -71,6 +74,7 @@ public partial class MongoRepository .Set(x => x.Name, agent.Name) .Set(x => x.Description, agent.Description) .Set(x => x.Instruction, agent.Instruction) + .Set(x => x.ChannelInstructions, agent.ChannelInstructions) .Set(x => x.Templates, agent.Templates) .Set(x => x.Functions, agent.Functions) .Set(x => x.Responses, agent.Responses) From 16795a13fd456b397b81cbc8575aabd4c0e2497f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 13 Aug 2024 16:54:32 -0500 Subject: [PATCH 05/13] relocate instructions --- .../BotSharp.Core/BotSharp.Core.csproj | 26 +++++++++---------- .../{ => instructions}/instruction.liquid | 0 .../{ => instructions}/instruction.liquid | 0 .../{ => instructions}/instruction.liquid | 0 .../{ => instructions}/instruction.liquid | 0 .../{ => instructions}/instruction.liquid | 0 .../{ => instructions}/instruction.liquid | 0 .../BotSharp.Plugin.HttpHandler.csproj | 4 +-- .../{ => instructions}/instruction.liquid | 0 .../BotSharp.Plugin.KnowledgeBase.csproj | 4 +-- .../{ => instructions}/instruction.liquid | 0 .../BotSharp.Plugin.SqlDriver.csproj | 4 +-- .../{ => instructions}/instruction.liquid | 0 .../BotSharp.Plugin.WebDriver.csproj | 4 +-- .../{ => instructions}/instruction.liquid | 0 15 files changed, 21 insertions(+), 21 deletions(-) rename src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/{ => instructions}/instruction.liquid (100%) rename src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/{ => instructions}/instruction.liquid (100%) rename src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/{ => instructions}/instruction.liquid (100%) rename src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/{ => instructions}/instruction.liquid (100%) rename src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/{ => instructions}/instruction.liquid (100%) rename src/Infrastructure/BotSharp.Core/data/agents/dfd9b46d-d00c-40af-8a75-3fbdc2b89869/{ => instructions}/instruction.liquid (100%) rename src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/87c458fc-ec5f-40ae-8ed6-05dda8a07523/{ => instructions}/instruction.liquid (100%) rename src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/{ => instructions}/instruction.liquid (100%) rename src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/{ => instructions}/instruction.liquid (100%) rename src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/{ => instructions}/instruction.liquid (100%) diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index ad46fb56..73bf035c 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) @@ -56,17 +56,17 @@ - + - + - + - + - + @@ -80,7 +80,7 @@ - + @@ -90,7 +90,7 @@ PreserveNewest - + PreserveNewest @@ -99,19 +99,19 @@ PreserveNewest - + PreserveNewest PreserveNewest - + PreserveNewest PreserveNewest - + PreserveNewest @@ -147,7 +147,7 @@ PreserveNewest - + PreserveNewest @@ -162,7 +162,7 @@ PreserveNewest - + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instructions/instruction.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instruction.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b/instructions/instruction.liquid diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/instructions/instruction.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/instruction.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/instructions/instruction.liquid diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/instructions/instruction.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/instruction.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-0af7-49e6-ad7a-a760bd12dc4d/instructions/instruction.liquid diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instruction.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/instructions/instruction.liquid diff --git a/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/instructions/instruction.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/instruction.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/instructions/instruction.liquid diff --git a/src/Infrastructure/BotSharp.Core/data/agents/dfd9b46d-d00c-40af-8a75-3fbdc2b89869/instruction.liquid b/src/Infrastructure/BotSharp.Core/data/agents/dfd9b46d-d00c-40af-8a75-3fbdc2b89869/instructions/instruction.liquid similarity index 100% rename from src/Infrastructure/BotSharp.Core/data/agents/dfd9b46d-d00c-40af-8a75-3fbdc2b89869/instruction.liquid rename to src/Infrastructure/BotSharp.Core/data/agents/dfd9b46d-d00c-40af-8a75-3fbdc2b89869/instructions/instruction.liquid diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj b/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj index 2911dc2b..8664c654 100644 --- a/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj +++ b/src/Plugins/BotSharp.Plugin.HttpHandler/BotSharp.Plugin.HttpHandler.csproj @@ -15,14 +15,14 @@ - + PreserveNewest - + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/87c458fc-ec5f-40ae-8ed6-05dda8a07523/instruction.liquid b/src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/87c458fc-ec5f-40ae-8ed6-05dda8a07523/instructions/instruction.liquid similarity index 100% rename from src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/87c458fc-ec5f-40ae-8ed6-05dda8a07523/instruction.liquid rename to src/Plugins/BotSharp.Plugin.HttpHandler/data/agents/87c458fc-ec5f-40ae-8ed6-05dda8a07523/instructions/instruction.liquid diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj b/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj index ecf15fe2..42663445 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/BotSharp.Plugin.KnowledgeBase.csproj @@ -20,7 +20,7 @@ - + @@ -34,7 +34,7 @@ PreserveNewest - + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/instruction.liquid b/src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/instructions/instruction.liquid similarity index 100% rename from src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/instruction.liquid rename to src/Plugins/BotSharp.Plugin.KnowledgeBase/data/agents/01acc3e5-0af7-49e6-ad7a-a760bd12dc40/instructions/instruction.liquid diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj index 8d861d68..6d2b4b05 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj @@ -18,7 +18,7 @@ - + @@ -26,7 +26,7 @@ PreserveNewest - + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/instruction.liquid b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/instructions/instruction.liquid similarity index 100% rename from src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/instruction.liquid rename to src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/instructions/instruction.liquid diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj index ce32616a..d6074b19 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.WebDriver/BotSharp.Plugin.WebDriver.csproj @@ -40,7 +40,7 @@ - + @@ -50,7 +50,7 @@ PreserveNewest - + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instruction.liquid b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instructions/instruction.liquid similarity index 100% rename from src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instruction.liquid rename to src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instructions/instruction.liquid From 281ace86585cd059a36a68a0b27090bd1e509dd0 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 13 Aug 2024 17:08:30 -0500 Subject: [PATCH 06/13] add json display name --- .../BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index 93440075..5c23c4d4 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -13,6 +13,8 @@ public class AgentViewModel public string Description { get; set; } public string Type { get; set; } = AgentType.Task; public string Instruction { get; set; } + + [JsonPropertyName("channel_instructions")] public List ChannelInstructions { get; set; } public List Templates { get; set; } public List Functions { get; set; } From 81f2b32761c81f2597092f7c961ca5435a20243f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 13 Aug 2024 17:16:55 -0500 Subject: [PATCH 07/13] rename --- .../Agents/Services/AgentService.CreateAgent.cs | 12 ++++++------ .../Agents/Services/AgentService.RefreshAgents.cs | 12 ++++++------ .../Agents/Services/AgentService.UpdateAgent.cs | 14 +++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index 5fb6318f..9e2e1d5f 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -44,7 +44,7 @@ public partial class AgentService return await Task.FromResult(agentRecord); } - private (string, List) FetchInstructionsFromFile(string fileDir) + private (string, List) GetInstructionsFromFile(string fileDir) { var defaultInstruction = string.Empty; var channelInstructions = new List(); @@ -86,7 +86,7 @@ public partial class AgentService return (defaultInstruction, channelInstructions); } - private List FetchTemplatesFromFile(string fileDir) + private List GetTemplatesFromFile(string fileDir) { var templates = new List(); var templateDir = Path.Combine(fileDir, "templates"); @@ -106,7 +106,7 @@ public partial class AgentService return templates; } - private List FetchFunctionsFromFile(string fileDir) + private List GetFunctionsFromFile(string fileDir) { var functions = new List(); var functionDir = Path.Combine(fileDir, "functions"); @@ -133,7 +133,7 @@ public partial class AgentService return functions; } - private List FetchResponsesFromFile(string fileDir) + private List GetResponsesFromFile(string fileDir) { var responses = new List(); var responseDir = Path.Combine(fileDir, "responses"); @@ -151,7 +151,7 @@ public partial class AgentService return responses; } - private List FetchSamplesFromFile(string fileDir) + private List GetSamplesFromFile(string fileDir) { var file = Path.Combine(fileDir, "samples.txt"); if (!File.Exists(file)) return new List(); @@ -160,7 +160,7 @@ public partial class AgentService return samples?.ToList() ?? new List(); } - private List FetchTasksFromFile(string fileDir) + private List GetTasksFromFile(string fileDir) { var tasks = new List(); var taskDir = Path.Combine(fileDir, "tasks"); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs index 998d64c8..10598d4e 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs @@ -42,11 +42,11 @@ public partial class AgentService continue; } - var (defaultInstruction, channelInstructions) = FetchInstructionsFromFile(dir); - var functions = FetchFunctionsFromFile(dir); - var responses = FetchResponsesFromFile(dir); - var templates = FetchTemplatesFromFile(dir); - var samples = FetchSamplesFromFile(dir); + var (defaultInstruction, channelInstructions) = GetInstructionsFromFile(dir); + var functions = GetFunctionsFromFile(dir); + var responses = GetResponsesFromFile(dir); + var templates = GetTemplatesFromFile(dir); + var samples = GetSamplesFromFile(dir); agent.SetInstruction(defaultInstruction) .SetChannelInstructions(channelInstructions) .SetTemplates(templates) @@ -55,7 +55,7 @@ public partial class AgentService .SetSamples(samples); var userAgent = BuildUserAgent(agent.Id, user.Id); - var tasks = FetchTasksFromFile(dir); + var tasks = GetTasksFromFile(dir); var isAgentDeleted = _db.DeleteAgent(agent.Id); if (isAgentDeleted) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index cef56c02..fe532e28 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -71,7 +71,7 @@ public partial class AgentService agentSettings.DataDir); var clonedAgent = Agent.Clone(agent); - var foundAgent = FetchAgentFileById(agent.Id, filePath); + var foundAgent = GetAgentFileById(agent.Id, filePath); if (foundAgent == null) { updateResult = $"Cannot find agent {agent.Name} in file directory: {filePath}"; @@ -166,7 +166,7 @@ public partial class AgentService return patchResult; } - private Agent? FetchAgentFileById(string agentId, string filePath) + private Agent? GetAgentFileById(string agentId, string filePath) { if (!Directory.Exists(filePath)) return null; @@ -176,11 +176,11 @@ public partial class AgentService var agent = JsonSerializer.Deserialize(agentJson, _options); if (agent != null && agent.Id == agentId) { - var (defaultInstruction, channelInstructions) = FetchInstructionsFromFile(dir); - var functions = FetchFunctionsFromFile(dir); - var responses = FetchResponsesFromFile(dir); - var templates = FetchTemplatesFromFile(dir); - var samples = FetchSamplesFromFile(dir); + var (defaultInstruction, channelInstructions) = GetInstructionsFromFile(dir); + var functions = GetFunctionsFromFile(dir); + var responses = GetResponsesFromFile(dir); + var templates = GetTemplatesFromFile(dir); + var samples = GetSamplesFromFile(dir); return agent.SetInstruction(defaultInstruction) .SetChannelInstructions(channelInstructions) .SetTemplates(templates) From d631c3cf8f615506beab4a5c94e08fffaa3937fb Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 13 Aug 2024 17:37:47 -0500 Subject: [PATCH 08/13] add channels in function def --- .../Functions/Models/FunctionDef.cs | 5 +++++ .../Agents/Services/AgentService.Rendering.cs | 21 ++++++++++++++++--- .../Controllers/AgentController.cs | 4 +++- .../Models/FunctionDefMongoElement.cs | 19 ++++++++++------- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs index 4366d323..d5dabb3c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Functions/Models/FunctionDef.cs @@ -8,7 +8,12 @@ public class FunctionDef [JsonPropertyName("description")] public string Description { get; set; } = null!; + [JsonPropertyName("channels")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public List? Channels { get; set; } + [JsonPropertyName("visibility_expression")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? VisibilityExpression { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs index a0b2f130..e9617d17 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Rendering.cs @@ -20,17 +20,32 @@ public partial class AgentService public bool RenderFunction(Agent agent, FunctionDef def) { - if (!string.IsNullOrEmpty(def.VisibilityExpression)) + var isRender = true; + + var channels = def.Channels; + if (channels != null) + { + var state = _services.GetRequiredService(); + var channel = state.GetState("channel"); + if (!string.IsNullOrWhiteSpace(channel)) + { + isRender = isRender && channels.Contains(channel); + } + } + + if (!isRender) return false; + + if (!string.IsNullOrWhiteSpace(def.VisibilityExpression)) { var render = _services.GetRequiredService(); var result = render.Render(def.VisibilityExpression, new Dictionary { { "states", agent.TemplateDict } }); - return result == "visible"; + isRender = isRender && result == "visible"; } - return true; + return isRender; } public FunctionParametersDef? RenderFunctionProperty(Agent agent, FunctionDef def) diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index 15c83c89..f033e31c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -42,7 +42,9 @@ public class AgentController : ControllerBase var redirectAgentIds = targetAgent.RoutingRules .Where(x => !string.IsNullOrEmpty(x.RedirectTo)) - .Select(x => x.RedirectTo).ToList(); + .Select(x => x.RedirectTo) + .ToList(); + var redirectAgents = await _agentService.GetAgents(new AgentFilter { AgentIds = redirectAgentIds diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs index dac77d69..6f72c517 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/FunctionDefMongoElement.cs @@ -8,6 +8,7 @@ public class FunctionDefMongoElement { public string Name { get; set; } public string Description { get; set; } + public List? Channels { get; set; } public string? VisibilityExpression { get; set; } public string? Impact { get; set; } public FunctionParametersDefMongoElement Parameters { get; set; } = new FunctionParametersDefMongoElement(); @@ -23,6 +24,7 @@ public class FunctionDefMongoElement { Name = function.Name, Description = function.Description, + Channels = function.Channels, VisibilityExpression = function.VisibilityExpression, Impact = function.Impact, Parameters = new FunctionParametersDefMongoElement @@ -34,19 +36,20 @@ public class FunctionDefMongoElement }; } - public static FunctionDef ToDomainElement(FunctionDefMongoElement mongoFunction) + public static FunctionDef ToDomainElement(FunctionDefMongoElement function) { return new FunctionDef { - Name = mongoFunction.Name, - Description = mongoFunction.Description, - VisibilityExpression = mongoFunction.VisibilityExpression, - Impact = mongoFunction.Impact, + Name = function.Name, + Description = function.Description, + Channels = function.Channels, + VisibilityExpression = function.VisibilityExpression, + Impact = function.Impact, Parameters = new FunctionParametersDef { - Type = mongoFunction.Parameters.Type, - Properties = JsonSerializer.Deserialize(mongoFunction.Parameters.Properties.IfNullOrEmptyAs("{}")), - Required = mongoFunction.Parameters.Required, + Type = function.Parameters.Type, + Properties = JsonSerializer.Deserialize(function.Parameters.Properties.IfNullOrEmptyAs("{}")), + Required = function.Parameters.Required, } }; } From 4cc3ae68d359ec27f064ae9f5d8a1dc5184275de Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 14 Aug 2024 10:45:22 -0500 Subject: [PATCH 09/13] change comment --- src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index 9b65b148..1fd7a38c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -21,7 +21,7 @@ public interface IAgentService Task LoadAgent(string id); /// - /// Inherit from host agent + /// Inherit from an agent /// /// /// From c29d2ac5518be7dea5f494ab4c8e663555555a25 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 14 Aug 2024 11:03:58 -0500 Subject: [PATCH 10/13] relocate instructions --- .../BotSharp.Plugin.PizzaBot.csproj | 12 ++++++------ .../{ => instructions}/instruction.liquid | 0 .../{ => instructions}/instruction.liquid | 0 .../{ => instructions}/instruction.liquid | 0 4 files changed, 6 insertions(+), 6 deletions(-) rename tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/{ => instructions}/instruction.liquid (100%) rename tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/{ => instructions}/instruction.liquid (100%) rename tests/BotSharp.Plugin.PizzaBot/data/agents/fe8c60aa-b114-4ef3-93cb-a8efeac80f75/{ => instructions}/instruction.liquid (100%) diff --git a/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj b/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj index e448b9e0..716f1f4d 100644 --- a/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj +++ b/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj @@ -24,16 +24,16 @@ - + - + - + @@ -50,13 +50,13 @@ PreserveNewest - + PreserveNewest PreserveNewest - + PreserveNewest @@ -65,7 +65,7 @@ PreserveNewest - + PreserveNewest diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/instruction.liquid b/tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/instructions/instruction.liquid similarity index 100% rename from tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/instruction.liquid rename to tests/BotSharp.Plugin.PizzaBot/data/agents/b284db86-e9c2-4c25-a59e-4649797dd130/instructions/instruction.liquid diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/instruction.liquid b/tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/instructions/instruction.liquid similarity index 100% rename from tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/instruction.liquid rename to tests/BotSharp.Plugin.PizzaBot/data/agents/c2b57a74-ae4e-4c81-b3ad-9ac5bff982bd/instructions/instruction.liquid diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/fe8c60aa-b114-4ef3-93cb-a8efeac80f75/instruction.liquid b/tests/BotSharp.Plugin.PizzaBot/data/agents/fe8c60aa-b114-4ef3-93cb-a8efeac80f75/instructions/instruction.liquid similarity index 100% rename from tests/BotSharp.Plugin.PizzaBot/data/agents/fe8c60aa-b114-4ef3-93cb-a8efeac80f75/instruction.liquid rename to tests/BotSharp.Plugin.PizzaBot/data/agents/fe8c60aa-b114-4ef3-93cb-a8efeac80f75/instructions/instruction.liquid From 694fcbfcaeb0e7130462307dbbcf81775091c502 Mon Sep 17 00:00:00 2001 From: Bo Yin <103488@smsassist.com> Date: Wed, 14 Aug 2024 16:44:47 -0500 Subject: [PATCH 11/13] update twilioPlugin --- .../Storage/LocalFileStorageService.Audio.cs | 8 +- .../Controllers/TwilioVoiceController.cs | 127 ++++++++---------- .../Models/AssistantMessage.cs | 8 ++ .../Models/CallerMessage.cs | 5 +- .../Services/ITwilioSessionManager.cs | 11 +- .../Services/TwilioMessageQueueService.cs | 42 ++++-- .../Services/TwilioService.cs | 6 +- .../Services/TwilioSessionManager.cs | 37 +++-- .../BotSharp.Plugin.Twilio/TwilioPlugin.cs | 1 - 9 files changed, 138 insertions(+), 107 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Audio.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Audio.cs index 9788631e..e90c7812 100644 --- a/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Audio.cs +++ b/src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Audio.cs @@ -6,19 +6,21 @@ namespace BotSharp.Core.Files.Services { public async Task SaveSpeechFileAsync(string conversationId, string fileName, BinaryData data) { - var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, TEXT_TO_SPEECH_FOLDER, conversationId); + var dir = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, TEXT_TO_SPEECH_FOLDER); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } - using var file = File.Create(Path.Combine(dir, fileName)); + var filePath = Path.Combine(dir, fileName); + if (File.Exists(filePath)) return; + using var file = File.Create(filePath); using var input = data.ToStream(); await input.CopyToAsync(file); } public async Task RetrieveSpeechFileAsync(string conversationId, string fileName) { - var path = Path.Combine(_baseDir, CONVERSATION_FOLDER, TEXT_TO_SPEECH_FOLDER, conversationId, fileName); + var path = Path.Combine(_baseDir, CONVERSATION_FOLDER, conversationId, TEXT_TO_SPEECH_FOLDER, fileName); using var file = new FileStream(path, FileMode.Open, FileAccess.Read); return await BinaryData.FromStreamAsync(file); } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs index 6e163ce8..859e34ff 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Controllers/TwilioVoiceController.cs @@ -1,5 +1,4 @@ using BotSharp.Abstraction.Files; -using BotSharp.Abstraction.Routing; using BotSharp.Core.Infrastructures; using BotSharp.Plugin.Twilio.Models; using BotSharp.Plugin.Twilio.Services; @@ -10,7 +9,7 @@ using System.IdentityModel.Tokens.Jwt; namespace BotSharp.Plugin.Twilio.Controllers; [AllowAnonymous] -[Route("[controller]")] +[Route("twilio/voice")] public class TwilioVoiceController : TwilioController { private readonly TwilioSetting _settings; @@ -38,73 +37,25 @@ public class TwilioVoiceController : TwilioController }; } - [HttpPost("/twilio/voice/welcome")] - public async Task StartConversation(VoiceRequest request) - { - string sessionId = $"TwilioVoice_{request.CallSid}"; - var twilio = _services.GetRequiredService(); - var response = twilio.ReturnInstructions("Hello, how may I help you?"); - return TwiML(response); - } - - [HttpPost("/twilio/voice/{agentId}")] - public async Task ReceivedVoiceMessage([FromRoute] string agentId, VoiceRequest input) - { - string sessionId = $"TwilioVoice_{input.CallSid}"; - - var inputMsg = new RoleDialogModel(AgentRole.User, input.SpeechResult); - var conv = _services.GetRequiredService(); - var routing = _services.GetRequiredService(); - routing.Context.SetMessageId(sessionId, inputMsg.MessageId); - - conv.SetConversationId(sessionId, new List - { - new MessageState("channel", ConversationChannel.Phone), - new MessageState("calling_phone", input.DialCallSid) - }); - - var twilio = _services.GetRequiredService(); - VoiceResponse response = default; - - var result = await conv.SendMessage(agentId, - inputMsg, - replyMessage: null, - async msg => - { - response = twilio.ReturnInstructions(msg.Content); - if (msg.FunctionName == "conversation_end") - { - response = twilio.HangUp(msg.Content); - } - }, async functionExecuting => - { - }, async functionExecuted => - { - }); - - return TwiML(response); - } - - - [HttpPost("start")] - public TwiMLResult InitiateConversation(VoiceRequest request) + [HttpPost("welcome")] + public TwiMLResult InitiateConversation(VoiceRequest request, [FromQuery] string states) { if (request?.CallSid == null) throw new ArgumentNullException(nameof(VoiceRequest.CallSid)); - string sessionId = $"TwilioVoice_{request.CallSid}"; + string conversationId = $"TwilioVoice_{request.CallSid}"; var twilio = _services.GetRequiredService(); - var url = $"twiliovoice/{sessionId}/send/0"; - var response = twilio.ReturnInstructions("twilio/welcome.mp3", url, false); + var url = $"twilio/voice/{conversationId}/receive/0?states={states}"; + var response = twilio.ReturnInstructions("twilio/welcome.mp3", url, true); return TwiML(response); } - [HttpPost("{sessionId}/send/{seqNum}")] - public async Task SendCallerMessage([FromRoute] string sessionId, [FromRoute] int seqNum, VoiceRequest request) + [HttpPost("{conversationId}/receive/{seqNum}")] + public async Task ReceiveCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, [FromQuery] string states, VoiceRequest request) { var twilio = _services.GetRequiredService(); var messageQueue = _services.GetRequiredService(); var sessionManager = _services.GetRequiredService(); - var url = $"twiliovoice/{sessionId}/reply/{seqNum}"; - var messages = await sessionManager.RetrieveStagedCallerMessagesAsync(sessionId, seqNum); + var url = $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}"; + var messages = await sessionManager.RetrieveStagedCallerMessagesAsync(conversationId, seqNum); if (!string.IsNullOrWhiteSpace(request.SpeechResult)) { messages.Add(request.SpeechResult); @@ -113,48 +64,78 @@ public class TwilioVoiceController : TwilioController VoiceResponse response; if (!string.IsNullOrWhiteSpace(messageContent)) { + var callerMessage = new CallerMessage() { - SessionId = sessionId, + ConversationId = conversationId, SeqNumber = seqNum, Content = messageContent, From = request.From }; + if (!string.IsNullOrEmpty(states)) + { + var kvp = states.Split(':'); + if (kvp.Length == 2) + { + callerMessage.States.Add(kvp[0], kvp[1]); + } + } await messageQueue.EnqueueAsync(callerMessage); - response = twilio.ReturnInstructions("twilio/holdon.mp3", url, true); + response = twilio.ReturnInstructions(null, url, true, 1); } else { - response = twilio.HangUp("twilio/holdon.mp3"); + var speechPath = seqNum > 0 ? $"twilio/voice/speeches/{conversationId}/{seqNum - 1}.mp3" : "twilio/welcome.mp3"; + response = twilio.ReturnInstructions(speechPath, $"twilio/voice/{conversationId}/receive/{seqNum}?states={states}", true); } return TwiML(response); } - [HttpPost("{sessionId}/reply/{seqNum}")] - public async Task ReplyCallerMessage([FromRoute] string sessionId, [FromRoute] int seqNum, VoiceRequest request) + [HttpPost("{conversationId}/reply/{seqNum}")] + public async Task ReplyCallerMessage([FromRoute] string conversationId, [FromRoute] int seqNum, [FromQuery] string states, VoiceRequest request) { var nextSeqNum = seqNum + 1; var sessionManager = _services.GetRequiredService(); var twilio = _services.GetRequiredService(); if (request.SpeechResult != null) { - await sessionManager.StageCallerMessageAsync(sessionId, nextSeqNum, request.SpeechResult); + await sessionManager.StageCallerMessageAsync(conversationId, nextSeqNum, request.SpeechResult); } - var reply = await sessionManager.GetAssistantReplyAsync(sessionId, seqNum); + var reply = await sessionManager.GetAssistantReplyAsync(conversationId, seqNum); VoiceResponse response; - if (string.IsNullOrEmpty(reply)) + if (reply == null) { - response = twilio.ReturnInstructions(null, $"twiliovoice/{sessionId}/reply/{seqNum}", true); + var indication = await sessionManager.GetReplyIndicationAsync(conversationId, seqNum); + if (indication != null) + { + var textToSpeechService = CompletionProvider.GetTextToSpeech(_services, "openai", "tts-1"); + var fileService = _services.GetRequiredService(); + var data = await textToSpeechService.GenerateSpeechFromTextAsync(indication); + var fileName = $"indication_{seqNum}.mp3"; + await fileService.SaveSpeechFileAsync(conversationId, fileName, data); + response = twilio.ReturnInstructions($"twilio/voice/speeches/{conversationId}/{fileName}", $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true, 2); + } + else + { + response = twilio.ReturnInstructions(null, $"twilio/voice/{conversationId}/reply/{seqNum}?states={states}", true, 1); + } } else { - var textToSpeechService = CompletionProvider.GetTextToSpeech(_services, "openai", "tts-1"); var fileService = _services.GetRequiredService(); - var data = await textToSpeechService.GenerateSpeechFromTextAsync(reply); - var fileName = $"{seqNum}.mp3"; - await fileService.SaveSpeechFileAsync(sessionId, fileName, data); - response = twilio.ReturnInstructions($"twiliovoice/speeches/{sessionId}/{fileName}", $"twiliovoice/{sessionId}/send/{nextSeqNum}", true); + var data = await textToSpeechService.GenerateSpeechFromTextAsync(reply.Content); + var fileName = $"reply_{seqNum}.mp3"; + await fileService.SaveSpeechFileAsync(conversationId, fileName, data); + if (reply.ConversationEnd) + { + response = twilio.HangUp($"twilio/voice/speeches/{conversationId}/{fileName}"); + } + else + { + response = twilio.ReturnInstructions($"twilio/voice/speeches/{conversationId}/{fileName}", $"twilio/voice/{conversationId}/receive/{nextSeqNum}?states={states}", true); + } + } return TwiML(response); } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs b/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs new file mode 100644 index 00000000..f9a83613 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Twilio/Models/AssistantMessage.cs @@ -0,0 +1,8 @@ +namespace BotSharp.Plugin.Twilio.Models +{ + public class AssistantMessage + { + public bool ConversationEnd { get; set; } + public string Content { get; set; } + } +} diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Models/CallerMessage.cs b/src/Plugins/BotSharp.Plugin.Twilio/Models/CallerMessage.cs index e0f4463a..a6339c7b 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Models/CallerMessage.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Models/CallerMessage.cs @@ -2,14 +2,15 @@ namespace BotSharp.Plugin.Twilio.Models { public class CallerMessage { - public string SessionId { get; set; } + public string ConversationId { get; set; } public int SeqNumber { get; set; } public string Content { get; set; } public string From { get; set; } + public Dictionary States { get; set; } = new(); public override string ToString() { - return $"{SessionId}-{SeqNumber}"; + return $"{ConversationId}-{SeqNumber}"; } } } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/ITwilioSessionManager.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/ITwilioSessionManager.cs index 3ad027f7..b1acd298 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/ITwilioSessionManager.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/ITwilioSessionManager.cs @@ -1,12 +1,15 @@ +using BotSharp.Plugin.Twilio.Models; using Task = System.Threading.Tasks.Task; namespace BotSharp.Plugin.Twilio.Services { public interface ITwilioSessionManager { - Task SetAssistantReplyAsync(string sessionId, int seqNum, string message); - Task GetAssistantReplyAsync(string sessionId, int seqNum); - Task StageCallerMessageAsync(string sessionId, int seqNum, string message); - Task> RetrieveStagedCallerMessagesAsync(string sessionId, int seqNum); + Task SetAssistantReplyAsync(string conversationId, int seqNum, AssistantMessage message); + Task GetAssistantReplyAsync(string conversationId, int seqNum); + Task StageCallerMessageAsync(string conversationId, int seqNum, string message); + Task> RetrieveStagedCallerMessagesAsync(string conversationId, int seqNum); + Task SetReplyIndicationAsync(string conversationId, int seqNum, string indication); + Task GetReplyIndicationAsync(string conversationId, int seqNum); } } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs index dab06c4b..a84ce5d9 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioMessageQueueService.cs @@ -55,35 +55,53 @@ namespace BotSharp.Plugin.Twilio.Services { using var scope = _serviceProvider.CreateScope(); var sp = scope.ServiceProvider; - string reply = null; + AssistantMessage reply = null; var inputMsg = new RoleDialogModel(AgentRole.User, message.Content); var conv = sp.GetRequiredService(); var routing = sp.GetRequiredService(); var config = sp.GetRequiredService(); - routing.Context.SetMessageId(message.SessionId, inputMsg.MessageId); - conv.SetConversationId(message.SessionId, new List + routing.Context.SetMessageId(message.ConversationId, inputMsg.MessageId); + var states = new List { new MessageState("channel", ConversationChannel.Phone), new MessageState("calling_phone", message.From) - }); + }; + foreach (var kvp in message.States) + { + states.Add(new MessageState(kvp.Key, kvp.Value)); + } + conv.SetConversationId(message.ConversationId, states); + var sessionManager = sp.GetRequiredService(); var result = await conv.SendMessage(config.AgentId, inputMsg, replyMessage: null, async msg => { - reply = msg.Content; + reply = new AssistantMessage() + { + ConversationEnd = msg.Instruction.ConversationEnd, + Content = msg.Content + }; + }, + async msg => + { + if (!string.IsNullOrEmpty(msg.Indication)) + { + await sessionManager.SetReplyIndicationAsync(message.ConversationId, message.SeqNumber, msg.Indication); + } }, - async functionExecuting => - { }, async functionExecuted => { } ); - if (string.IsNullOrWhiteSpace(reply)) + if (reply == null || string.IsNullOrWhiteSpace(reply.Content)) { - reply = "Sorry, something was wrong."; - } - var sessionManager = sp.GetRequiredService(); - await sessionManager.SetAssistantReplyAsync(message.SessionId, message.SeqNumber, reply); + reply = new AssistantMessage() + { + ConversationEnd = true, + Content = "Sorry, something was wrong." + }; + } + await sessionManager.SetAssistantReplyAsync(message.ConversationId, message.SeqNumber, reply); } } } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs index 09405817..a4e9d4ab 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioService.cs @@ -63,7 +63,7 @@ public class TwilioService return response; } - public VoiceResponse ReturnInstructions(string speechPath, string callbackPath, bool actionOnEmptyResult) + public VoiceResponse ReturnInstructions(string speechPath, string callbackPath, bool actionOnEmptyResult, int timeout = 3) { var response = new VoiceResponse(); var gather = new Gather() @@ -73,7 +73,9 @@ public class TwilioService Gather.InputEnum.Speech }, Action = new Uri($"{_settings.CallbackHost}/{callbackPath}"), - SpeechTimeout = "3", + SpeechModel = Gather.SpeechModelEnum.PhoneCall, + SpeechTimeout = timeout > 0 ? timeout.ToString() : "3", + Timeout = timeout > 0 ? timeout : 3, ActionOnEmptyResult = actionOnEmptyResult }; if (!string.IsNullOrEmpty(speechPath)) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioSessionManager.cs b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioSessionManager.cs index daefbdcc..eae0b238 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioSessionManager.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/Services/TwilioSessionManager.cs @@ -1,3 +1,4 @@ +using BotSharp.Plugin.Twilio.Models; using StackExchange.Redis; using Task = System.Threading.Tasks.Task; @@ -12,35 +13,51 @@ namespace BotSharp.Plugin.Twilio.Services _redis = redis; } - public async Task GetAssistantReplyAsync(string sessionId, int seqNum) + public async Task GetAssistantReplyAsync(string conversationId, int seqNum) { var db = _redis.GetDatabase(); - var key = $"{sessionId}:Assisist:{seqNum}"; - return await db.StringGetAsync(key); + var key = $"{conversationId}:Assisist:{seqNum}"; + var jsonStr = await db.StringGetAsync(key); + return jsonStr.IsNull ? null : JsonSerializer.Deserialize(jsonStr); } - public async Task> RetrieveStagedCallerMessagesAsync(string sessionId, int seqNum) + public async Task> RetrieveStagedCallerMessagesAsync(string conversationId, int seqNum) { var db = _redis.GetDatabase(); - var key = $"{sessionId}:Caller:{seqNum}"; + var key = $"{conversationId}:Caller:{seqNum}"; return (await db.ListRangeAsync(key)) .Select(x => (string)x) .ToList(); } - public async Task SetAssistantReplyAsync(string sessionId, int seqNum, string message) + public async Task SetAssistantReplyAsync(string conversationId, int seqNum, AssistantMessage message) { + var jsonStr = JsonSerializer.Serialize(message); var db = _redis.GetDatabase(); - var key = $"{sessionId}:Assisist:{seqNum}"; - await db.StringSetAsync(key, message, TimeSpan.FromMinutes(5)); + var key = $"{conversationId}:Assisist:{seqNum}"; + await db.StringSetAsync(key, jsonStr, TimeSpan.FromMinutes(5)); } - public async Task StageCallerMessageAsync(string sessionId, int seqNum, string message) + public async Task StageCallerMessageAsync(string conversationId, int seqNum, string message) { var db = _redis.GetDatabase(); - var key = $"{sessionId}:Caller:{seqNum}"; + var key = $"{conversationId}:Caller:{seqNum}"; await db.ListRightPushAsync(key, message); await db.KeyExpireAsync(key, DateTime.UtcNow.AddMinutes(10)); } + + public async Task SetReplyIndicationAsync(string conversationId, int seqNum, string indication) + { + var db = _redis.GetDatabase(); + var key = $"{conversationId}:Indication:{seqNum}"; + await db.StringSetAsync(key, indication, TimeSpan.FromMinutes(5)); + } + + public async Task GetReplyIndicationAsync(string conversationId, int seqNum) + { + var db = _redis.GetDatabase(); + var key = $"{conversationId}:Indication:{seqNum}"; + return await db.StringGetAsync(key); + } } } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs b/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs index 38d278a6..1247d0d4 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/TwilioPlugin.cs @@ -17,7 +17,6 @@ public class TwilioPlugin : IBotSharpPlugin var settingService = provider.GetRequiredService(); return settingService.Bind("Twilio"); }); - services.AddScoped(); var conn = ConnectionMultiplexer.Connect(config["Twilio:RedisConnectionString"]); var sessionManager = new TwilioSessionManager(conn); From 548cd558c0366a986697f5508499068e0a42832c Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 14 Aug 2024 16:56:32 -0500 Subject: [PATCH 12/13] add default collection --- .../Knowledges/Settings/KnowledgeBaseSettings.cs | 3 +++ .../Repository/FileRepository/FileRepository.Agent.cs | 2 +- .../Functions/KnowledgeRetrievalFn.cs | 3 ++- .../Functions/MemorizeKnowledgeFn.cs | 5 +++-- src/WebStarter/appsettings.json | 1 + 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs index ac3f0500..f2c62fd0 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs @@ -1,8 +1,11 @@ +using BotSharp.Abstraction.Knowledges.Enums; + namespace BotSharp.Abstraction.Knowledges.Settings; public class KnowledgeBaseSettings { public string VectorDb { get; set; } + public string DefaultCollection { get; set; } = KnowledgeCollectionName.BotSharp; public KnowledgeModelSetting TextEmbedding { get; set; } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index 950a2b2d..bb137279 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -298,7 +298,7 @@ namespace BotSharp.Core.Repository var json = JsonSerializer.Serialize(agent, _options); File.WriteAllText(agentFile, json); - UpdateAgentInstructions(inputAgent.Id, inputAgent.Instruction, agent.ChannelInstructions); + UpdateAgentInstructions(inputAgent.Id, inputAgent.Instruction, inputAgent.ChannelInstructions); UpdateAgentResponses(inputAgent.Id, inputAgent.Responses); UpdateAgentTemplates(inputAgent.Id, inputAgent.Templates); UpdateAgentFunctions(inputAgent.Id, inputAgent.Functions); diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs index 62397c53..52e45e7e 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/KnowledgeRetrievalFn.cs @@ -22,7 +22,8 @@ public class KnowledgeRetrievalFn : IFunctionCallback var vector = await embedding.GetVectorAsync(args.Question); var vectorDb = _services.GetServices().FirstOrDefault(x => x.Name == _settings.VectorDb); - var knowledges = await vectorDb.Search(KnowledgeCollectionName.BotSharp, vector, new List { KnowledgePayloadName.Answer }); + var collectionName = !string.IsNullOrWhiteSpace(_settings.DefaultCollection) ? _settings.DefaultCollection : KnowledgeCollectionName.BotSharp; + var knowledges = await vectorDb.Search(collectionName, vector, new List { KnowledgePayloadName.Answer }); if (!knowledges.IsNullOrEmpty()) { diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs index b4b0111c..237f7385 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Functions/MemorizeKnowledgeFn.cs @@ -26,10 +26,11 @@ public class MemorizeKnowledgeFn : IFunctionCallback }); var vectorDb = _services.GetServices().FirstOrDefault(x => x.Name == _settings.VectorDb); - await vectorDb.CreateCollection(KnowledgeCollectionName.BotSharp, vector[0].Length); + var collectionName = !string.IsNullOrWhiteSpace(_settings.DefaultCollection) ? _settings.DefaultCollection : KnowledgeCollectionName.BotSharp; + await vectorDb.CreateCollection(collectionName, vector[0].Length); var id = Guid.NewGuid().ToString(); - var result = await vectorDb.Upsert(KnowledgeCollectionName.BotSharp, id, vector[0], + var result = await vectorDb.Upsert(collectionName, id, vector[0], args.Question, new Dictionary { diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 155dae1b..d8e29023 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -259,6 +259,7 @@ "KnowledgeBase": { "VectorDb": "Qdrant", + "DefaultCollection": "BotSharp", "TextEmbedding": { "Provider": "openai", "Model": "text-embedding-3-small" From d99d89783b920163d6593a79842865526bf0e41b Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 14 Aug 2024 22:46:01 -0500 Subject: [PATCH 13/13] unite knowledge search model --- .../Knowledges/IKnowledgeService.cs | 4 +- .../Models/KnowledgeCollectionData.cs | 6 +-- ...alOptions.cs => KnowledgeSearchOptions.cs} | 2 +- .../Models/KnowledgeSearchResult.cs | 22 +++++++---- .../VectorStorage/IVectorDb.cs | 4 +- .../Controllers/KnowledgeBaseController.cs | 24 ++++++------ .../KnowledgeCollectionDataViewModel.cs | 33 ---------------- .../Knowledges/KnowledgeRetrivalViewModel.cs | 27 ------------- .../KnowledgeSearchResultViewModel.cs | 33 ++++++++++++++++ ...edgeModel.cs => SearchKnowledgeRequest.cs} | 2 +- .../MemVecDb/MemoryVectorDb.cs | 12 +++--- .../Services/KnowledgeService.Get.cs | 26 ++++++------- .../Providers/FaissDb.cs | 4 +- .../BotSharp.Plugin.Qdrant/QdrantDb.cs | 38 +++++++++++-------- .../SemanticKernelMemoryStoreProvider.cs | 9 ++--- 15 files changed, 117 insertions(+), 129 deletions(-) rename src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/{KnowledgeRetrievalOptions.cs => KnowledgeSearchOptions.cs} (91%) delete mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeCollectionDataViewModel.cs delete mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeRetrivalViewModel.cs create mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeSearchResultViewModel.cs rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{SearchKnowledgeModel.cs => SearchKnowledgeRequest.cs} (93%) diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs index d2882bdc..02afb420 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs @@ -3,8 +3,8 @@ namespace BotSharp.Abstraction.Knowledges; public interface IKnowledgeService { Task> GetKnowledgeCollections(); - Task> SearchKnowledge(string collectionName, KnowledgeRetrievalOptions options); + Task> SearchKnowledge(string collectionName, KnowledgeSearchOptions options); Task FeedKnowledge(string collectionName, KnowledgeCreationModel model); - Task> GetKnowledgeCollectionData(string collectionName, KnowledgeFilter filter); + Task> GetKnowledgeCollectionData(string collectionName, KnowledgeFilter filter); Task DeleteKnowledgeCollectionData(string collectionName, string id); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeCollectionData.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeCollectionData.cs index d013529f..6a8faff4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeCollectionData.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeCollectionData.cs @@ -3,7 +3,7 @@ namespace BotSharp.Abstraction.Knowledges.Models; public class KnowledgeCollectionData { public string Id { get; set; } - public string Question { get; set; } - public string Answer { get; set; } + public Dictionary Data { get; set; } = new(); + public double? Score { get; set; } public float[]? Vector { get; set; } -} +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeRetrievalOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchOptions.cs similarity index 91% rename from src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeRetrievalOptions.cs rename to src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchOptions.cs index 8a608ddc..4ac1b77b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeRetrievalOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchOptions.cs @@ -2,7 +2,7 @@ using BotSharp.Abstraction.Knowledges.Enums; namespace BotSharp.Abstraction.Knowledges.Models; -public class KnowledgeRetrievalOptions +public class KnowledgeSearchOptions { public string Text { get; set; } = string.Empty; public IEnumerable? Fields { get; set; } = new List { KnowledgePayloadName.Text, KnowledgePayloadName.Answer }; diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchResult.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchResult.cs index 2d5d9c1f..661578c2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/KnowledgeSearchResult.cs @@ -1,12 +1,20 @@ namespace BotSharp.Abstraction.Knowledges.Models; -public class KnowledgeSearchResult +public class KnowledgeSearchResult : KnowledgeCollectionData { - public Dictionary Data { get; set; } = new(); - public double Score { get; set; } - public float[]? Vector { get; set; } -} + public KnowledgeSearchResult() + { + + } -public class KnowledgeRetrievalResult : KnowledgeSearchResult -{ + public static KnowledgeSearchResult CopyFrom(KnowledgeCollectionData data) + { + return new KnowledgeSearchResult + { + Id = data.Id, + Data = data.Data, + Score = data.Score, + Vector = data.Vector + }; + } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs index a079aaef..ff4ec26a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs +++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs @@ -3,11 +3,11 @@ namespace BotSharp.Abstraction.VectorStorage; public interface IVectorDb { string Name { get; } - + Task> GetCollections(); Task> GetCollectionData(string collectionName, KnowledgeFilter filter); Task CreateCollection(string collectionName, int dim); Task Upsert(string collectionName, string id, float[] vector, string text, Dictionary? payload = null); - Task> Search(string collectionName, float[] vector, IEnumerable fields, int limit = 5, float confidence = 0.5f, bool withVector = false); + Task> Search(string collectionName, float[] vector, IEnumerable? fields, int limit = 5, float confidence = 0.5f, bool withVector = false); Task DeleteCollectionData(string collectionName, string id); } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs index 55712aaa..b01c4950 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs @@ -23,29 +23,29 @@ public class KnowledgeBaseController : ControllerBase } [HttpPost("/knowledge/{collection}/search")] - public async Task> SearchKnowledge([FromRoute] string collection, [FromBody] SearchKnowledgeModel model) + public async Task> SearchKnowledge([FromRoute] string collection, [FromBody] SearchKnowledgeRequest request) { - var options = new KnowledgeRetrievalOptions + var options = new KnowledgeSearchOptions { - Text = model.Text, - Fields = model.Fields, - Limit = model.Limit ?? 5, - Confidence = model.Confidence ?? 0.5f, - WithVector = model.WithVector + Text = request.Text, + Fields = request.Fields, + Limit = request.Limit ?? 5, + Confidence = request.Confidence ?? 0.5f, + WithVector = request.WithVector }; var results = await _knowledgeService.SearchKnowledge(collection, options); - return results.Select(x => KnowledgeRetrivalViewModel.From(x)).ToList(); + return results.Select(x => KnowledgeSearchResultViewModel.From(x)).ToList(); } [HttpPost("/knowledge/{collection}/data")] - public async Task> GetKnowledgeCollectionData([FromRoute] string collection, [FromBody] KnowledgeFilter filter) + public async Task> GetKnowledgeCollectionData([FromRoute] string collection, [FromBody] KnowledgeFilter filter) { var data = await _knowledgeService.GetKnowledgeCollectionData(collection, filter); - var items = data.Items?.Select(x => KnowledgeCollectionDataViewModel.From(x))? - .ToList() ?? new List(); + var items = data.Items?.Select(x => KnowledgeSearchResultViewModel.From(x))? + .ToList() ?? new List(); - return new StringIdPagedItems + return new StringIdPagedItems { Count = data.Count, NextId = data.NextId, diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeCollectionDataViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeCollectionDataViewModel.cs deleted file mode 100644 index f77b8777..00000000 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeCollectionDataViewModel.cs +++ /dev/null @@ -1,33 +0,0 @@ -using BotSharp.Abstraction.Knowledges.Models; -using System.Text.Json.Serialization; - -namespace BotSharp.OpenAPI.ViewModels.Knowledges; - -public class KnowledgeCollectionDataViewModel -{ - [JsonPropertyName("id")] - public string Id { get; set; } - - [JsonPropertyName("question")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string Question { get; set; } - - [JsonPropertyName("answer")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string Answer { get; set; } - - [JsonPropertyName("vector")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public float[]? Vector { get; set; } - - public static KnowledgeCollectionDataViewModel From(KnowledgeCollectionData data) - { - return new KnowledgeCollectionDataViewModel - { - Id = data.Id, - Question = data.Question, - Answer = data.Answer, - Vector = data.Vector - }; - } -} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeRetrivalViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeRetrivalViewModel.cs deleted file mode 100644 index 2e2b9e08..00000000 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeRetrivalViewModel.cs +++ /dev/null @@ -1,27 +0,0 @@ -using BotSharp.Abstraction.Knowledges.Models; -using System.Text.Json.Serialization; - -namespace BotSharp.OpenAPI.ViewModels.Knowledges; - -public class KnowledgeRetrivalViewModel -{ - [JsonPropertyName("data")] - public IDictionary Data { get; set; } - - [JsonPropertyName("score")] - public double Score { get; set; } - - [JsonPropertyName("vector")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public float[]? Vector { get; set; } - - public static KnowledgeRetrivalViewModel From(KnowledgeRetrievalResult model) - { - return new KnowledgeRetrivalViewModel - { - Data = model.Data, - Score = model.Score, - Vector = model.Vector - }; - } -} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeSearchResultViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeSearchResultViewModel.cs new file mode 100644 index 00000000..f322bc7d --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeSearchResultViewModel.cs @@ -0,0 +1,33 @@ +using BotSharp.Abstraction.Knowledges.Models; +using System.Text.Json.Serialization; + +namespace BotSharp.OpenAPI.ViewModels.Knowledges; + +public class KnowledgeSearchResultViewModel +{ + [JsonPropertyName("id")] + public string Id { get; set; } + + [JsonPropertyName("data")] + public IDictionary Data { get; set; } + + [JsonPropertyName("score")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public double? Score { get; set; } + + [JsonPropertyName("vector")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public float[]? Vector { get; set; } + + + public static KnowledgeSearchResultViewModel From(KnowledgeSearchResult result) + { + return new KnowledgeSearchResultViewModel + { + Id = result.Id, + Data = result.Data, + Score = result.Score, + Vector = result.Vector + }; + } +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/SearchKnowledgeModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/SearchKnowledgeRequest.cs similarity index 93% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/SearchKnowledgeModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/SearchKnowledgeRequest.cs index 9a91e004..cacb133a 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/SearchKnowledgeModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/SearchKnowledgeRequest.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; namespace BotSharp.OpenAPI.ViewModels.Knowledges; -public class SearchKnowledgeModel +public class SearchKnowledgeRequest { [JsonPropertyName("text")] public string Text { get; set; } = string.Empty; diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemoryVectorDb.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemoryVectorDb.cs index ba5e6383..f8f31ca2 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemoryVectorDb.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/MemVecDb/MemoryVectorDb.cs @@ -27,12 +27,12 @@ public class MemoryVectorDb : IVectorDb throw new NotImplementedException(); } - public async Task> Search(string collectionName, float[] vector, - IEnumerable fields, int limit = 5, float confidence = 0.5f, bool withVector = false) + public async Task> Search(string collectionName, float[] vector, + IEnumerable? fields, int limit = 5, float confidence = 0.5f, bool withVector = false) { if (!_vectors.ContainsKey(collectionName)) { - return new List(); + return new List(); } var similarities = VectorUtility.CalCosineSimilarity(vector, _vectors[collectionName]); @@ -41,7 +41,7 @@ public class MemoryVectorDb : IVectorDb var results = np.argsort(similarities).ToArray() .Reverse() .Take(limit) - .Select(i => new KnowledgeSearchResult + .Select(i => new KnowledgeCollectionData { Data = new Dictionary { { "text", _vectors[collectionName][i].Text } }, Score = similarities[i], @@ -64,8 +64,8 @@ public class MemoryVectorDb : IVectorDb return true; } - public Task DeleteCollectionData(string collectionName, string id) + public async Task DeleteCollectionData(string collectionName, string id) { - throw new NotImplementedException(); + return await Task.FromResult(false); } } diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs index 75824707..243eca02 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Get.cs @@ -16,21 +16,27 @@ public partial class KnowledgeService } } - public async Task> GetKnowledgeCollectionData(string collectionName, KnowledgeFilter filter) + public async Task> GetKnowledgeCollectionData(string collectionName, KnowledgeFilter filter) { try { var db = GetVectorDb(); - return await db.GetCollectionData(collectionName, filter); + var pagedResult = await db.GetCollectionData(collectionName, filter); + return new StringIdPagedItems + { + Count = pagedResult.Count, + Items = pagedResult.Items.Select(x => KnowledgeSearchResult.CopyFrom(x)), + NextId = pagedResult.NextId, + }; } catch (Exception ex) { _logger.LogWarning($"Error when getting knowledge collection data ({collectionName}). {ex.Message}\r\n{ex.InnerException}"); - return new StringIdPagedItems(); + return new StringIdPagedItems(); } } - public async Task> SearchKnowledge(string collectionName, KnowledgeRetrievalOptions options) + public async Task> SearchKnowledge(string collectionName, KnowledgeSearchOptions options) { try { @@ -39,21 +45,15 @@ public partial class KnowledgeService // Vector search var db = GetVectorDb(); - var fields = !options.Fields.IsNullOrEmpty() ? options.Fields : new List { KnowledgePayloadName.Text, KnowledgePayloadName.Answer }; - var found = await db.Search(collectionName, vector, fields, limit: options.Limit ?? 5, confidence: options.Confidence ?? 0.5f, withVector: options.WithVector); + var found = await db.Search(collectionName, vector, options.Fields, limit: options.Limit ?? 5, confidence: options.Confidence ?? 0.5f, withVector: options.WithVector); - var results = found.Select(x => new KnowledgeRetrievalResult - { - Data = x.Data, - Score = x.Score, - Vector = x.Vector - }).ToList(); + var results = found.Select(x => KnowledgeSearchResult.CopyFrom(x)).ToList(); return results; } catch (Exception ex) { _logger.LogWarning($"Error when searching knowledge ({collectionName}). {ex.Message}\r\n{ex.InnerException}"); - return new List(); + return new List(); } } } diff --git a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs index 785acd4d..225c0705 100644 --- a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs +++ b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/FaissDb.cs @@ -26,8 +26,8 @@ public class FaissDb : IVectorDb throw new NotImplementedException(); } - public Task> Search(string collectionName, float[] vector, - IEnumerable fields, int limit = 10, float confidence = 0.5f, bool withVector = false) + public Task> Search(string collectionName, float[] vector, + IEnumerable? fields, int limit = 10, float confidence = 0.5f, bool withVector = false) { throw new NotImplementedException(); } diff --git a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs index 02520517..0e56f680 100644 --- a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs +++ b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs @@ -57,8 +57,7 @@ public class QdrantDb : IVectorDb var points = response?.Result?.Select(x => new KnowledgeCollectionData { Id = x.Id?.Uuid ?? string.Empty, - Question = x.Payload.ContainsKey(KnowledgePayloadName.Text) ? x.Payload[KnowledgePayloadName.Text].StringValue : string.Empty, - Answer = x.Payload.ContainsKey(KnowledgePayloadName.Answer) ? x.Payload[KnowledgePayloadName.Answer].StringValue : string.Empty, + Data = x.Payload.ToDictionary(x => x.Key, x => x.Value.StringValue), Vector = filter.WithVector ? x.Vectors?.Vector?.Data?.ToArray() : null })?.ToList() ?? new List(); @@ -125,10 +124,10 @@ public class QdrantDb : IVectorDb return result.Status == UpdateStatus.Completed; } - public async Task> Search(string collectionName, float[] vector, - IEnumerable fields, int limit = 5, float confidence = 0.5f, bool withVector = false) + public async Task> Search(string collectionName, float[] vector, + IEnumerable? fields, int limit = 5, float confidence = 0.5f, bool withVector = false) { - var results = new List(); + var results = new List(); var client = GetClient(); var exist = await DoesCollectionExist(client, collectionName); @@ -138,24 +137,33 @@ public class QdrantDb : IVectorDb } var points = await client.SearchAsync(collectionName, vector, limit: (ulong)limit, scoreThreshold: confidence); - + + var pickFields = fields != null; foreach (var point in points) { var data = new Dictionary(); - foreach (var field in fields) + if (pickFields) { - if (point.Payload.ContainsKey(field)) + foreach (var field in fields) { - data[field] = point.Payload[field].StringValue; - } - else - { - data[field] = ""; + if (point.Payload.ContainsKey(field)) + { + data[field] = point.Payload[field].StringValue; + } + else + { + data[field] = ""; + } } } - - results.Add(new KnowledgeSearchResult + else { + data = point.Payload.ToDictionary(k => k.Key, v => v.Value.StringValue); + } + + results.Add(new KnowledgeCollectionData + { + Id = point.Id.Uuid, Data = data, Score = point.Score, Vector = withVector ? point.Vectors?.Vector?.Data?.ToArray() : null diff --git a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs index a5db6de8..487b8ffd 100644 --- a/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SemanticKernel/SemanticKernelMemoryStoreProvider.cs @@ -2,7 +2,6 @@ using BotSharp.Abstraction.Knowledges.Models; using BotSharp.Abstraction.Utilities; using BotSharp.Abstraction.VectorStorage; using Microsoft.SemanticKernel.Memory; -using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -44,15 +43,15 @@ namespace BotSharp.Plugin.SemanticKernel return result; } - public async Task> Search(string collectionName, float[] vector, - IEnumerable fields, int limit = 5, float confidence = 0.5f, bool withVector = false) + public async Task> Search(string collectionName, float[] vector, + IEnumerable? fields, int limit = 5, float confidence = 0.5f, bool withVector = false) { var results = _memoryStore.GetNearestMatchesAsync(collectionName, vector, limit); - var resultTexts = new List(); + var resultTexts = new List(); await foreach (var (record, score) in results) { - resultTexts.Add(new KnowledgeSearchResult + resultTexts.Add(new KnowledgeCollectionData { Data = new Dictionary { { "text", record.Metadata.Text } }, Score = score,