diff --git a/BotSharp.sln b/BotSharp.sln
index ec114349..1d78c41a 100644
--- a/BotSharp.sln
+++ b/BotSharp.sln
@@ -45,7 +45,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.MetaMesseng
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.OpenAPI", "src\Infrastructure\BotSharp.OpenAPI\BotSharp.OpenAPI.csproj", "{7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.LLamaSharp", "src\Plugins\BotSharp.Plugin.LLamaSharp\BotSharp.Plugin.LLamaSharp.csproj", "{46B7B54F-1425-4C9D-824A-9B826855D249}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.LLamaSharp", "src\Plugins\BotSharp.Plugin.LLamaSharp\BotSharp.Plugin.LLamaSharp.csproj", "{46B7B54F-1425-4C9D-824A-9B826855D249}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.PizzaBot", "tests\BotSharp.Plugin.PizzaBot\BotSharp.Plugin.PizzaBot.csproj", "{A1118A2C-C6D7-4E22-9462-964AEC7CC46E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{51AFE054-AE99-497D-A593-69BAEFB5106F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.RoutingSpeeder", "src\Plugins\BotSharp.Plugin.RoutingSpeeder\BotSharp.Plugin.RoutingSpeeder.csproj", "{631D9C12-86C4-44F0-99C3-D32C0754BF37}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -167,6 +173,22 @@ Global
{46B7B54F-1425-4C9D-824A-9B826855D249}.Release|Any CPU.Build.0 = Release|Any CPU
{46B7B54F-1425-4C9D-824A-9B826855D249}.Release|x64.ActiveCfg = Release|Any CPU
{46B7B54F-1425-4C9D-824A-9B826855D249}.Release|x64.Build.0 = Release|Any CPU
+ {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Debug|x64.Build.0 = Debug|Any CPU
+ {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|x64.ActiveCfg = Release|Any CPU
+ {A1118A2C-C6D7-4E22-9462-964AEC7CC46E}.Release|x64.Build.0 = Release|Any CPU
+ {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Debug|x64.Build.0 = Debug|Any CPU
+ {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|Any CPU.Build.0 = Release|Any CPU
+ {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|x64.ActiveCfg = Release|Any CPU
+ {631D9C12-86C4-44F0-99C3-D32C0754BF37}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -190,6 +212,9 @@ Global
{8300F66D-9EB8-438A-BF0F-70DFBE07D9DE} = {64264688-0F5C-4AB0-8F2B-B59B717CCE00}
{7E63F5F8-4EA0-498B-ABFE-2BBE4D7DDBA7} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
{46B7B54F-1425-4C9D-824A-9B826855D249} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
+ {A1118A2C-C6D7-4E22-9462-964AEC7CC46E} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
+ {51AFE054-AE99-497D-A593-69BAEFB5106F} = {2635EC9B-2E5F-4313-AC21-0B847F31F36C}
+ {631D9C12-86C4-44F0-99C3-D32C0754BF37} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs
index 60307439..bf242ecc 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs
@@ -2,16 +2,6 @@ namespace BotSharp.Abstraction.Agents.Settings;
public class AgentSettings
{
- ///
- /// Router Agent Id
- ///
- public string RouterId { get; set; }
-
- ///
- /// Reasoner Agent Id
- ///
- public string ReasonerId { get; set; }
-
public string DataDir { get; set; }
public string TemplateFormat { get; set; }
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj
index 802652e9..80a83846 100644
--- a/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj
+++ b/src/Infrastructure/BotSharp.Abstraction/BotSharp.Abstraction.csproj
@@ -4,7 +4,7 @@
netstandard2.1
enable
10.0
- 0.9.4
+ 0.10.0
Icon.png
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs
index 4e8de8b3..3634255a 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs
@@ -1,5 +1,3 @@
-using BotSharp.Abstraction.Conversations.Models;
-
namespace BotSharp.Abstraction.Conversations;
public abstract class ConversationHookBase : IConversationHook
@@ -13,6 +11,9 @@ public abstract class ConversationHookBase : IConversationHook
protected List _dialogs;
public List Dialogs => _dialogs;
+ protected int _priority = 0;
+ public int Priority => _priority;
+
public IConversationHook SetAgent(Agent agent)
{
_agent = agent;
@@ -35,7 +36,7 @@ public abstract class ConversationHookBase : IConversationHook
return Task.CompletedTask;
}
- public virtual Task BeforeCompletion()
+ public virtual Task BeforeCompletion(RoleDialogModel message)
{
return Task.CompletedTask;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
index 6b2fdccb..8e55dab7 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs
@@ -1,10 +1,8 @@
-using BotSharp.Abstraction.Conversations.Models;
-using BotSharp.Abstraction.MLTasks;
-
namespace BotSharp.Abstraction.Conversations;
public interface IConversationHook
{
+ int Priority { get; }
Agent Agent { get; }
IConversationHook SetAgent(Agent agent);
@@ -22,7 +20,7 @@ public interface IConversationHook
Task OnStateLoaded(ConversationState state);
Task OnStateChanged(string name, string preValue, string currentValue);
- Task BeforeCompletion();
+ Task BeforeCompletion(RoleDialogModel message);
Task OnFunctionExecuting(RoleDialogModel message);
Task OnFunctionExecuted(RoleDialogModel message);
Task AfterCompletion(RoleDialogModel message);
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
index e80c9427..2740c8c8 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
@@ -30,6 +30,16 @@ public class RoleDialogModel
///
public object ExecutionData { get; set; }
+ ///
+ /// Intent name
+ ///
+ public string IntentName { get; set; }
+
+ ///
+ /// Stop conversation completion
+ ///
+ public bool StopCompletion { get; set; }
+
///
/// Channel name
///
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/ConversationSetting.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/ConversationSetting.cs
index c9836d4b..7cad03ff 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/ConversationSetting.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/ConversationSetting.cs
@@ -7,5 +7,4 @@ public class ConversationSetting
public bool EnableKnowledgeBase { get; set; }
public bool ShowVerboseLog { get; set; }
public int MaxRecursiveDepth { get; set; } = 3;
- public bool EnableReasoning { get; set; }
}
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBaseSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs
similarity index 81%
rename from src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBaseSettings.cs
rename to src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs
index 0cb08a48..97f7f55c 100644
--- a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBaseSettings.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Settings/KnowledgeBaseSettings.cs
@@ -1,4 +1,4 @@
-namespace BotSharp.Core.Plugins.Knowledges;
+namespace BotSharp.Abstraction.Knowledges.Settings;
public class KnowledgeBaseSettings
{
diff --git a/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs
index f23b70f8..95b1ea28 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Plugins/PluginLoaderSettings.cs
@@ -3,5 +3,4 @@ namespace BotSharp.Abstraction.Plugins;
public class PluginLoaderSettings
{
public string[] Assemblies { get; set; }
- public string[] Plugins { get; set; }
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingProfileRecord.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingProfileRecord.cs
new file mode 100644
index 00000000..753881ec
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingProfileRecord.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace BotSharp.Abstraction.Routing.Models;
+
+public class RoutingProfileRecord
+{
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("agent_ids")]
+ public string[] AgentIds { get; set; }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRecord.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRecord.cs
index f0d6d742..73fd4d1e 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRecord.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRecord.cs
@@ -14,7 +14,7 @@ public class RoutingRecord
public string Description { get; set; }
[JsonPropertyName("required")]
- public List RequiredFields { get; set; }
+ public List RequiredFields { get; set; } = new List();
[JsonPropertyName("redirect_to")]
public string RedirectTo { get; set; }
diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs
new file mode 100644
index 00000000..b9cc51a9
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Settings/RoutingSettings.cs
@@ -0,0 +1,14 @@
+namespace BotSharp.Abstraction.Routing.Settings;
+
+public class RoutingSettings
+{
+ ///
+ /// Router Agent Id
+ ///
+ public string RouterId { get; set; }
+
+ ///
+ /// Reasoner Agent Id
+ ///
+ public string ReasonerId { get; set; }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Templating/IResponseTemplateService.cs b/src/Infrastructure/BotSharp.Abstraction/Templating/IResponseTemplateService.cs
new file mode 100644
index 00000000..da52c432
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Templating/IResponseTemplateService.cs
@@ -0,0 +1,8 @@
+namespace BotSharp.Abstraction.Templating;
+
+public interface IResponseTemplateService
+{
+ Task RenderFunctionResponse(string agentId, RoleDialogModel message);
+
+ Task RenderIntentResponse(string agentId, RoleDialogModel message);
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs b/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs
index 61ba9ff7..e5ddeb61 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Templating/ITemplateRender.cs
@@ -2,5 +2,5 @@ namespace BotSharp.Abstraction.Templating;
public interface ITemplateRender
{
- bool Render(Agent agent, Dictionary dict);
+ string Render(string template, Dictionary dict);
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Using.cs b/src/Infrastructure/BotSharp.Abstraction/Using.cs
index 3a545a0a..1a298f42 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Using.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Using.cs
@@ -4,4 +4,5 @@ global using System.Text;
global using System.Linq;
global using System.Threading.Tasks;
global using System.ComponentModel.DataAnnotations;
-global using BotSharp.Abstraction.Agents.Models;
\ No newline at end of file
+global using BotSharp.Abstraction.Agents.Models;
+global using BotSharp.Abstraction.Conversations.Models;
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
index ec89334f..e2f13567 100644
--- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
+++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Agents.Models;
+using BotSharp.Abstraction.Templating;
using BotSharp.Core.Templating;
namespace BotSharp.Core.Agents.Services;
@@ -45,8 +46,8 @@ public partial class AgentService
}
// render liquid template
- var render = _services.GetRequiredService();
- render.Render(agent, templateDict);
+ var render = _services.GetRequiredService();
+ agent.Instruction = render.Render(agent.Instruction, templateDict);
_logger.LogInformation($"Loaded agent {agent}.");
diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
index f59e6493..21f5e111 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
+++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
@@ -4,7 +4,7 @@
netstandard2.1
10.0
false
- 0.9.4
+ 0.10.0
diff --git a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs
index 6c081daf..45121b49 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs
+++ b/src/Infrastructure/BotSharp.Core/BotSharpServiceCollectionExtensions.cs
@@ -8,7 +8,8 @@ using BotSharp.Core.Templating;
using BotSharp.Core.Plugins.Knowledges.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
-using DatabaseSettings = BotSharp.Abstraction.Repositories.DatabaseSettings;
+using BotSharp.Abstraction.Routing.Settings;
+using BotSharp.Abstraction.Templating;
namespace BotSharp.Core;
@@ -43,16 +44,18 @@ public static class BotSharpServiceCollectionExtensions
RegisterPlugins(services, config);
// Register template render
- services.AddSingleton();
+ services.AddSingleton();
+ services.AddScoped();
// Register router
+ var routingSettings = new RoutingSettings();
+ config.Bind("Router", routingSettings);
+ services.AddSingleton((IServiceProvider x) => routingSettings);
+
services.AddScoped();
services.AddScoped();
- services.AddScoped(p =>
- {
- var setting = p.GetRequiredService();
- return setting.EnableReasoning ? p.GetRequiredService() : p.GetRequiredService();
- });
+ services.AddScoped();
+ services.AddScoped();
// Register function callback
services.AddScoped();
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.CallFunctions.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.CallFunctions.cs
index 66f0a25d..02a81350 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.CallFunctions.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.CallFunctions.cs
@@ -7,7 +7,8 @@ public partial class ConversationService
{
private async Task CallFunctions(RoleDialogModel msg)
{
- var hooks = _services.GetServices().ToList();
+ var hooks = _services.GetServices()
+ .OrderBy(x => x.Priority).ToList();
// Invoke functions
var functions = _services.GetServices()
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs
index 4e5a0d23..21e8950c 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.GetChatCompletionsAsyncRecursively.cs
@@ -1,7 +1,7 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
-using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
+using BotSharp.Abstraction.Templating;
namespace BotSharp.Core.Conversations.Services;
@@ -68,22 +68,48 @@ public partial class ConversationService
{
var agentService = _services.GetRequiredService();
agent = await agentService.LoadAgent(fn.CurrentAgentId);
+
+ wholeDialogs.Add(fn);
+
+ await GetChatCompletionsAsyncRecursively(chatCompletion,
+ conversationId,
+ agent,
+ wholeDialogs,
+ onMessageReceived,
+ onFunctionExecuting,
+ onFunctionExecuted);
}
+ else
+ {
+ // Find response template
+ var templateService = _services.GetRequiredService();
+ var response = await templateService.RenderFunctionResponse(agent.Id, fn);
+ if (!string.IsNullOrEmpty(response))
+ {
+ await HandleAssistantMessage(new RoleDialogModel(AgentRole.Assistant, response)
+ {
+ CurrentAgentId = agent.Id,
+ Channel = wholeDialogs.Last().Channel
+ }, onMessageReceived);
- // Add to dialog history
- // The server had an error processing your request. Sorry about that!
- // _storage.Append(conversationId, preAgentId, fn);
+ return;
+ }
+
+ // Add to dialog history
+ // The server had an error processing your request. Sorry about that!
+ // _storage.Append(conversationId, preAgentId, fn);
- // After function is executed, pass the result to LLM to get a natural response
- wholeDialogs.Add(fn);
+ // After function is executed, pass the result to LLM to get a natural response
+ wholeDialogs.Add(fn);
- await GetChatCompletionsAsyncRecursively(chatCompletion,
- conversationId,
- agent,
- wholeDialogs,
- onMessageReceived,
- onFunctionExecuting,
- onFunctionExecuted);
+ await GetChatCompletionsAsyncRecursively(chatCompletion,
+ conversationId,
+ agent,
+ wholeDialogs,
+ onMessageReceived,
+ onFunctionExecuting,
+ onFunctionExecuted);
+ }
});
return result;
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
index 24376c21..d44eb56a 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs
@@ -2,6 +2,7 @@ using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
+using BotSharp.Abstraction.Routing.Settings;
using BotSharp.Core.Routing;
namespace BotSharp.Core.Conversations.Services;
@@ -33,8 +34,8 @@ public partial class ConversationService
stateService.Load();
stateService.SetState("channel", lastDialog.Channel);
- var router = _services.GetRequiredService();
- Agent agent = await router.LoadRouter();
+ var agentService = _services.GetRequiredService();
+ Agent agent = await agentService.LoadAgent(agentId);
_logger.LogInformation($"[{agent.Name}] {lastDialog.Role}: {lastDialog.Content}");
@@ -65,11 +66,21 @@ public partial class ConversationService
.SetConversation(converation);
await hook.OnDialogsLoaded(wholeDialogs);
- await hook.BeforeCompletion();
+ await hook.BeforeCompletion(lastDialog);
+
+ // Interrupted by hook
+ if (lastDialog.StopCompletion)
+ {
+ var response = new RoleDialogModel(AgentRole.Assistant, lastDialog.Content);
+ await onMessageReceived(response);
+ _storage.Append(conversationId, agent.Id, response);
+ return true;
+ }
}
// reasoning
- if (_settings.EnableReasoning)
+ var settings = _services.GetRequiredService();
+ if (settings.ReasonerId == agent.Id)
{
var simulator = _services.GetRequiredService();
var reasonedContext = await simulator.Enter(agent, wholeDialogs);
@@ -96,7 +107,6 @@ public partial class ConversationService
{
if (reasonedContext.CurrentAgentId != agent.Id)
{
- var agentService = _services.GetRequiredService();
agent = await agentService.LoadAgent(reasonedContext.CurrentAgentId);
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBasePlugin.cs b/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBasePlugin.cs
index e0c74e83..8f862ca4 100644
--- a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBasePlugin.cs
+++ b/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/KnowledgeBasePlugin.cs
@@ -1,3 +1,4 @@
+using BotSharp.Abstraction.Knowledges.Settings;
using BotSharp.Core.Plugins.Knowledges.Services;
using Microsoft.Extensions.Configuration;
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/Services/KnowledgeService.cs b/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/Services/KnowledgeService.cs
index 9ed87e6a..3a627ec9 100644
--- a/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/Services/KnowledgeService.cs
+++ b/src/Infrastructure/BotSharp.Core/Plugins/Knowledges/Services/KnowledgeService.cs
@@ -1,7 +1,7 @@
using BotSharp.Abstraction.Knowledges.Models;
+using BotSharp.Abstraction.Knowledges.Settings;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.VectorStorage;
-using System.Text.Json;
namespace BotSharp.Core.Plugins.Knowledges.Services;
diff --git a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs
index 60bc3d3d..1287a6d9 100644
--- a/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs
+++ b/src/Infrastructure/BotSharp.Core/Plugins/PluginLoader.cs
@@ -40,15 +40,8 @@ public class PluginLoader
foreach (var module in modules)
{
- if (_settings.Plugins.Contains(module.GetType().Name))
- {
- module.RegisterDI(_services, _config);
- Console.WriteLine($"Loaded plugin {module.GetType().Name} from {assemblyName}.", Color.Green);
- }
- else
- {
- Console.WriteLine($"Skipped plugin {module.GetType().Name} from {assemblyName}.", Color.Yellow);
- }
+ module.RegisterDI(_services, _config);
+ Console.WriteLine($"Loaded plugin {module.GetType().Name} from {assemblyName}.", Color.Green);
}
_modules.AddRange(modules);
@@ -62,7 +55,7 @@ public class PluginLoader
public void Configure(IApplicationBuilder app)
{
- if(_modules.Count == 0)
+ if (_modules.Count == 0)
{
Console.WriteLine($"No plugin loaded. Please check whether the Load() method is called.", Color.Yellow);
}
@@ -71,10 +64,7 @@ public class PluginLoader
{
if (module.GetType().GetInterface(nameof(IBotSharpAppPlugin)) != null)
{
- if (_settings.Plugins.Contains(module.GetType().Name))
- {
- (module as IBotSharpAppPlugin).Configure(app);
- }
+ (module as IBotSharpAppPlugin).Configure(app);
}
});
}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Reasoner.cs b/src/Infrastructure/BotSharp.Core/Routing/Reasoner.cs
index c08f83ac..182bedb6 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Reasoner.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Reasoner.cs
@@ -1,3 +1,5 @@
+using BotSharp.Abstraction.Routing.Settings;
+
namespace BotSharp.Core.Routing;
public class Reasoner : Router
@@ -6,7 +8,7 @@ public class Reasoner : Router
public Reasoner(IServiceProvider services,
ILogger logger,
- AgentSettings settings) : base(services, logger, settings)
+ RoutingSettings settings) : base(services, logger, settings)
{
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/Router.cs b/src/Infrastructure/BotSharp.Core/Routing/Router.cs
index 996e83bc..c92d26e4 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/Router.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/Router.cs
@@ -1,6 +1,7 @@
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Repositories;
using BotSharp.Abstraction.Routing.Models;
+using BotSharp.Abstraction.Routing.Settings;
using System.IO;
namespace BotSharp.Core.Routing;
@@ -9,13 +10,13 @@ public class Router : IAgentRouting
{
protected readonly IServiceProvider _services;
protected readonly ILogger _logger;
- protected readonly AgentSettings _settings;
+ protected readonly RoutingSettings _settings;
public virtual string AgentId => _settings.RouterId;
public Router(IServiceProvider services,
ILogger logger,
- AgentSettings settings)
+ RoutingSettings settings)
{
_services = services;
_logger = logger;
@@ -32,20 +33,24 @@ public class Router : IAgentRouting
{
var agentSettings = _services.GetRequiredService();
var dbSettings = _services.GetRequiredService();
- var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, agentSettings.RouterId, "route.json");
+ var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, _settings.RouterId, "route.json");
+ var records = JsonSerializer.Deserialize(File.ReadAllText(filePath));
- var db = _services.GetRequiredService();
- var agent = db.Agent.FirstOrDefault(x => x.Id == agentSettings.RouterId);
- var routes = agent?.Routes ?? new List();
- var routingRecords = new RoutingRecord[routes.Count];
-
- for (int i = 0; i < routes.Count; i++)
+ // check if routing profile is specified
+ filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, "routing-profile.json");
+ if (File.Exists(filePath))
{
- if (string.IsNullOrEmpty(routes[i])) continue;
- routingRecords[i] = JsonSerializer.Deserialize(routes[i]);
+ var state = _services.GetRequiredService();
+ var name = state.GetState("channel");
+ var profiles = JsonSerializer.Deserialize(File.ReadAllText(filePath));
+ var spcificedProfile = profiles.FirstOrDefault(x => x.Name == name);
+ if (spcificedProfile != null)
+ {
+ records = records.Where(x => spcificedProfile.AgentIds.Contains(x.AgentId)).ToArray();
+ }
}
- return routingRecords;
+ return records;
}
public RoutingRecord GetRecordByName(string name)
diff --git a/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs b/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs
new file mode 100644
index 00000000..cac30ff7
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/Templating/ResponseTemplateService.cs
@@ -0,0 +1,120 @@
+using BotSharp.Abstraction.Templating;
+using System.IO;
+using System.Reflection;
+
+namespace BotSharp.Core.Templating;
+
+public class ResponseTemplateService : IResponseTemplateService
+{
+ private readonly IServiceProvider _services;
+ private readonly ILogger _logger;
+
+ public ResponseTemplateService(IServiceProvider services, ILogger logger)
+ {
+ _services = services;
+ _logger = logger;
+ }
+
+ public async Task RenderFunctionResponse(string agentId, RoleDialogModel message)
+ {
+ // Find response template
+ var agentService = _services.GetRequiredService();
+ var dir = Path.Combine(agentService.GetAgentDataDir(agentId), "responses");
+ var responses = Directory.GetFiles(dir)
+ .Where(f => f.Split(Path.DirectorySeparatorChar).Last().Split('.')[1] == message.FunctionName)
+ .ToList();
+
+ if (responses.Count == 0)
+ {
+ return string.Empty;
+ }
+
+ var randomIndex = new Random().Next(0, responses.Count);
+ var template = File.ReadAllText(responses[randomIndex]);
+
+ var render = _services.GetRequiredService();
+
+ // Convert args and execute data to dictionary
+ var dict = new Dictionary();
+
+ if (message.FunctionArgs != null)
+ {
+ ExtractArgs(JsonSerializer.Deserialize(message.FunctionArgs), dict);
+ }
+
+ if (message.ExecutionData != null)
+ {
+ ExtractExecuteData(message.ExecutionData, dict);
+ }
+
+ var text = render.Render(template, dict);
+
+ return text;
+ }
+
+ public async Task RenderIntentResponse(string agentId, RoleDialogModel message)
+ {
+ // Find response template
+ var agentService = _services.GetRequiredService();
+ var dir = Path.Combine(agentService.GetAgentDataDir(agentId), "responses");
+ if (!Directory.Exists(dir))
+ {
+ return string.Empty;
+ }
+ var responses = Directory.GetFiles(dir)
+ .Where(f => f.Split(Path.DirectorySeparatorChar).Last().Split('.')[1] == message.IntentName)
+ .ToList();
+
+ if (responses.Count == 0)
+ {
+ return string.Empty;
+ }
+
+ var randomIndex = new Random().Next(0, responses.Count);
+ var template = File.ReadAllText(responses[randomIndex]);
+
+ var render = _services.GetRequiredService();
+
+ // Convert args and execute data to dictionary
+ var dict = new Dictionary();
+ if (!string.IsNullOrEmpty(message.FunctionArgs))
+ {
+ ExtractArgs(JsonSerializer.Deserialize(message.FunctionArgs), dict);
+ }
+
+ if (message.ExecutionData != null)
+ {
+ ExtractExecuteData(message.ExecutionData, dict);
+ }
+
+ var text = render.Render(template, dict);
+
+ return text;
+ }
+
+ private void ExtractArgs(JsonDocument args, Dictionary dict)
+ {
+ if (args.RootElement is JsonElement root)
+ {
+ foreach (JsonProperty property in root.EnumerateObject())
+ {
+ if (!string.IsNullOrEmpty(property.Value.ToString()))
+ {
+ dict[property.Name] = property.Value.ToString();
+ }
+ }
+ }
+ }
+
+ private void ExtractExecuteData(object data, Dictionary dict)
+ {
+ foreach (PropertyInfo property in data.GetType().GetProperties())
+ {
+ var value = property.GetValue(data, null);
+ if (value != null)
+ {
+ dict[property.Name] = value;
+ }
+ }
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs
index 97263119..d268db4c 100644
--- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs
+++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs
@@ -1,8 +1,6 @@
-using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Templating;
using Fluid;
-using Microsoft.Extensions.Options;
namespace BotSharp.Core.Templating;
@@ -22,19 +20,18 @@ public class TemplateRender : ITemplateRender
_options.MemberAccessStrategy.Register();
}
- public bool Render(Agent agent, Dictionary dict)
+ public string Render(string template, Dictionary dict)
{
- var template = agent.Instruction;
if (_parser.TryParse(template, out var t, out var error))
{
var context = new TemplateContext(dict, _options);
- agent.Instruction = t.Render(context);
- return true;
+ template = t.Render(context);
+ return template;
}
else
{
- return false;
+ return template;
}
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs
index 0c26934d..6349e6f7 100644
--- a/src/Infrastructure/BotSharp.Core/Using.cs
+++ b/src/Infrastructure/BotSharp.Core/Using.cs
@@ -13,11 +13,12 @@ global using BotSharp.Abstraction.Conversations;
global using BotSharp.Abstraction.Knowledges;
global using BotSharp.Abstraction.Users;
global using BotSharp.Abstraction.Utilities;
+global using BotSharp.Abstraction.Conversations.Models;
+global using BotSharp.Abstraction.Agents.Settings;
+global using BotSharp.Abstraction.Conversations.Settings;
global using BotSharp.Core.Repository;
global using BotSharp.Core.Agents.Services;
global using BotSharp.Core.Conversations.Services;
global using BotSharp.Core.Infrastructures;
global using BotSharp.Core.Plugins;
-global using BotSharp.Core.Users.Services;
-global using BotSharp.Abstraction.Agents.Settings;
-global using BotSharp.Abstraction.Conversations.Settings;
\ No newline at end of file
+global using BotSharp.Core.Users.Services;
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
index 7b8290d6..316f4488 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
+++ b/src/Infrastructure/BotSharp.OpenAPI/BotSharp.OpenAPI.csproj
@@ -1,10 +1,10 @@
-
+
net6.0
enable
enable
- 0.9.0
+ 0.10.0
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
index 026a7874..364416cd 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
@@ -40,7 +40,8 @@ public class ConversationController : ControllerBase, IApiAdapter
[HttpPost("/conversation/{agentId}/{conversationId}")]
public async Task SendMessage([FromRoute] string agentId,
[FromRoute] string conversationId,
- [FromBody] NewMessageModel input)
+ [FromBody] NewMessageModel input,
+ [FromQuery] string? channel = "openapi")
{
var conv = _services.GetRequiredService();
@@ -50,7 +51,7 @@ public class ConversationController : ControllerBase, IApiAdapter
await conv.SendMessage(agentId, conversationId,
new RoleDialogModel("user", input.Text)
{
- Channel = "webapi"
+ Channel = channel
},
async msg =>
{
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs
index 3824f476..bea5597b 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeController.cs
@@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Http;
using UglyToad.PdfPig.Content;
using UglyToad.PdfPig;
using BotSharp.Core.Plugins.Knowledges;
-
+using BotSharp.Abstraction.Knowledges.Settings;
namespace BotSharp.OpenAPI.Controllers;
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj b/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj
index dfc28a95..05ba639e 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/BotSharp.Plugin.AzureOpenAI.csproj
@@ -4,11 +4,11 @@
netstandard2.1
enable
10
- 0.9.0
+ 0.10.0
-
+
diff --git a/src/Plugins/BotSharp.Plugin.ChatbotUI/BotSharp.Plugin.ChatbotUI.csproj b/src/Plugins/BotSharp.Plugin.ChatbotUI/BotSharp.Plugin.ChatbotUI.csproj
index e9e233a9..d69c44a5 100644
--- a/src/Plugins/BotSharp.Plugin.ChatbotUI/BotSharp.Plugin.ChatbotUI.csproj
+++ b/src/Plugins/BotSharp.Plugin.ChatbotUI/BotSharp.Plugin.ChatbotUI.csproj
@@ -4,7 +4,7 @@
netstandard2.1
enable
10
- 0.9.0
+ 0.10.0
diff --git a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs
index d6e1824d..3388f04c 100644
--- a/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.MetaAI/Providers/fastTextEmbeddingProvider.cs
@@ -15,10 +15,7 @@ public class fastTextEmbeddingProvider : ITextEmbedding
{
get
{
- if (!_fastText.IsModelReady())
- {
- _fastText.LoadModel(_settings.ModelPath);
- }
+ LoadModel();
return _fastText.GetModelDimension();
}
}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj
new file mode 100644
index 00000000..fb145e8d
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/BotSharp.Plugin.RoutingSpeeder.csproj
@@ -0,0 +1,18 @@
+
+
+
+ netstandard2.1
+ enable
+ 10
+ 0.11.0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs
new file mode 100644
index 00000000..3c3a33f2
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/IntentClassifier.cs
@@ -0,0 +1,258 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+using Tensorflow;
+using static Tensorflow.KerasApi;
+using Tensorflow.Keras.Engine;
+using Tensorflow.NumPy;
+using static Tensorflow.Binding;
+using Tensorflow.Keras.Callbacks;
+using System.Text.RegularExpressions;
+using BotSharp.Plugin.RoutingSpeeder.Settings;
+using BotSharp.Abstraction.MLTasks;
+using BotSharp.Plugin.RoutingSpeeder.Providers.Models;
+using Microsoft.Extensions.DependencyInjection;
+using System.Linq;
+using Tensorflow.Keras;
+using BotSharp.Abstraction.Knowledges.Settings;
+using System.Numerics;
+using Newtonsoft.Json;
+using Tensorflow.Keras.Layers;
+using BotSharp.Abstraction.Agents;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Providers;
+
+public class IntentClassifier
+{
+ private readonly IServiceProvider _services;
+ Model _model;
+ public Model model => _model;
+ private bool _isModelReady;
+ public bool isModelReady => _isModelReady;
+ private ClassifierSetting _settings;
+
+ public IntentClassifier(IServiceProvider services, ClassifierSetting settings)
+ {
+ _services = services;
+ _settings = settings;
+ }
+
+ private void Reset()
+ {
+ keras.backend.clear_session();
+ _isModelReady = false;
+ }
+
+ private void Build()
+ {
+ if (_isModelReady)
+ {
+ return;
+ }
+
+ var vector = _services.GetRequiredService();
+
+ var labels = GetLabels();
+
+ var layers = new List
+ {
+ keras.layers.InputLayer((vector.Dimension), name: "Input"),
+ keras.layers.Dense(256, activation:"relu"),
+ keras.layers.Dense(256, activation:"relu"),
+ keras.layers.Dense(labels.Length, activation: keras.activations.Softmax)
+ };
+ _model = keras.Sequential(layers);
+
+#if DEBUG
+ Console.WriteLine();
+ _model.summary();
+#endif
+ _isModelReady = true;
+ }
+
+ private void Fit(NDArray x, NDArray y, TrainingParams trainingParams)
+ {
+ _model.compile(optimizer: keras.optimizers.Adam(trainingParams.LearningRate),
+ loss: keras.losses.SparseCategoricalCrossentropy(),
+ metrics: new[] { "accuracy" }
+ );
+
+ CallbackParams callback_parameters = new CallbackParams
+ {
+ Model = _model,
+ Epochs = trainingParams.Epochs,
+ Verbose = 1,
+ Steps = 10
+ };
+
+ ICallback earlyStop = new EarlyStopping(callback_parameters, "accuracy");
+
+ var callbacks = new List() { earlyStop };
+
+ var weights = LoadWeights();
+
+ _model.fit(x, y,
+ batch_size: trainingParams.BatchSize,
+ epochs: trainingParams.Epochs,
+ callbacks: callbacks,
+ // validation_split: 0.1f,
+ shuffle: true);
+
+ _model.save_weights(weights);
+
+ _isModelReady = true;
+ }
+
+ public string LoadWeights()
+ {
+ var agentService = _services.CreateScope().ServiceProvider.GetRequiredService();
+
+ var weightsFile = Path.Combine(agentService.GetDataDir(), _settings.MODEL_DIR, $"intent-classifier.h5");
+ if (File.Exists(weightsFile))
+ {
+ _model.load_weights(weightsFile);
+ _isModelReady = true;
+ Console.WriteLine($"Successfully load the weights!");
+ }
+ else
+ {
+ Console.WriteLine("No available weights.");
+ }
+ return weightsFile;
+ }
+
+ public (NDArray x, NDArray y) Vectorize(List items)
+ {
+ var vector = _services.GetRequiredService();
+
+ var x = np.zeros((items.Count, vector.Dimension), dtype: np.float32);
+ var y = np.zeros((items.Count, 1), dtype: np.float32);
+
+ for (int i = 0; i < items.Count; i++)
+ {
+ x[i] = vector.GetVector(TextClean(items[i].text));
+ if (_settings.LabelMappingDict.ContainsKey(items[i].label))
+ {
+ y[i] = _settings.LabelMappingDict[items[i].label];
+ }
+ }
+ return (x, y);
+ }
+
+ public NDArray GetTextEmbedding(string text)
+ {
+ var knowledgeSettings = _services.GetRequiredService();
+ var embedding = _services.GetServices()
+ .FirstOrDefault(x => x.GetType().FullName.EndsWith(knowledgeSettings.TextEmbedding));
+
+ var x = np.zeros((1, embedding.Dimension), dtype: np.float32);
+ x[0] = embedding.GetVector(text);
+ return x;
+ }
+
+ public (NDArray, NDArray) PrepareLoadData()
+ {
+ var agentService = _services.CreateScope().ServiceProvider.GetRequiredService();
+ string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.RAW_DATA_DIR);
+ string saveLabelDirectory = Path.Combine(agentService.GetDataDir(), _settings.MODEL_DIR, _settings.LABEL_FILE_NAME);
+
+ if (!Directory.Exists(rootDirectory))
+ {
+ throw new Exception($"No training data found! Please put training data in this path: {rootDirectory}");
+ }
+
+ var vector = _services.GetRequiredService();
+
+
+ var vectorList = new List();
+
+ var labelList = new List();
+ foreach (var filePath in GetFiles())
+ {
+ var texts = File.ReadAllLines(filePath, Encoding.UTF8).Select(x => TextClean(x)).ToList();
+ vectorList.AddRange(vector.GetVectors(texts));
+ string fileName = Path.GetFileNameWithoutExtension(filePath);
+ labelList.AddRange(Enumerable.Repeat(fileName, texts.Count).ToList());
+ }
+
+ // Write label into local file
+ var uniqueLabelList = labelList.Distinct().OrderBy(x => x).ToArray();
+ File.WriteAllLines(saveLabelDirectory, uniqueLabelList);
+
+ var x = np.zeros((vectorList.Count, vector.Dimension), dtype: np.float32);
+ var y = np.zeros((vectorList.Count, 1), dtype: np.float32);
+
+ for (int i = 0; i < vectorList.Count; i++)
+ {
+ x[i] = vectorList[i];
+ // y[i] = (float)uniqueLabelList.IndexOf(labelList[i]);
+ y[i] = (float)Array.IndexOf(uniqueLabelList, labelList[i]);
+ }
+ return (x, y);
+ }
+
+ public string[] GetFiles()
+ {
+ var agentService = _services.CreateScope().ServiceProvider.GetRequiredService();
+ string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.RAW_DATA_DIR);
+ return Directory.GetFiles(rootDirectory).OrderBy(x => x).ToArray();
+ }
+
+ public string[] GetLabels()
+ {
+ var agentService = _services.CreateScope().ServiceProvider.GetRequiredService();
+ string rootDirectory = Path.Combine(agentService.GetDataDir(), _settings.MODEL_DIR, _settings.LABEL_FILE_NAME);
+ var labelText = File.ReadAllLines(rootDirectory);
+ return labelText.OrderBy(x => x).ToArray();
+ }
+
+ public string TextClean(string text)
+ {
+ // Remove punctuation
+ // Remove digits
+ // To lowercase
+ var processedText = Regex.Replace(text, "[AB0-9]", " ");
+ processedText = string.Join("", processedText.Select(c => char.IsPunctuation(c) ? ' ' : c).ToList());
+ processedText = processedText.Replace(" ", " ").ToLower();
+ return processedText;
+ }
+
+ public string Predict(NDArray vector, float confidenceScore = 0.9f)
+ {
+ if (!_isModelReady)
+ {
+ InitClassifer();
+ }
+
+ var prob = _model.predict(vector).numpy();
+
+ var probLabel = tf.arg_max(prob, -1).numpy().ToArray();
+ prob = np.squeeze(prob, axis: 0);
+
+ if (prob[probLabel[0]] < confidenceScore)
+ {
+ return string.Empty;
+ }
+
+ var prediction = GetLabels()[probLabel[0]];
+
+ return prediction;
+ }
+ public void InitClassifer()
+ {
+ Reset();
+ Build();
+ LoadWeights();
+ }
+
+ public void Train()
+ {
+ var trainingParams = new TrainingParams();
+ Reset();
+ (var x, var y) = PrepareLoadData();
+ Build();
+ Fit(x, y, trainingParams);
+
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/DialoguePredictionModel.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/DialoguePredictionModel.cs
new file mode 100644
index 00000000..4641b9cd
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/DialoguePredictionModel.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Providers.Models;
+
+public class DialoguePredictionModel
+{
+ public int Id { get; set; }
+ public string text { get; set; }
+ public string? label { get; set; }
+ public string? prediction { get; set; }
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/TrainingParams.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/TrainingParams.cs
new file mode 100644
index 00000000..f3c822ac
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Providers/Models/TrainingParams.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Providers.Models;
+
+public class TrainingParams
+{
+ public int ClientId { get; set; }
+ public int Epochs { get; set; } = 10;
+ public int BatchSize { get; set; } = 16;
+ public float LearningRate { get; set; } = 1.0e-4f;
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
new file mode 100644
index 00000000..869afe94
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingConversationHook.cs
@@ -0,0 +1,75 @@
+using BotSharp.Abstraction.Agents.Enums;
+using BotSharp.Abstraction.Agents.Models;
+using BotSharp.Abstraction.Conversations;
+using BotSharp.Abstraction.Conversations.Models;
+using BotSharp.Abstraction.MLTasks;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using BotSharp.Plugin.RoutingSpeeder.Settings;
+using BotSharp.Abstraction.Templating;
+using BotSharp.Plugin.RoutingSpeeder.Providers;
+using System.Runtime.InteropServices;
+using BotSharp.Abstraction.Agents;
+using System.IO;
+using BotSharp.Abstraction.Routing.Settings;
+
+namespace BotSharp.Plugin.RoutingSpeeder;
+
+public class RoutingConversationHook: ConversationHookBase
+{
+ private readonly IServiceProvider _services;
+ private RouterSpeederSettings _settings;
+ public RoutingConversationHook(IServiceProvider service, RouterSpeederSettings settings)
+ {
+ _services = service;
+ _settings = settings;
+ }
+ public override async Task BeforeCompletion(RoleDialogModel message)
+ {
+ var intentClassifier = _services.GetRequiredService();
+ var vector = intentClassifier.GetTextEmbedding(message.Content);
+
+ // intentClassifier.Train();
+ // Utilize local discriminative model to predict intent
+ var predText = intentClassifier.Predict(vector);
+
+ message.IntentName = predText;
+
+ // Render by template
+ var templateService = _services.GetRequiredService();
+ var response = await templateService.RenderIntentResponse(_agent.Id, message);
+
+ if (!string.IsNullOrEmpty(response))
+ {
+ message.Content = response;
+ message.StopCompletion = true;
+ }
+ }
+
+ public override async Task AfterCompletion(RoleDialogModel message)
+ {
+ var routerSettings = _services.GetRequiredService();
+ bool saveFlag = (message.CurrentAgentId != routerSettings.RouterId) && (message.CurrentAgentId != routerSettings.ReasonerId);
+
+ if (saveFlag)
+ {
+ // save train data
+ var agentService = _services.CreateScope().ServiceProvider.GetRequiredService();
+ var rootDataPath = agentService.GetDataDir();
+
+ string rawDataDir = Path.Combine(rootDataPath, "raw_data", $"{message.CurrentAgentId}.txt");
+ var lastThreeDialogs = _dialogs.Where(x => x.Role == AgentRole.User).Select(x => x.Content).Reverse().Take(3).ToArray();
+
+ if (!File.Exists(rawDataDir))
+ {
+ await File.WriteAllLinesAsync(rawDataDir, lastThreeDialogs);
+ }
+ else
+ {
+ await File.AppendAllLinesAsync(rawDataDir, lastThreeDialogs);
+ }
+ }
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs
new file mode 100644
index 00000000..c3dac3a2
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/RoutingSpeederPlugin.cs
@@ -0,0 +1,24 @@
+using BotSharp.Abstraction.Conversations;
+using BotSharp.Abstraction.MLTasks;
+using BotSharp.Abstraction.Plugins;
+using BotSharp.Plugin.RoutingSpeeder.Settings;
+using BotSharp.Plugin.RoutingSpeeder.Providers;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace BotSharp.Plugin.RoutingSpeeder;
+
+public class RoutingSpeederPlugin : IBotSharpPlugin
+{
+ public void RegisterDI(IServiceCollection services, IConfiguration config)
+ {
+ var settings = new RouterSpeederSettings();
+ config.Bind("RouterSpeeder", settings);
+ services.AddSingleton(x => settings);
+
+ services.AddSingleton();
+
+ services.AddScoped();
+ services.AddSingleton();
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
new file mode 100644
index 00000000..09bda6e8
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/classifierSetting.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Settings;
+
+public class ClassifierSetting
+{
+ public Dictionary LabelMappingDict { get; set; } = new Dictionary()
+ {
+ {"goodbye", 0f},
+ {"greeting", 1f},
+ {"other", 2f}
+ };
+
+ public string RAW_DATA_DIR { get; set; } = "raw_data";
+ public string MODEL_DIR { get; set; } = "models";
+ public string LABEL_FILE_NAME { get; set; } = "label.txt";
+}
diff --git a/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs
new file mode 100644
index 00000000..fbedf581
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.RoutingSpeeder/Settings/routerSpeedSettings.cs
@@ -0,0 +1,9 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BotSharp.Plugin.RoutingSpeeder.Settings;
+
+public class RouterSpeederSettings
+{
+}
diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj
index e8ef139f..0cb02ead 100644
--- a/src/WebStarter/WebStarter.csproj
+++ b/src/WebStarter/WebStarter.csproj
@@ -34,6 +34,7 @@
+
@@ -43,6 +44,7 @@
+
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index 9657f072..72502bf7 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -13,14 +13,20 @@
"Key": "31ba6052aa6f4569901facc3a41fcb4a"
},
+ "Router": {
+ "RouterId": "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a",
+ "ReasonerId": "03d3fb55-9ada-423b-a6b4-f9ecddf4b26e"
+ },
+
"Agent": {
- "RouterId": "",
"DataDir": "agents",
- "TemplateFormat": "liquid"
+ "TemplateFormat": "liquid",
+ "MaxRecursiveDepth": 3
},
"Conversation": {
"DataDir": "conversations",
+ "ShowVerboseLog": false,
"ChatCompletion": "AzureOpenAI.Providers.ChatCompletionProvider"
// "ChatCompletion": "LLamaSharp.ChatCompletionProvider"
},
@@ -43,10 +49,13 @@
"MetaAi": {
"fastText": {
- "ModelPath": "crawl-300d-2M-subword.bin"
+ "ModelPath": "dbpedia.ftz"
}
},
+ "RoutingSpeeder": {
+ },
+
"MetaMessenger": {
"Endpoint": "https://graph.facebook.com",
"ApiVersion": "v17.0",
@@ -98,17 +107,9 @@
"BotSharp.Plugin.MetaAI",
"BotSharp.Plugin.Qdrant",
"BotSharp.Plugin.PaddleSharp",
- "BotSharp.Plugin.WeChat"
- ],
- "Plugins": [
- "KnowledgeBasePlugin",
- "MemVecDbPlugin",
- "LLamaSharpPlugin",
- "AzureOpenAiPlugin",
- "MetaAiPlugin",
- "QdrantPlugin",
- "PaddleSharpPlugin",
- "WeChatPlugin"
+ "BotSharp.Plugin.WeChat",
+ "BotSharp.Plugin.RoutingSpeeder",
+ "BotSharp.Plugin.PizzaBot"
]
}
}
diff --git a/src/WebStarter/data/models/dbpedia.ftz b/src/WebStarter/data/models/dbpedia.ftz
new file mode 100644
index 00000000..8f649cc9
Binary files /dev/null and b/src/WebStarter/data/models/dbpedia.ftz differ
diff --git a/src/WebStarter/data/models/intent-classifier.h5 b/src/WebStarter/data/models/intent-classifier.h5
new file mode 100644
index 00000000..13f2ebed
Binary files /dev/null and b/src/WebStarter/data/models/intent-classifier.h5 differ
diff --git a/src/WebStarter/data/models/label.txt b/src/WebStarter/data/models/label.txt
new file mode 100644
index 00000000..03fb5a19
--- /dev/null
+++ b/src/WebStarter/data/models/label.txt
@@ -0,0 +1,3 @@
+goodbye
+greeting
+other
diff --git a/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj b/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj
new file mode 100644
index 00000000..a451378c
--- /dev/null
+++ b/tests/BotSharp.Plugin.PizzaBot/BotSharp.Plugin.PizzaBot.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netstandard2.1
+ enable
+ 10
+
+
+
+
+
+
+
diff --git a/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaPricesFn.cs b/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaPricesFn.cs
new file mode 100644
index 00000000..625e9ec4
--- /dev/null
+++ b/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaPricesFn.cs
@@ -0,0 +1,19 @@
+using BotSharp.Abstraction.Conversations.Models;
+using System.Text.Json;
+
+namespace BotSharp.Plugin.PizzaBot.Functions;
+
+public class GetPizzaPricesFn : IFunctionCallback
+{
+ public string Name => "get_pizza_price";
+
+ public async Task Execute(RoleDialogModel message)
+ {
+ message.ExecutionData = new
+ {
+ cheese = "3.5"
+ };
+ message.ExecutionResult = "Pepperoni Pizza: $3.5/slice, Cheese Pizza: $2.5/slice, Margherita Pizza: $3.0/slice";
+ return true;
+ }
+}
diff --git a/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaTypesFn.cs b/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaTypesFn.cs
new file mode 100644
index 00000000..0e505d09
--- /dev/null
+++ b/tests/BotSharp.Plugin.PizzaBot/Functions/GetPizzaTypesFn.cs
@@ -0,0 +1,14 @@
+using BotSharp.Abstraction.Conversations.Models;
+
+namespace BotSharp.Plugin.PizzaBot.Functions;
+
+public class GetPizzaTypesFn : IFunctionCallback
+{
+ public string Name => "get_pizza_types";
+
+ public async Task Execute(RoleDialogModel message)
+ {
+ message.ExecutionResult = "Pepperoni Pizza, Cheese Pizza, Margherita Pizza";
+ return true;
+ }
+}
diff --git a/tests/BotSharp.Plugin.PizzaBot/Functions/MakeOrderFn.cs b/tests/BotSharp.Plugin.PizzaBot/Functions/MakeOrderFn.cs
new file mode 100644
index 00000000..68405157
--- /dev/null
+++ b/tests/BotSharp.Plugin.PizzaBot/Functions/MakeOrderFn.cs
@@ -0,0 +1,14 @@
+using BotSharp.Abstraction.Conversations.Models;
+
+namespace BotSharp.Plugin.PizzaBot.Functions;
+
+public class MakeOrderFn : IFunctionCallback
+{
+ public string Name => "make_order";
+
+ public async Task Execute(RoleDialogModel message)
+ {
+ message.ExecutionResult = "The order number is P123-01";
+ return true;
+ }
+}
diff --git a/tests/BotSharp.Plugin.PizzaBot/Functions/OrderFoundFn.cs b/tests/BotSharp.Plugin.PizzaBot/Functions/OrderFoundFn.cs
new file mode 100644
index 00000000..90a85b50
--- /dev/null
+++ b/tests/BotSharp.Plugin.PizzaBot/Functions/OrderFoundFn.cs
@@ -0,0 +1,14 @@
+using BotSharp.Abstraction.Conversations.Models;
+
+namespace BotSharp.Plugin.PizzaBot.Functions;
+
+public class OrderFoundFn : IFunctionCallback
+{
+ public string Name => "order_found";
+
+ public async Task Execute(RoleDialogModel message)
+ {
+ message.ExecutionResult = "The order number is P123-01";
+ return true;
+ }
+}
diff --git a/tests/BotSharp.Plugin.PizzaBot/Hooks/PizzaBotAgentHook.cs b/tests/BotSharp.Plugin.PizzaBot/Hooks/PizzaBotAgentHook.cs
new file mode 100644
index 00000000..be1bbe6f
--- /dev/null
+++ b/tests/BotSharp.Plugin.PizzaBot/Hooks/PizzaBotAgentHook.cs
@@ -0,0 +1,17 @@
+namespace BotSharp.Plugin.PizzaBot.Hooks;
+
+public class PizzaBotAgentHook : AgentHookBase
+{
+ public PizzaBotAgentHook(IServiceProvider services, AgentSettings settings)
+ : base(services, settings)
+ {
+ }
+
+ public override bool OnInstructionLoaded(string template, Dictionary dict)
+ {
+ dict["current_date"] = $"{DateTime.Now:MMM dd, yyyy}";
+ dict["current_time"] = $"{DateTime.Now:hh:mm t}";
+ dict["current_weekday"] = $"{DateTime.Now:dddd}";
+ return true;
+ }
+}
diff --git a/tests/BotSharp.Plugin.PizzaBot/PizzaBotPlugin.cs b/tests/BotSharp.Plugin.PizzaBot/PizzaBotPlugin.cs
new file mode 100644
index 00000000..87b2c7ae
--- /dev/null
+++ b/tests/BotSharp.Plugin.PizzaBot/PizzaBotPlugin.cs
@@ -0,0 +1,19 @@
+using BotSharp.Abstraction.Agents;
+using BotSharp.Plugin.PizzaBot.Hooks;
+
+namespace BotSharp.Plugin.PizzaBot;
+
+public class PizzaBotPlugin : IBotSharpPlugin
+{
+ public void RegisterDI(IServiceCollection services, IConfiguration config)
+ {
+ // Register callback function
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ // Register hooks
+ services.AddScoped();
+ }
+}
diff --git a/tests/BotSharp.Plugin.PizzaBot/Using.cs b/tests/BotSharp.Plugin.PizzaBot/Using.cs
new file mode 100644
index 00000000..e1f3009b
--- /dev/null
+++ b/tests/BotSharp.Plugin.PizzaBot/Using.cs
@@ -0,0 +1,12 @@
+global using System;
+global using System.Collections.Generic;
+global using System.Text;
+global using System.Threading.Tasks;
+global using System.ComponentModel.DataAnnotations;
+global using Microsoft.Extensions.Configuration;
+global using Microsoft.Extensions.DependencyInjection;
+global using BotSharp.Abstraction.Plugins;
+global using BotSharp.Abstraction.Functions;
+global using BotSharp.Plugin.PizzaBot.Functions;
+global using BotSharp.Abstraction.Agents.Settings;
+global using BotSharp.Core.Agents.Services;
\ No newline at end of file