From 3da73d6fce84f63718ec8b7e1d96ce33eab63170 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Mon, 30 Jun 2025 23:07:58 -0500
Subject: [PATCH 01/51] init
---
BotSharp.sln | 11 +++
.../Messaging/BotSharpMessageParser.cs | 4 +
.../Messaging/Enums/RichTypeEnum.cs | 2 +
.../Template/JsCodeTemplateMessage.cs | 10 +++
.../BotSharp.Plugin.ChartHandler.csproj | 29 +++++++
.../ChartHandlerPlugin.cs | 18 +++++
.../Enums/UtilityName.cs | 6 ++
.../Functions/GenerateChartFn.cs | 80 +++++++++++++++++++
.../Hooks/ChartHandlerUtilityHook.cs | 24 ++++++
.../LlmContext/LlmContextIn.cs | 10 +++
.../BotSharp.Plugin.ChartHandler/Using.cs | 32 ++++++++
.../functions/util-chart-generate_chart.json | 14 ++++
.../util-chart-generate_chart.fn.liquid | 1 +
.../util-chart-plot-instruction.liquid | 9 +++
.../BotSharp.Plugin.FileHandler.csproj | 6 +-
.../LlmContexts/LlmContextIn.cs | 4 -
src/WebStarter/WebStarter.csproj | 3 +-
src/WebStarter/appsettings.json | 1 +
18 files changed, 254 insertions(+), 10 deletions(-)
create mode 100644 src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/JsCodeTemplateMessage.cs
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/BotSharp.Plugin.ChartHandler.csproj
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/ChartHandlerPlugin.cs
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/Enums/UtilityName.cs
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/Functions/GenerateChartFn.cs
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/Hooks/ChartHandlerUtilityHook.cs
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/LlmContext/LlmContextIn.cs
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/Using.cs
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-chart-generate_chart.json
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-generate_chart.fn.liquid
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid
diff --git a/BotSharp.sln b/BotSharp.sln
index 972dc35f..4aa709de 100644
--- a/BotSharp.sln
+++ b/BotSharp.sln
@@ -143,6 +143,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.LLM.Tests", "tests
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Test.RealtimeVoice", "tests\BotSharp.Test.RealtimeVoice\BotSharp.Test.RealtimeVoice.csproj", "{B067B126-88CD-4282-BEEF-7369B64423EF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.ChartHandler", "src\Plugins\BotSharp.Plugin.ChartHandler\BotSharp.Plugin.ChartHandler.csproj", "{0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -599,6 +601,14 @@ Global
{B067B126-88CD-4282-BEEF-7369B64423EF}.Release|Any CPU.Build.0 = Release|Any CPU
{B067B126-88CD-4282-BEEF-7369B64423EF}.Release|x64.ActiveCfg = Release|Any CPU
{B067B126-88CD-4282-BEEF-7369B64423EF}.Release|x64.Build.0 = Release|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Debug|x64.Build.0 = Debug|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x64.ActiveCfg = Release|Any CPU
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -668,6 +678,7 @@ Global
{7D0DB012-9798-4BB9-B15B-A5B0B7B3B094} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
{7C0C7D13-D161-4AB0-9C29-83A0F1FF990E} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
{B067B126-88CD-4282-BEEF-7369B64423EF} = {32FAFFFE-A4CB-4FEE-BF7C-84518BBC6DCC}
+ {0428DEAA-E4FE-4259-A6D8-6EDD1A9D0702} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs
index 1f495a44..57db70d7 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs
@@ -38,6 +38,10 @@ public static class BotSharpMessageParser
{
targetType = typeof(TextMessage);
}
+ else if (richType == RichTypeEnum.JsCode)
+ {
+ targetType = typeof(JsCodeTemplateMessage);
+ }
else if (richType == RichTypeEnum.GenericTemplate)
{
if (root.TryGetProperty("element_type", out element))
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs
index 7603f790..6227f522 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs
@@ -9,4 +9,6 @@ public static class RichTypeEnum
public const string QuickReply = "quick_reply";
public const string Text = "text";
public const string Attachment = "attachment";
+
+ public const string JsCode = "js_code";
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/JsCodeTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/JsCodeTemplateMessage.cs
new file mode 100644
index 00000000..1d58546b
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/JsCodeTemplateMessage.cs
@@ -0,0 +1,10 @@
+namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template;
+
+public class JsCodeTemplateMessage : IRichMessage
+{
+ [JsonPropertyName("rich_type")]
+ public string RichType => RichTypeEnum.JsCode;
+
+ [JsonPropertyName("text")]
+ public string Text { get; set; } = string.Empty;
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/BotSharp.Plugin.ChartHandler.csproj b/src/Plugins/BotSharp.Plugin.ChartHandler/BotSharp.Plugin.ChartHandler.csproj
new file mode 100644
index 00000000..8051876c
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/BotSharp.Plugin.ChartHandler.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+
+
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/ChartHandlerPlugin.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/ChartHandlerPlugin.cs
new file mode 100644
index 00000000..1161f6e4
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/ChartHandlerPlugin.cs
@@ -0,0 +1,18 @@
+using Microsoft.Extensions.Configuration;
+
+namespace BotSharp.Plugin.ChartHandler;
+
+public class ChartHandlerPlugin : IBotSharpPlugin
+{
+ public string Id => "9dacac1d-2e29-4f01-9d66-b0201f05a9fa";
+ public string Name => "Chart Plotter";
+ public string Description => "AI plots chart";
+ public string IconUrl => "https://cdn-icons-png.flaticon.com/512/423/423786.png";
+ public string[] AgentIds => [];
+
+ public void RegisterDI(IServiceCollection services, IConfiguration config)
+ {
+ services.AddScoped();
+ }
+
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Enums/UtilityName.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Enums/UtilityName.cs
new file mode 100644
index 00000000..a93a17e5
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Enums/UtilityName.cs
@@ -0,0 +1,6 @@
+namespace BotSharp.Plugin.ChartHandler.Enums;
+
+public class UtilityName
+{
+ public const string ChartGenerator = "chart-generator";
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/GenerateChartFn.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/GenerateChartFn.cs
new file mode 100644
index 00000000..c7e66f1f
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/GenerateChartFn.cs
@@ -0,0 +1,80 @@
+using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
+
+namespace BotSharp.Plugin.ChartHandler.Functions;
+
+public class GenerateChartFn : IFunctionCallback
+{
+ private readonly IServiceProvider _services;
+ private readonly ILogger _logger;
+
+ public string Name => "util-chart-generate_chart";
+ public string Indication => "Generating chart";
+
+
+ public GenerateChartFn(
+ IServiceProvider services,
+ ILogger logger)
+ {
+ _services = services;
+ _logger = logger;
+ }
+
+ public async Task Execute(RoleDialogModel message)
+ {
+ var db = _services.GetRequiredService();
+ var agentService = _services.GetRequiredService();
+ var convService = _services.GetRequiredService();
+
+ var args = JsonSerializer.Deserialize(message.FunctionArgs);
+ var agent = await agentService.GetAgent(message.CurrentAgentId);
+ var inst = db.GetAgentTemplate(BuiltInAgentId.UtilityAssistant, "util-chart-plot-instruction");
+ var innerAgent = new Agent
+ {
+ Id = agent.Id,
+ Name = agent.Name,
+ Instruction = inst,
+ TemplateDict = new Dictionary
+ {
+ { "plotting_requirement", args?.PlottingRequirement ?? string.Empty },
+ { "chart_element_id", $"chart-{message.MessageId}" }
+ }
+ };
+
+ var response = await GetChatCompletion(innerAgent, [
+ new RoleDialogModel(AgentRole.User, "Please follow the instruction to generate the javascript code.")
+ {
+ CurrentAgentId = message.CurrentAgentId,
+ MessageId = message.MessageId
+ }
+ ]);
+
+ message.Content = response;
+ message.RichContent = new RichContent
+ {
+ Recipient = new Recipient { Id = convService.ConversationId },
+ Message = new JsCodeTemplateMessage
+ {
+ Text = response
+ }
+ };
+ message.StopCompletion = true;
+ return true;
+ }
+
+ private async Task GetChatCompletion(Agent agent, List dialogs)
+ {
+ try
+ {
+ var llmProviderService = _services.GetRequiredService();
+ var completion = CompletionProvider.GetChatCompletion(_services, provider: "openai", model: "gpt-4.1");
+ var response = await completion.GetChatCompletions(agent, dialogs);
+ return response.Content;
+ }
+ catch (Exception ex)
+ {
+ var error = $"Error when plotting chart. {ex.Message}";
+ _logger.LogWarning(ex, error);
+ return error;
+ }
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Hooks/ChartHandlerUtilityHook.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Hooks/ChartHandlerUtilityHook.cs
new file mode 100644
index 00000000..51600147
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Hooks/ChartHandlerUtilityHook.cs
@@ -0,0 +1,24 @@
+namespace BotSharp.Plugin.ChartHandler.Hooks;
+
+public class ChartHandlerUtilityHook : IAgentUtilityHook
+{
+ private const string GENERATE_CHART_FN = "util-chart-generate_chart";
+
+ public void AddUtilities(List utilities)
+ {
+ var item = new AgentUtility
+ {
+ Category = "chart",
+ Name = UtilityName.ChartGenerator,
+ Items = [
+ new UtilityItem
+ {
+ FunctionName = GENERATE_CHART_FN,
+ TemplateName = $"{GENERATE_CHART_FN}.fn"
+ }
+ ]
+ };
+
+ utilities.Add(item);
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/LlmContext/LlmContextIn.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/LlmContext/LlmContextIn.cs
new file mode 100644
index 00000000..daaef271
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/LlmContext/LlmContextIn.cs
@@ -0,0 +1,10 @@
+using System.Text.Json.Serialization;
+
+namespace BotSharp.Plugin.ChartHandler.LlmContext;
+
+public class LlmContextIn
+{
+ [JsonPropertyName("plotting_requirement")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? PlottingRequirement { get; set; }
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Using.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Using.cs
new file mode 100644
index 00000000..fa94465c
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Using.cs
@@ -0,0 +1,32 @@
+global using System;
+global using System.Collections.Generic;
+global using System.Text;
+global using System.Linq;
+global using System.Text.Json;
+global using System.Net.Mime;
+global using System.Threading.Tasks;
+global using Microsoft.Extensions.DependencyInjection;
+global using Microsoft.Extensions.Logging;
+global using BotSharp.Abstraction.Agents;
+global using BotSharp.Abstraction.Conversations;
+global using BotSharp.Abstraction.Plugins;
+global using BotSharp.Abstraction.Conversations.Models;
+global using BotSharp.Abstraction.Functions;
+global using BotSharp.Abstraction.Agents.Models;
+global using BotSharp.Abstraction.Agents.Enums;
+global using BotSharp.Abstraction.Files.Enums;
+global using BotSharp.Abstraction.Files.Models;
+global using BotSharp.Abstraction.Files;
+global using BotSharp.Abstraction.MLTasks;
+global using BotSharp.Abstraction.Utilities;
+global using BotSharp.Abstraction.Agents.Settings;
+global using BotSharp.Abstraction.Functions.Models;
+global using BotSharp.Abstraction.Repositories;
+global using BotSharp.Abstraction.Settings;
+global using BotSharp.Abstraction.Messaging;
+global using BotSharp.Abstraction.Messaging.Models.RichContent;
+global using BotSharp.Abstraction.Options;
+global using BotSharp.Core.Infrastructures;
+global using BotSharp.Plugin.ChartHandler.Enums;
+global using BotSharp.Plugin.ChartHandler.LlmContext;
+global using BotSharp.Plugin.ChartHandler.Hooks;
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-chart-generate_chart.json b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-chart-generate_chart.json
new file mode 100644
index 00000000..937647f6
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-chart-generate_chart.json
@@ -0,0 +1,14 @@
+{
+ "name": "util-chart-generate_chart",
+ "description": "If the user requests you plotting or generating chart, you can call this function to generate chart in any format that user requested.",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "plotting_requirement": {
+ "type": "string",
+ "description": "The requirement that user posted for plotting or generating chart."
+ }
+ },
+ "required": [ "plotting_requirement" ]
+ }
+}
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-generate_chart.fn.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-generate_chart.fn.liquid
new file mode 100644
index 00000000..c5fb2d00
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-generate_chart.fn.liquid
@@ -0,0 +1 @@
+Please call function util-chart-generate_chart if user wants to generate a chart.
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid
new file mode 100644
index 00000000..272f3a3d
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid
@@ -0,0 +1,9 @@
+Please take a look at "Requirement" and generate a javascript code that can be used to render a pie chart on a canvas element with id {{ chart_element_id }}.
+
+=== Requirement ===
+{{ plotting_requirement }}
+
+
+*** Important ***
+** You must not create any new html element.
+** You must output the js code only, and wrap it between the script tags.
diff --git a/src/Plugins/BotSharp.Plugin.FileHandler/BotSharp.Plugin.FileHandler.csproj b/src/Plugins/BotSharp.Plugin.FileHandler/BotSharp.Plugin.FileHandler.csproj
index 2147d178..2b343db0 100644
--- a/src/Plugins/BotSharp.Plugin.FileHandler/BotSharp.Plugin.FileHandler.csproj
+++ b/src/Plugins/BotSharp.Plugin.FileHandler/BotSharp.Plugin.FileHandler.csproj
@@ -1,4 +1,4 @@
-
+
$(TargetFramework)
@@ -9,10 +9,6 @@
$(GenerateDocumentationFile)
$(SolutionDir)packages
-
-
-
-
diff --git a/src/Plugins/BotSharp.Plugin.FileHandler/LlmContexts/LlmContextIn.cs b/src/Plugins/BotSharp.Plugin.FileHandler/LlmContexts/LlmContextIn.cs
index a35e83bd..9280bfd7 100644
--- a/src/Plugins/BotSharp.Plugin.FileHandler/LlmContexts/LlmContextIn.cs
+++ b/src/Plugins/BotSharp.Plugin.FileHandler/LlmContexts/LlmContextIn.cs
@@ -12,10 +12,6 @@ public class LlmContextIn
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? ImageDescription { get; set; }
- //[JsonPropertyName("image_url")]
- //[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
- //public string? ImageUrl { get; set; }
-
[JsonPropertyName("image_urls")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IEnumerable? ImageUrls { get; set; }
diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj
index 5db5199f..2610d4f1 100644
--- a/src/WebStarter/WebStarter.csproj
+++ b/src/WebStarter/WebStarter.csproj
@@ -36,11 +36,11 @@
-
+
@@ -75,6 +75,7 @@
+
diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json
index 2b217318..4e7a4263 100644
--- a/src/WebStarter/appsettings.json
+++ b/src/WebStarter/appsettings.json
@@ -539,6 +539,7 @@
"BotSharp.Plugin.FileHandler",
"BotSharp.Plugin.EmailHandler",
"BotSharp.Plugin.AudioHandler",
+ "BotSharp.Plugin.ChartHandler",
"BotSharp.Plugin.TencentCos"
],
"ExcludedFunctions": [
From e4ad50cb7b9bba81bfab3c9962670276c513efca Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Tue, 1 Jul 2025 09:33:25 -0500
Subject: [PATCH 02/51] update instruction
---
.../templates/util-chart-plot-instruction.liquid | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid
index 272f3a3d..c396d793 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid
@@ -1,9 +1,12 @@
-Please take a look at "Requirement" and generate a javascript code that can be used to render a pie chart on a canvas element with id {{ chart_element_id }}.
+Please take a look at "Plotting Requirement" and generate a javascript code that can be used to render a chart on a div html element with id {{ chart_element_id }}.
-=== Requirement ===
+=== Plotting Requirement ===
{{ plotting_requirement }}
*** Important ***
+** You must not generate multiple script tags, which means only one is allowed.
** You must not create any new html element.
-** You must output the js code only, and wrap it between the script tags.
+** You must output the javascript code only, and wrap it between the script tag.
+** You need to import Plotly.js in the same script tag. The script source should be "https://cdn.plot.ly/plotly-3.0.1.min.js".
+** You must not apply any styles on the div html element.
\ No newline at end of file
From ba485cef48b7ee67890d2f13cf0864e78d24f8f9 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Tue, 1 Jul 2025 12:01:09 -0500
Subject: [PATCH 03/51] rename and refine instructions
---
.../BotSharp.Core/BotSharp.Core.csproj | 4 ++--
.../Instruct/FileInstructService.SelectFile.cs | 2 +-
... => util-file-select_file_instruction.liquid} | 0
.../BotSharp.Plugin.ChartHandler.csproj | 11 ++++++-----
.../Enums/UtilityName.cs | 2 +-
.../{GenerateChartFn.cs => PlotChartFn.cs} | 16 ++++++++--------
.../Hooks/ChartHandlerUtilityHook.cs | 16 ++++++++--------
...ate_chart.json => util-chart-plot_chart.json} | 6 +++---
.../util-chart-generate_chart.fn.liquid | 1 -
.../templates/util-chart-plot_chart.fn.liquid | 1 +
...liquid => util-chart-plot_instruction.liquid} | 1 +
11 files changed, 31 insertions(+), 29 deletions(-)
rename src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/{select_file_prompt.liquid => util-file-select_file_instruction.liquid} (100%)
rename src/Plugins/BotSharp.Plugin.ChartHandler/Functions/{GenerateChartFn.cs => PlotChartFn.cs} (87%)
rename src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/{util-chart-generate_chart.json => util-chart-plot_chart.json} (66%)
delete mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-generate_chart.fn.liquid
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_chart.fn.liquid
rename src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/{util-chart-plot-instruction.liquid => util-chart-plot_instruction.liquid} (91%)
diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
index ddcf68cb..635d35d7 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
+++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj
@@ -86,7 +86,6 @@
-
@@ -97,6 +96,7 @@
+
@@ -188,7 +188,7 @@
PreserveNewest
-
+
PreserveNewest
diff --git a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.SelectFile.cs b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.SelectFile.cs
index 13525fb1..e00f9158 100644
--- a/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.SelectFile.cs
+++ b/src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.SelectFile.cs
@@ -65,7 +65,7 @@ public partial class FileInstructService
}).ToList();
var agentId = !string.IsNullOrWhiteSpace(options.AgentId) ? options.AgentId : BuiltInAgentId.UtilityAssistant;
- var template = !string.IsNullOrWhiteSpace(options.Template) ? options.Template : "select_file_prompt";
+ var template = !string.IsNullOrWhiteSpace(options.Template) ? options.Template : "util-file-select_file_instruction";
var foundAgent = db.GetAgent(agentId);
var prompt = db.GetAgentTemplate(BuiltInAgentId.UtilityAssistant, template);
diff --git a/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/select_file_prompt.liquid b/src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-file-select_file_instruction.liquid
similarity index 100%
rename from src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/select_file_prompt.liquid
rename to src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-file-select_file_instruction.liquid
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/BotSharp.Plugin.ChartHandler.csproj b/src/Plugins/BotSharp.Plugin.ChartHandler/BotSharp.Plugin.ChartHandler.csproj
index 8051876c..4e7e8a21 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/BotSharp.Plugin.ChartHandler.csproj
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/BotSharp.Plugin.ChartHandler.csproj
@@ -7,18 +7,19 @@
-
-
+
+
+
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Enums/UtilityName.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Enums/UtilityName.cs
index a93a17e5..ee97a4a5 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/Enums/UtilityName.cs
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Enums/UtilityName.cs
@@ -2,5 +2,5 @@ namespace BotSharp.Plugin.ChartHandler.Enums;
public class UtilityName
{
- public const string ChartGenerator = "chart-generator";
+ public const string ChartPlotter = "chart-plotter";
}
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/GenerateChartFn.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
similarity index 87%
rename from src/Plugins/BotSharp.Plugin.ChartHandler/Functions/GenerateChartFn.cs
rename to src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
index c7e66f1f..afda35ed 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/GenerateChartFn.cs
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
@@ -2,18 +2,18 @@ using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
namespace BotSharp.Plugin.ChartHandler.Functions;
-public class GenerateChartFn : IFunctionCallback
+public class PlotChartFn : IFunctionCallback
{
private readonly IServiceProvider _services;
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
- public string Name => "util-chart-generate_chart";
- public string Indication => "Generating chart";
+ public string Name => "util-chart-plot_chart";
+ public string Indication => "Plotting chart";
- public GenerateChartFn(
+ public PlotChartFn(
IServiceProvider services,
- ILogger logger)
+ ILogger logger)
{
_services = services;
_logger = logger;
@@ -27,7 +27,7 @@ public class GenerateChartFn : IFunctionCallback
var args = JsonSerializer.Deserialize(message.FunctionArgs);
var agent = await agentService.GetAgent(message.CurrentAgentId);
- var inst = db.GetAgentTemplate(BuiltInAgentId.UtilityAssistant, "util-chart-plot-instruction");
+ var inst = db.GetAgentTemplate(BuiltInAgentId.UtilityAssistant, "util-chart-plot_instruction");
var innerAgent = new Agent
{
Id = agent.Id,
@@ -48,7 +48,7 @@ public class GenerateChartFn : IFunctionCallback
}
]);
- message.Content = response;
+ message.Content = "Here is the chart you ask for:";
message.RichContent = new RichContent
{
Recipient = new Recipient { Id = convService.ConversationId },
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Hooks/ChartHandlerUtilityHook.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Hooks/ChartHandlerUtilityHook.cs
index 51600147..02793ffe 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/Hooks/ChartHandlerUtilityHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Hooks/ChartHandlerUtilityHook.cs
@@ -2,21 +2,21 @@ namespace BotSharp.Plugin.ChartHandler.Hooks;
public class ChartHandlerUtilityHook : IAgentUtilityHook
{
- private const string GENERATE_CHART_FN = "util-chart-generate_chart";
+ private const string PLOT_CHART_FN = "util-chart-plot_chart";
public void AddUtilities(List utilities)
{
var item = new AgentUtility
{
Category = "chart",
- Name = UtilityName.ChartGenerator,
+ Name = UtilityName.ChartPlotter,
Items = [
- new UtilityItem
- {
- FunctionName = GENERATE_CHART_FN,
- TemplateName = $"{GENERATE_CHART_FN}.fn"
- }
- ]
+ new UtilityItem
+ {
+ FunctionName = PLOT_CHART_FN,
+ TemplateName = $"{PLOT_CHART_FN}.fn"
+ }
+ ]
};
utilities.Add(item);
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-chart-generate_chart.json b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-chart-plot_chart.json
similarity index 66%
rename from src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-chart-generate_chart.json
rename to src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-chart-plot_chart.json
index 937647f6..da950832 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-chart-generate_chart.json
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-chart-plot_chart.json
@@ -1,12 +1,12 @@
{
- "name": "util-chart-generate_chart",
- "description": "If the user requests you plotting or generating chart, you can call this function to generate chart in any format that user requested.",
+ "name": "util-chart-plot_chart",
+ "description": "If the user requests you plotting or generating charts, you can call this function to generate charts in any format that user requested.",
"parameters": {
"type": "object",
"properties": {
"plotting_requirement": {
"type": "string",
- "description": "The requirement that user posted for plotting or generating chart."
+ "description": "The requirement that user posted for plotting or generating charts."
}
},
"required": [ "plotting_requirement" ]
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-generate_chart.fn.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-generate_chart.fn.liquid
deleted file mode 100644
index c5fb2d00..00000000
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-generate_chart.fn.liquid
+++ /dev/null
@@ -1 +0,0 @@
-Please call function util-chart-generate_chart if user wants to generate a chart.
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_chart.fn.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_chart.fn.liquid
new file mode 100644
index 00000000..e54b3c4a
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_chart.fn.liquid
@@ -0,0 +1 @@
+Please call function util-chart-plot_chart if user wants to plot or generate charts.
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
similarity index 91%
rename from src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid
rename to src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
index c396d793..50a27eff 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot-instruction.liquid
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
@@ -9,4 +9,5 @@ Please take a look at "Plotting Requirement" and generate a javascript code that
** You must not create any new html element.
** You must output the javascript code only, and wrap it between the script tag.
** You need to import Plotly.js in the same script tag. The script source should be "https://cdn.plot.ly/plotly-3.0.1.min.js".
+** You need to add the MODE bar for each chart you plot.
** You must not apply any styles on the div html element.
\ No newline at end of file
From fdfaffb4789691af2364ec879d096ed833a3960d Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Tue, 1 Jul 2025 12:02:21 -0500
Subject: [PATCH 04/51] minor change
---
.../templates/util-chart-plot_instruction.liquid | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
index 50a27eff..fd7a5e7a 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
@@ -5,9 +5,9 @@ Please take a look at "Plotting Requirement" and generate a javascript code that
*** Important ***
-** You must not generate multiple script tags, which means only one is allowed.
+** You must not generate multiple script tag pairs, which means only one is allowed.
** You must not create any new html element.
-** You must output the javascript code only, and wrap it between the script tag.
+** You must output the javascript code only, and wrap it between one pair of script tags.
** You need to import Plotly.js in the same script tag. The script source should be "https://cdn.plot.ly/plotly-3.0.1.min.js".
** You need to add the MODE bar for each chart you plot.
** You must not apply any styles on the div html element.
\ No newline at end of file
From 0b54cfa4fafffeca759e7f30bf44ac9b5484ace0 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Tue, 1 Jul 2025 15:47:18 -0500
Subject: [PATCH 05/51] refine response format
---
.../Functions/PlotChartFn.cs | 5 +++--
.../LlmContext/LlmContextOut.cs | 14 ++++++++++++++
.../templates/util-chart-plot_instruction.liquid | 9 ++++++++-
3 files changed, 25 insertions(+), 3 deletions(-)
create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/LlmContext/LlmContextOut.cs
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
index afda35ed..02ea76b6 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
@@ -48,13 +48,14 @@ public class PlotChartFn : IFunctionCallback
}
]);
- message.Content = "Here is the chart you ask for:";
+ var obj = response.JsonContent();
+ message.Content = obj?.GreetingMessage ?? "Here is the chart you ask for:";
message.RichContent = new RichContent
{
Recipient = new Recipient { Id = convService.ConversationId },
Message = new JsCodeTemplateMessage
{
- Text = response
+ Text = obj?.JsCode ?? string.Empty
}
};
message.StopCompletion = true;
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/LlmContext/LlmContextOut.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/LlmContext/LlmContextOut.cs
new file mode 100644
index 00000000..7109f72d
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/LlmContext/LlmContextOut.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+
+namespace BotSharp.Plugin.ChartHandler.LlmContext;
+
+public class LlmContextOut
+{
+ [JsonPropertyName("greeting_message")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? GreetingMessage { get; set; }
+
+ [JsonPropertyName("js_code")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public string? JsCode { get; set; }
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
index fd7a5e7a..9e8f8034 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
@@ -10,4 +10,11 @@ Please take a look at "Plotting Requirement" and generate a javascript code that
** You must output the javascript code only, and wrap it between one pair of script tags.
** You need to import Plotly.js in the same script tag. The script source should be "https://cdn.plot.ly/plotly-3.0.1.min.js".
** You need to add the MODE bar for each chart you plot.
-** You must not apply any styles on the div html element.
\ No newline at end of file
+** You must not apply any styles on the div html element.
+
+*** Response Format ***
+You must output the response in the following JSON format:
+{
+ "greeting_message": "A short polite message that informs user that the charts have been generated.",
+ "js_code": "The javascript code wrapped in a pair of script tags"
+}
\ No newline at end of file
From a9b804d08e70a7b36d513d47a59ef950037a76c5 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Wed, 2 Jul 2025 11:54:41 -0500
Subject: [PATCH 06/51] refine plot instruction
---
.../util-chart-plot_instruction.liquid | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
index 9e8f8034..6f5d241c 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
@@ -1,20 +1,20 @@
-Please take a look at "Plotting Requirement" and generate a javascript code that can be used to render a chart on a div html element with id {{ chart_element_id }}.
-
+Please take a look at "Plotting Requirement" and generate a javascript code that can be used to render the charts on an html element.
=== Plotting Requirement ===
{{ plotting_requirement }}
-*** Important ***
-** You must not generate multiple script tag pairs, which means only one is allowed.
-** You must not create any new html element.
-** You must output the javascript code only, and wrap it between one pair of script tags.
-** You need to import Plotly.js in the same script tag. The script source should be "https://cdn.plot.ly/plotly-3.0.1.min.js".
+***** Important *****
+** Your output must be a single block with everything needed inside.
+** You need to import Plotly.js to plot the charts. The script source should be "https://cdn.plot.ly/plotly-3.0.1.min.js".
** You need to add the MODE bar for each chart you plot.
-** You must not apply any styles on the div html element.
+** You must render the charts on the div html element with id {{ chart_element_id }}.
+** You must not create any new html element.
+** You must not apply any styles on any html element.
+** You must generate as less token as possible.
*** Response Format ***
You must output the response in the following JSON format:
{
"greeting_message": "A short polite message that informs user that the charts have been generated.",
- "js_code": "The javascript code wrapped in a pair of script tags"
+ "js_code": "The javascript code that can generate the charts as requested."
}
\ No newline at end of file
From eb8eb43ea372ecf55f39fb288ea4610495f8366a Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Wed, 2 Jul 2025 13:12:04 -0500
Subject: [PATCH 07/51] handle null string
---
.../BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
index 02ea76b6..43c917d2 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
@@ -49,7 +49,7 @@ public class PlotChartFn : IFunctionCallback
]);
var obj = response.JsonContent();
- message.Content = obj?.GreetingMessage ?? "Here is the chart you ask for:";
+ message.Content = obj?.GreetingMessage.IfNullOrEmptyAs("Here is the chart you ask for:");
message.RichContent = new RichContent
{
Recipient = new Recipient { Id = convService.ConversationId },
From 894237c416fa6e1e94351b779abf8484c5197f15 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Tue, 8 Jul 2025 17:45:54 -0500
Subject: [PATCH 08/51] add program code template
---
.../Messaging/BotSharpMessageParser.cs | 12 ++++++------
.../Messaging/Enums/RichTypeEnum.cs | 3 +--
.../Messaging/Enums/TemplateTypeEnum.cs | 2 +-
.../Template/JsCodeTemplateMessage.cs | 10 ----------
.../Template/ProductTemplateMessage.cs | 19 -------------------
.../Template/ProgramCodeTemplateMessage.cs | 16 ++++++++++++++++
.../Functions/PlotChartFn.cs | 7 ++++---
7 files changed, 28 insertions(+), 41 deletions(-)
delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/JsCodeTemplateMessage.cs
delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs
create mode 100644 src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProgramCodeTemplateMessage.cs
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs
index 57db70d7..25a97b2b 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/BotSharpMessageParser.cs
@@ -1,7 +1,7 @@
-using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
-using BotSharp.Abstraction.Messaging.Models.RichContent;
using System.Text.Json;
using System.Reflection;
+using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
+using BotSharp.Abstraction.Messaging.Models.RichContent;
namespace BotSharp.Abstraction.Messaging;
@@ -38,9 +38,9 @@ public static class BotSharpMessageParser
{
targetType = typeof(TextMessage);
}
- else if (richType == RichTypeEnum.JsCode)
+ else if (richType == RichTypeEnum.ProgramCode)
{
- targetType = typeof(JsCodeTemplateMessage);
+ targetType = typeof(ProgramCodeTemplateMessage);
}
else if (richType == RichTypeEnum.GenericTemplate)
{
@@ -88,9 +88,9 @@ public static class BotSharpMessageParser
{
targetType = typeof(CouponTemplateMessage);
}
- else if (templateType == TemplateTypeEnum.Product)
+ else if (templateType == TemplateTypeEnum.ProgramCode)
{
- targetType = typeof(ProductTemplateMessage);
+ targetType = typeof(ProgramCodeTemplateMessage);
}
else if (templateType == TemplateTypeEnum.Generic)
{
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs
index 6227f522..8d66904b 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/RichTypeEnum.cs
@@ -9,6 +9,5 @@ public static class RichTypeEnum
public const string QuickReply = "quick_reply";
public const string Text = "text";
public const string Attachment = "attachment";
-
- public const string JsCode = "js_code";
+ public const string ProgramCode = "program_code";
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/TemplateTypeEnum.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/TemplateTypeEnum.cs
index 74f88735..73a4e9d4 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/TemplateTypeEnum.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Enums/TemplateTypeEnum.cs
@@ -6,5 +6,5 @@ public static class TemplateTypeEnum
public const string Coupon = "coupon";
public const string Generic = "generic";
public const string MultiSelect = "multi-select";
- public const string Product = "product";
+ public const string ProgramCode = "program_code";
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/JsCodeTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/JsCodeTemplateMessage.cs
deleted file mode 100644
index 1d58546b..00000000
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/JsCodeTemplateMessage.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template;
-
-public class JsCodeTemplateMessage : IRichMessage
-{
- [JsonPropertyName("rich_type")]
- public string RichType => RichTypeEnum.JsCode;
-
- [JsonPropertyName("text")]
- public string Text { get; set; } = string.Empty;
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs
deleted file mode 100644
index 05af097a..00000000
--- a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProductTemplateMessage.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template;
-
-public class ProductTemplateMessage : IRichMessage, ITemplateMessage
-{
- [JsonPropertyName("rich_type")]
- public string RichType => RichTypeEnum.GenericTemplate;
-
- [JsonPropertyName("text")]
- [Translate]
- public string Text { get; set; } = string.Empty;
-
- [JsonPropertyName("template_type")]
- public string TemplateType => TemplateTypeEnum.Product;
-}
-
-public class ProductElement
-{
- public string Id { get; set; }
-}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProgramCodeTemplateMessage.cs b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProgramCodeTemplateMessage.cs
new file mode 100644
index 00000000..60f6464a
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Messaging/Models/RichContent/Template/ProgramCodeTemplateMessage.cs
@@ -0,0 +1,16 @@
+namespace BotSharp.Abstraction.Messaging.Models.RichContent.Template;
+
+public class ProgramCodeTemplateMessage : IRichMessage, ITemplateMessage
+{
+ [JsonPropertyName("rich_type")]
+ public string RichType => RichTypeEnum.ProgramCode;
+
+ [JsonPropertyName("text")]
+ public string Text { get; set; } = string.Empty;
+
+ [JsonPropertyName("template_type")]
+ public virtual string TemplateType { get; set; } = TemplateTypeEnum.ProgramCode;
+
+ [JsonPropertyName("language")]
+ public string Language { get; set; } = string.Empty;
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
index 43c917d2..ad8de0d7 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs
@@ -49,13 +49,14 @@ public class PlotChartFn : IFunctionCallback
]);
var obj = response.JsonContent();
- message.Content = obj?.GreetingMessage.IfNullOrEmptyAs("Here is the chart you ask for:");
+ message.Content = obj?.GreetingMessage ?? "Here is the chart you ask for:";
message.RichContent = new RichContent
{
Recipient = new Recipient { Id = convService.ConversationId },
- Message = new JsCodeTemplateMessage
+ Message = new ProgramCodeTemplateMessage
{
- Text = obj?.JsCode ?? string.Empty
+ Text = obj?.JsCode ?? string.Empty,
+ Language = "javascript"
}
};
message.StopCompletion = true;
From 030e306a5597ff7d88eaa9a84dec4d717247a962 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Fri, 11 Jul 2025 14:28:09 -0500
Subject: [PATCH 09/51] minor change
---
.../templates/util-chart-plot_instruction.liquid | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
index 6f5d241c..9bffa73f 100644
--- a/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
+++ b/src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid
@@ -1,4 +1,6 @@
Please take a look at "Plotting Requirement" and generate a javascript code that can be used to render the charts on an html element.
+
+
=== Plotting Requirement ===
{{ plotting_requirement }}
From 0a6066c5b276e9db2eface9f37f0ee5b3cd77cbe Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Mon, 28 Jul 2025 17:41:28 -0500
Subject: [PATCH 10/51] refine indication
---
.../Conversations/Dtos/ChatResponseDto.cs | 4 +
.../Conversations/Enums/ChatEvent.cs | 22 +++
.../Models/ConversationSenderActionModel.cs | 2 -
.../Models/HubObserveData.cs | 2 +-
.../Models/ObserveDataBase.cs | 2 +-
.../Conversations/ConversationPlugin.cs | 4 +-
.../Demo/Functions/GetWeatherFn.cs | 26 +++
.../Queues => MessageHub}/MessageHub.cs | 4 +-
.../Routing/RoutingService.InvokeFunction.cs | 21 ++-
src/Infrastructure/BotSharp.Core/Using.cs | 3 +
.../Providers/Chat/ChatCompletionProvider.cs | 4 +-
.../BotSharp.Plugin.ChatHub/ChatHubPlugin.cs | 4 +-
.../Helpers/ChatHubHelper.cs | 38 ++++
.../Hooks/ChatHubConversationHook.cs | 170 +++--------------
.../Hooks/ChatHubCrontabHook.cs | 18 +-
.../Hooks/StreamingLogHook.cs | 174 ++++++------------
.../Hooks/WelcomeHook.cs | 36 +---
.../Observers/ChatHubObserver.cs | 146 ++++++++-------
src/Plugins/BotSharp.Plugin.ChatHub/Using.cs | 3 +-
.../Providers/Chat/ChatCompletionProvider.cs | 4 +-
.../Providers/ChatCompletionProvider.cs | 4 +-
.../Providers/Chat/ChatCompletionProvider.cs | 4 +-
.../Providers/ChatCompletionProvider.cs | 4 +-
23 files changed, 305 insertions(+), 394 deletions(-)
create mode 100644 src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ChatEvent.cs
rename src/Infrastructure/BotSharp.Abstraction/{Observables => MessageHub}/Models/HubObserveData.cs (75%)
rename src/Infrastructure/BotSharp.Abstraction/{Observables => MessageHub}/Models/ObserveDataBase.cs (68%)
rename src/Infrastructure/BotSharp.Core/{Observables/Queues => MessageHub}/MessageHub.cs (88%)
create mode 100644 src/Plugins/BotSharp.Plugin.ChatHub/Helpers/ChatHubHelper.cs
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Dtos/ChatResponseDto.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Dtos/ChatResponseDto.cs
index 19b5e8b2..9e4fcb41 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Dtos/ChatResponseDto.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Dtos/ChatResponseDto.cs
@@ -32,6 +32,10 @@ public class ChatResponseDto : InstructResult
[JsonPropertyName("payload")]
public string? Payload { get; set; }
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("indication")]
+ public string? Indication { get; set; }
+
[JsonPropertyName("has_message_files")]
public bool HasMessageFiles { get; set; }
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ChatEvent.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ChatEvent.cs
new file mode 100644
index 00000000..0d68fef7
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ChatEvent.cs
@@ -0,0 +1,22 @@
+namespace BotSharp.Abstraction.Conversations.Enums;
+
+public static class ChatEvent
+{
+ public const string OnConversationInitFromClient = nameof(OnConversationInitFromClient);
+ public const string OnMessageReceivedFromClient = nameof(OnMessageReceivedFromClient);
+ public const string OnMessageReceivedFromAssistant = nameof(OnMessageReceivedFromAssistant);
+
+ public const string OnMessageDeleted = nameof(OnMessageDeleted);
+ public const string OnNotificationGenerated = nameof(OnNotificationGenerated);
+ public const string OnIndicationReceived = nameof(OnIndicationReceived);
+
+ public const string OnConversationContentLogGenerated = nameof(OnConversationContentLogGenerated);
+ public const string OnConversateStateLogGenerated = nameof(OnConversateStateLogGenerated);
+ public const string OnAgentQueueChanged = nameof(OnAgentQueueChanged);
+ public const string OnStateChangeGenerated = nameof(OnStateChangeGenerated);
+
+ public const string BeforeReceiveLlmStreamMessage = nameof(BeforeReceiveLlmStreamMessage);
+ public const string OnReceiveLlmStreamMessage = nameof(OnReceiveLlmStreamMessage);
+ public const string AfterReceiveLlmStreamMessage = nameof(AfterReceiveLlmStreamMessage);
+ public const string OnSenderActionGenerated = nameof(OnSenderActionGenerated);
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs
index b49fc55d..502cca4c 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/ConversationSenderActionModel.cs
@@ -1,5 +1,3 @@
-using BotSharp.Abstraction.Messaging.Enums;
-
namespace BotSharp.Abstraction.Conversations.Models;
public class ConversationSenderActionModel
diff --git a/src/Infrastructure/BotSharp.Abstraction/Observables/Models/HubObserveData.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
similarity index 75%
rename from src/Infrastructure/BotSharp.Abstraction/Observables/Models/HubObserveData.cs
rename to src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
index bf771cb4..403adbc6 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Observables/Models/HubObserveData.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
@@ -1,4 +1,4 @@
-namespace BotSharp.Abstraction.Observables.Models;
+namespace BotSharp.Abstraction.MessageHub.Models;
public class HubObserveData : ObserveDataBase
{
diff --git a/src/Infrastructure/BotSharp.Abstraction/Observables/Models/ObserveDataBase.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
similarity index 68%
rename from src/Infrastructure/BotSharp.Abstraction/Observables/Models/ObserveDataBase.cs
rename to src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
index 17773272..62c2950d 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Observables/Models/ObserveDataBase.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
@@ -1,4 +1,4 @@
-namespace BotSharp.Abstraction.Observables.Models;
+namespace BotSharp.Abstraction.MessageHub.Models;
public abstract class ObserveDataBase
{
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
index a559877f..2f0af290 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
@@ -10,9 +10,9 @@ using BotSharp.Core.Messaging;
using BotSharp.Core.Routing.Reasoning;
using BotSharp.Core.Templating;
using BotSharp.Core.Translation;
-using BotSharp.Core.Observables.Queues;
using Microsoft.Extensions.Configuration;
-using BotSharp.Abstraction.Observables.Models;
+using BotSharp.Abstraction.MessageHub.Models;
+using BotSharp.Core.MessageHub;
namespace BotSharp.Core.Conversations;
diff --git a/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs b/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
index cb6bcd88..b8bb0f39 100644
--- a/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
+++ b/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Functions;
+using BotSharp.Core.MessageHub;
namespace BotSharp.Core.Demo.Functions;
@@ -16,6 +17,31 @@ public class GetWeatherFn : IFunctionCallback
public async Task Execute(RoleDialogModel message)
{
+ var conv = _services.GetRequiredService();
+ var messageHub = _services.GetRequiredService>();
+
+ await Task.Delay(1000);
+
+ message.Indication = "Start querying weather data";
+ messageHub.Push(new()
+ {
+ EventName = ChatEvent.OnIndicationReceived,
+ Data = message,
+ ServiceProvider = _services
+ });
+
+ await Task.Delay(1500);
+
+ message.Indication = "Still working on it";
+ messageHub.Push(new()
+ {
+ EventName = ChatEvent.OnIndicationReceived,
+ Data = message,
+ ServiceProvider = _services
+ });
+
+ await Task.Delay(1500);
+
message.Content = $"It is a sunny day!";
message.StopCompletion = false;
return true;
diff --git a/src/Infrastructure/BotSharp.Core/Observables/Queues/MessageHub.cs b/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
similarity index 88%
rename from src/Infrastructure/BotSharp.Core/Observables/Queues/MessageHub.cs
rename to src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
index affb142d..a104f4f6 100644
--- a/src/Infrastructure/BotSharp.Core/Observables/Queues/MessageHub.cs
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
@@ -1,11 +1,11 @@
using System.Reactive.Subjects;
-namespace BotSharp.Core.Observables.Queues;
+namespace BotSharp.Core.MessageHub;
public class MessageHub where T : class
{
private readonly ILogger> _logger;
- private readonly ISubject _observable = new Subject();
+ private readonly ISubject _observable = Subject.Synchronize(new Subject());
public IObservable Events => _observable;
public MessageHub(ILogger> logger)
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
index 1001dede..0922dcdb 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
@@ -1,4 +1,4 @@
-using BotSharp.Abstraction.Hooks;
+using BotSharp.Core.MessageHub;
using BotSharp.Core.Routing.Executor;
namespace BotSharp.Core.Routing;
@@ -23,15 +23,22 @@ public partial class RoutingService
// Clone message
var clonedMessage = RoleDialogModel.From(message);
clonedMessage.FunctionName = name;
-
- var progressService = _services.GetService();
clonedMessage.Indication = await funcExecutor.GetIndicatorAsync(message);
- if (progressService?.OnFunctionExecuting != null)
+ //var progressService = _services.GetService();
+ //if (progressService?.OnFunctionExecuting != null)
+ //{
+ // await progressService.OnFunctionExecuting(clonedMessage);
+ //}
+
+ var messageHub = _services.GetRequiredService>();
+ messageHub.Push(new()
{
- await progressService.OnFunctionExecuting(clonedMessage);
- }
-
+ EventName = ChatEvent.OnIndicationReceived,
+ Data = clonedMessage,
+ ServiceProvider = _services
+ });
+
var hooks = _services.GetHooksOrderByPriority(clonedMessage.CurrentAgentId);
foreach (var hook in hooks)
{
diff --git a/src/Infrastructure/BotSharp.Core/Using.cs b/src/Infrastructure/BotSharp.Core/Using.cs
index 0e8e2762..f67baef0 100644
--- a/src/Infrastructure/BotSharp.Core/Using.cs
+++ b/src/Infrastructure/BotSharp.Core/Using.cs
@@ -36,6 +36,9 @@ global using BotSharp.Abstraction.Loggers.Services;
global using BotSharp.Abstraction.Infrastructures.Events;
global using BotSharp.Abstraction.Templating.Constants;
global using BotSharp.Abstraction.Realtime.Models.Session;
+global using BotSharp.Abstraction.Conversations.Enums;
+global using BotSharp.Abstraction.Hooks;
+global using BotSharp.Abstraction.MessageHub.Models;
global using BotSharp.Core.Agents.Services;
global using BotSharp.Core.Conversations.Services;
global using BotSharp.Core.Infrastructures;
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
index 605bceb8..d3c6e14a 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
@@ -1,9 +1,9 @@
using Azure;
using BotSharp.Abstraction.Files.Utilities;
using BotSharp.Abstraction.Hooks;
-using BotSharp.Abstraction.Observables.Models;
+using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Core.Infrastructures.Streams;
-using BotSharp.Core.Observables.Queues;
+using BotSharp.Core.MessageHub;
using OpenAI.Chat;
using System.ClientModel;
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
index d3c3f2ac..d771d0a2 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
@@ -1,6 +1,6 @@
using BotSharp.Abstraction.Crontab;
-using BotSharp.Abstraction.Observables.Models;
-using BotSharp.Core.Observables.Queues;
+using BotSharp.Abstraction.MessageHub.Models;
+using BotSharp.Core.MessageHub;
using BotSharp.Plugin.ChatHub.Hooks;
using BotSharp.Plugin.ChatHub.Observers;
using Microsoft.AspNetCore.Builder;
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Helpers/ChatHubHelper.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Helpers/ChatHubHelper.cs
new file mode 100644
index 00000000..94bbc90d
--- /dev/null
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Helpers/ChatHubHelper.cs
@@ -0,0 +1,38 @@
+using Microsoft.AspNetCore.SignalR;
+using System.Runtime.CompilerServices;
+
+namespace BotSharp.Plugin.ChatHub.Helpers;
+
+public class ChatHubHelper
+{
+ public static async Task SendChatEvent(
+ IServiceProvider services,
+ ILogger logger,
+ string @event,
+ string conversationId,
+ string userId,
+ T data,
+ string callerClass = "",
+ [CallerMemberName] string callerMethod = "",
+ LogLevel logLevel = LogLevel.Warning)
+ {
+ try
+ {
+ var settings = services.GetRequiredService();
+ var chatHub = services.GetRequiredService>();
+
+ if (settings.EventDispatchBy == EventDispatchType.Group)
+ {
+ await chatHub.Clients.Group(conversationId).SendAsync(@event, data);
+ }
+ else
+ {
+ await chatHub.Clients.User(userId).SendAsync(@event, data);
+ }
+ }
+ catch (Exception ex)
+ {
+ logger.Log(logLevel, ex, $"Failed to send event '{@event}' in ({callerClass}-{callerMethod}) (conversation id: {conversationId})");
+ }
+ }
+}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
index 1d641554..5cbc117e 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
@@ -1,8 +1,10 @@
using BotSharp.Abstraction.Conversations.Dtos;
+using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.Routing.Enums;
using BotSharp.Abstraction.SideCar;
using BotSharp.Abstraction.Users.Dtos;
using Microsoft.AspNetCore.SignalR;
+using System.Runtime.CompilerServices;
namespace BotSharp.Plugin.ChatHub.Hooks;
@@ -15,15 +17,6 @@ public class ChatHubConversationHook : ConversationHookBase
private readonly BotSharpOptions _options;
private readonly ChatHubSettings _settings;
- #region Events
- private const string INIT_CLIENT_CONVERSATION = "OnConversationInitFromClient";
- private const string RECEIVE_CLIENT_MESSAGE = "OnMessageReceivedFromClient";
- private const string RECEIVE_ASSISTANT_MESSAGE = "OnMessageReceivedFromAssistant";
- private const string GENERATE_SENDER_ACTION = "OnSenderActionGenerated";
- private const string DELETE_MESSAGE = "OnMessageDeleted";
- private const string GENERATE_NOTIFICATION = "OnNotificationGenerated";
- #endregion
-
public ChatHubConversationHook(
IServiceProvider services,
IHubContext chatHub,
@@ -51,7 +44,8 @@ public class ChatHubConversationHook : ConversationHookBase
var user = await userService.GetUser(conv.User.Id);
conv.User = UserDto.FromUser(user);
- await InitClientConversation(conv.Id, conv);
+ //await InitClientConversation(conv.Id, conv);
+ await SendEvent(ChatEvent.OnConversationInitFromClient, conv.Id, conv);
await base.OnConversationInitialized(conversation);
}
@@ -72,7 +66,7 @@ public class ChatHubConversationHook : ConversationHookBase
Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
Sender = UserDto.FromUser(sender)
};
- await ReceiveClientMessage(conv.ConversationId, model);
+ await SendEvent(ChatEvent.OnMessageReceivedFromClient, conv.ConversationId, model);
// Send typing-on to client
var action = new ConversationSenderActionModel
@@ -80,22 +74,12 @@ public class ChatHubConversationHook : ConversationHookBase
ConversationId = conv.ConversationId,
SenderAction = SenderActionEnum.TypingOn
};
-
- await GenerateSenderAction(conv.ConversationId, action);
+ await SendEvent(ChatEvent.OnSenderActionGenerated, conv.ConversationId, action);
await base.OnMessageReceived(message);
}
public override async Task OnFunctionExecuting(RoleDialogModel message, string from = InvokeSource.Manual)
{
- var conv = _services.GetRequiredService();
- var action = new ConversationSenderActionModel
- {
- ConversationId = conv.ConversationId,
- SenderAction = SenderActionEnum.TypingOn,
- Indication = message.Indication
- };
-
- await GenerateSenderAction(conv.ConversationId, action);
await base.OnFunctionExecuting(message, from: from);
}
@@ -110,7 +94,7 @@ public class ChatHubConversationHook : ConversationHookBase
var conv = _services.GetRequiredService();
var state = _services.GetRequiredService();
- var json = JsonSerializer.Serialize(new ChatResponseDto()
+ var data = new ChatResponseDto()
{
ConversationId = conv.ConversationId,
MessageId = message.MessageId,
@@ -126,21 +110,20 @@ public class ChatHubConversationHook : ConversationHookBase
LastName = "Assistant",
Role = AgentRole.Assistant
}
- }, _options.JsonSerializerOptions);
-
- // Send typing-off to client
- var action = new ConversationSenderActionModel
- {
- ConversationId = conv.ConversationId,
- SenderAction = SenderActionEnum.TypingOff
};
+ // Send typing-off to client
if (!message.IsStreaming)
{
- await GenerateSenderAction(conv.ConversationId, action);
+ var action = new ConversationSenderActionModel
+ {
+ ConversationId = conv.ConversationId,
+ SenderAction = SenderActionEnum.TypingOff
+ };
+ await SendEvent(ChatEvent.OnSenderActionGenerated, conv.ConversationId, action);
}
- await ReceiveAssistantMessage(conv.ConversationId, json);
+ await SendEvent(ChatEvent.OnMessageReceivedFromAssistant, conv.ConversationId, data);
await base.OnResponseGenerated(message);
}
@@ -148,7 +131,7 @@ public class ChatHubConversationHook : ConversationHookBase
public override async Task OnNotificationGenerated(RoleDialogModel message)
{
var conv = _services.GetRequiredService();
- var json = JsonSerializer.Serialize(new ChatResponseDto()
+ var data = new ChatResponseDto()
{
ConversationId = conv.ConversationId,
MessageId = message.MessageId,
@@ -162,9 +145,9 @@ public class ChatHubConversationHook : ConversationHookBase
LastName = "Assistant",
Role = AgentRole.Assistant
}
- }, _options.JsonSerializerOptions);
+ };
- await GenerateNotification(conv.ConversationId, json);
+ await SendEvent(ChatEvent.OnNotificationGenerated, conv.ConversationId, data);
await base.OnNotificationGenerated(message);
}
@@ -177,7 +160,7 @@ public class ChatHubConversationHook : ConversationHookBase
MessageId = messageId
};
- await DeleteMessage(conversationId, model);
+ await SendEvent(ChatEvent.OnMessageDeleted, conversationId, model);
await base.OnMessageDeleted(conversationId, messageId);
}
@@ -188,119 +171,10 @@ public class ChatHubConversationHook : ConversationHookBase
return sidecar == null || !sidecar.IsEnabled;
}
- private async Task InitClientConversation(string conversationId, ConversationDto conversation)
+ private async Task SendEvent(string @event, string conversationId, T data, [CallerMemberName] string callerName = "")
{
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(INIT_CLIENT_CONVERSATION, conversation);
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(INIT_CLIENT_CONVERSATION, conversation);
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to init client conversation in {nameof(ChatHubConversationHook)} (conversation id: {conversationId})");
- }
- }
-
- private async Task ReceiveClientMessage(string conversationId, ChatResponseDto model)
- {
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(RECEIVE_CLIENT_MESSAGE, model);
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(RECEIVE_CLIENT_MESSAGE, model);
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to receive assistant message in {nameof(ChatHubConversationHook)} (conversation id: {conversationId})");
- }
- }
-
- private async Task ReceiveAssistantMessage(string conversationId, string? json)
- {
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(RECEIVE_ASSISTANT_MESSAGE, json);
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(RECEIVE_ASSISTANT_MESSAGE, json);
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to receive assistant message in {nameof(ChatHubConversationHook)} (conversation id: {conversationId})");
- }
-
- }
-
- private async Task GenerateSenderAction(string conversationId, ConversationSenderActionModel action)
- {
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(GENERATE_SENDER_ACTION, action);
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(GENERATE_SENDER_ACTION, action);
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to generate sender action in {nameof(ChatHubConversationHook)} (conversation id: {conversationId})");
- }
- }
-
- private async Task DeleteMessage(string conversationId, ChatResponseDto model)
- {
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(DELETE_MESSAGE, model);
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(DELETE_MESSAGE, model);
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to delete message in {nameof(ChatHubConversationHook)} (conversation id: {conversationId})");
- }
- }
-
- private async Task GenerateNotification(string conversationId, string? json)
- {
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(GENERATE_NOTIFICATION, json);
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(GENERATE_NOTIFICATION, json);
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to generate notification in {nameof(ChatHubConversationHook)} (conversation id: {conversationId})");
- }
+ var user = _services.GetRequiredService();
+ await ChatHubHelper.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, data, nameof(ChatHubConversationHook), callerName);
}
#endregion
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs
index ef956670..f965d28e 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Conversations.Dtos;
+using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.Crontab;
using BotSharp.Abstraction.Crontab.Models;
using Microsoft.AspNetCore.SignalR;
@@ -14,11 +15,8 @@ public class ChatHubCrontabHook : ICrontabHook
private readonly BotSharpOptions _options;
private readonly ChatHubSettings _settings;
- #region Events
- private const string GENERATE_NOTIFICATION = "OnNotificationGenerated";
- #endregion
-
- public ChatHubCrontabHook(IServiceProvider services,
+ public ChatHubCrontabHook(
+ IServiceProvider services,
IHubContext chatHub,
ILogger logger,
IUserIdentity user,
@@ -35,7 +33,7 @@ public class ChatHubCrontabHook : ICrontabHook
public async Task OnCronTriggered(CrontabItem item)
{
- var json = JsonSerializer.Serialize(new ChatResponseDto()
+ var data = new ChatResponseDto()
{
ConversationId = item.ConversationId,
MessageId = Guid.NewGuid().ToString(),
@@ -47,16 +45,16 @@ public class ChatHubCrontabHook : ICrontabHook
LastName = "AI",
Role = AgentRole.Assistant
}
- }, _options.JsonSerializerOptions);
+ };
- await SendEvent(item, json);
+ await SendEvent(item, data);
}
- private async Task SendEvent(CrontabItem item, string json)
+ private async Task SendEvent(CrontabItem item, ChatResponseDto data)
{
try
{
- await _chatHub.Clients.User(item.UserId).SendAsync(GENERATE_NOTIFICATION, json);
+ await _chatHub.Clients.User(item.UserId).SendAsync(ChatEvent.OnNotificationGenerated, data);
}
catch { }
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
index 5dd89a2b..f036deab 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
@@ -1,5 +1,7 @@
+using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.Routing.Enums;
using Microsoft.AspNetCore.SignalR;
+using System.Runtime.CompilerServices;
using System.Text.Encodings.Web;
using System.Text.Unicode;
@@ -19,13 +21,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
private readonly IAgentService _agentService;
private readonly IRoutingContext _routingCtx;
- #region Events
- private const string CONTENT_LOG_GENERATED = "OnConversationContentLogGenerated";
- private const string STATE_LOG_GENERATED = "OnConversateStateLogGenerated";
- private const string AGENT_QUEUE_CHANGED = "OnAgentQueueChanged";
- private const string STATE_CHANGED = "OnStateChangeGenerated";
- #endregion
-
public StreamingLogHook(
ConversationSetting convSettings,
BotSharpOptions options,
@@ -65,7 +60,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.UserInput,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public override async Task OnPostbackMessageReceived(RoleDialogModel message, PostbackMessageModel replyMsg)
@@ -83,7 +79,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.UserInput,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public async Task OnSessionUpdated(Agent agent, string instruction, FunctionDef[] functions, bool isInit = false)
@@ -112,7 +109,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.Prompt,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public async Task OnRenderingTemplate(Agent agent, string name, string content)
@@ -134,7 +132,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public async Task BeforeGenerating(Agent agent, List conversations)
@@ -162,7 +161,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public override async Task OnFunctionExecuted(RoleDialogModel message, string from = InvokeSource.Manual)
@@ -183,7 +183,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
///
@@ -210,7 +211,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.Prompt,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
///
@@ -225,7 +227,9 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
var conv = _services.GetRequiredService();
var routingCtx = _services.GetRequiredService();
- await SendStateLog(conv.ConversationId, routingCtx.EntryAgentId, _state.GetStates(), message);
+ var stateLog = BuildStateLog(conv.ConversationId, routingCtx.EntryAgentId, _state.GetStates(), message);
+ //await SendStateLog(conv.ConversationId, routingCtx.EntryAgentId, _state.GetStates(), message);
+ await SendEvent(ChatEvent.OnConversateStateLogGenerated, conv.ConversationId, stateLog);
if (message.Role == AgentRole.Assistant)
{
@@ -244,7 +248,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.AgentResponse,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
}
@@ -262,7 +267,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public override async Task OnConversationEnding(RoleDialogModel message)
@@ -279,7 +285,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public override async Task OnBreakpointUpdated(string conversationId, bool resetStates)
@@ -307,7 +314,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
},
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public override async Task OnStateChanged(StateChangeModel stateChange)
@@ -317,7 +325,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
if (stateChange == null) return;
- await SendStateChange(conversationId, stateChange);
+ //await SendStateChange(conversationId, stateChange);
+ await SendEvent(ChatEvent.OnStateChangeGenerated, conversationId, BuildStateChangeLog(stateChange));
}
#endregion
@@ -331,7 +340,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"{agent.Name} is enqueued";
- await SendAgentQueueLog(conversationId, log);
+ //await SendAgentQueueLog(conversationId, log);
+ await SendEvent(ChatEvent.OnAgentQueueChanged, conversationId, BuildAgentQueueChangedLog(conversationId, log));
// Content log
log = $"{agent.Name} is enqueued{(reason != null ? $" ({reason})" : "")}";
@@ -346,7 +356,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public async Task OnAgentDequeued(string agentId, string currentAgentId, string? reason = null)
@@ -359,7 +370,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"{agent.Name} is dequeued";
- await SendAgentQueueLog(conversationId, log);
+ //await SendAgentQueueLog(conversationId, log);
+ await SendEvent(ChatEvent.OnAgentQueueChanged, conversationId, BuildAgentQueueChangedLog(conversationId, log));
// Content log
log = $"{agent.Name} is dequeued{(reason != null ? $" ({reason})" : "")}, current agent is {currentAgent?.Name}";
@@ -374,7 +386,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public async Task OnAgentReplaced(string fromAgentId, string toAgentId, string? reason = null)
@@ -387,7 +400,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"Agent queue is replaced from {fromAgent.Name} to {toAgent.Name}";
- await SendAgentQueueLog(conversationId, log);
+ //await SendAgentQueueLog(conversationId, log);
+ await SendEvent(ChatEvent.OnAgentQueueChanged, conversationId, BuildAgentQueueChangedLog(conversationId, log));
// Content log
log = $"{fromAgent.Name} is replaced to {toAgent.Name}{(reason != null ? $" ({reason})" : "")}";
@@ -402,7 +416,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public async Task OnAgentQueueEmptied(string agentId, string? reason = null)
@@ -412,7 +427,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"Agent queue is empty";
- await SendAgentQueueLog(conversationId, log);
+ //await SendAgentQueueLog(conversationId, log);
+ await SendEvent(ChatEvent.OnAgentQueueChanged, conversationId, BuildAgentQueueChangedLog(conversationId, log));
// Content log
log = reason ?? "Agent queue is cleared";
@@ -427,7 +443,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public async Task OnRoutingInstructionReceived(FunctionCallFromLlm instruct, RoleDialogModel message)
@@ -446,7 +463,8 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.AgentResponse,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
public async Task OnRoutingInstructionRevised(FunctionCallFromLlm instruct, RoleDialogModel message)
@@ -464,90 +482,20 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- await SendContentLog(conversationId, input);
+ //await SendContentLog(conversationId, input);
+ await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
#endregion
#region Private methods
- private async Task SendContentLog(string conversationId, ContentLogInputModel input)
+ private async Task SendEvent(string @event, string conversationId, T data, [CallerMemberName] string callerName = "")
{
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(CONTENT_LOG_GENERATED, BuildContentLog(input));
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(CONTENT_LOG_GENERATED, BuildContentLog(input));
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to send content log in {nameof(StreamingLogHook)} (conversation id: {conversationId}).");
- }
+ var user = _services.GetRequiredService();
+ await ChatHubHelper.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, data, nameof(StreamingLogHook), callerName);
}
- private async Task SendStateLog(string conversationId, string agentId, Dictionary states, RoleDialogModel message)
- {
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(STATE_LOG_GENERATED, BuildStateLog(conversationId, agentId, states, message));
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(STATE_LOG_GENERATED, BuildStateLog(conversationId, agentId, states, message));
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to send state log in {nameof(StreamingLogHook)} (conversation id: {conversationId}).");
- }
- }
-
- private async Task SendAgentQueueLog(string conversationId, string log)
- {
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(AGENT_QUEUE_CHANGED, BuildAgentQueueChangedLog(conversationId, log));
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(AGENT_QUEUE_CHANGED, BuildAgentQueueChangedLog(conversationId, log));
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to send agent queue log in {nameof(StreamingLogHook)} (conversation id: {conversationId}).");
- }
- }
-
- private async Task SendStateChange(string conversationId, StateChangeModel stateChange)
- {
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(STATE_CHANGED, BuildStateChangeLog(stateChange));
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(STATE_CHANGED, BuildStateChangeLog(stateChange));
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to send state change in {nameof(StreamingLogHook)} (conversation id: {conversationId}).");
- }
- }
-
-
- private string BuildContentLog(ContentLogInputModel input)
+ private ContentLogOutputModel BuildContentLog(ContentLogInputModel input)
{
var output = new ContentLogOutputModel
{
@@ -561,8 +509,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
CreatedTime = DateTime.UtcNow
};
- var json = JsonSerializer.Serialize(output, _options.JsonSerializerOptions);
-
var convSettings = _services.GetRequiredService();
if (convSettings.EnableContentLog)
{
@@ -570,10 +516,10 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
db.SaveConversationContentLog(output);
}
- return json;
+ return output;
}
- private string BuildStateLog(string conversationId, string agentId, Dictionary states, RoleDialogModel message)
+ private ConversationStateLogModel BuildStateLog(string conversationId, string agentId, Dictionary states, RoleDialogModel message)
{
var log = new ConversationStateLogModel
{
@@ -591,10 +537,10 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
db.SaveConversationStateLog(log);
}
- return JsonSerializer.Serialize(log, _options.JsonSerializerOptions);
+ return log;
}
- private string BuildStateChangeLog(StateChangeModel stateChange)
+ private StateChangeOutputModel BuildStateChangeLog(StateChangeModel stateChange)
{
var log = new StateChangeOutputModel
{
@@ -611,10 +557,10 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
CreateTime = DateTime.UtcNow
};
- return JsonSerializer.Serialize(log, _options.JsonSerializerOptions);
+ return log;
}
- private string BuildAgentQueueChangedLog(string conversationId, string log)
+ private AgentQueueChangedLogModel BuildAgentQueueChangedLog(string conversationId, string log)
{
var model = new AgentQueueChangedLogModel
{
@@ -623,7 +569,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
CreatedTime = DateTime.UtcNow
};
- return JsonSerializer.Serialize(model, _options.JsonSerializerOptions);
+ return model;
}
private string GetMessageContent(RoleDialogModel message)
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs
index 3ae8b313..3db8017b 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs
@@ -1,5 +1,7 @@
using BotSharp.Abstraction.Conversations.Dtos;
+using BotSharp.Abstraction.Conversations.Enums;
using Microsoft.AspNetCore.SignalR;
+using System.Runtime.CompilerServices;
namespace BotSharp.Plugin.ChatHub.Hooks;
@@ -13,11 +15,8 @@ public class WelcomeHook : ConversationHookBase
private readonly BotSharpOptions _options;
private readonly ChatHubSettings _settings;
- #region Events
- private const string RECEIVE_ASSISTANT_MESSAGE = "OnMessageReceivedFromAssistant";
- #endregion
-
- public WelcomeHook(IServiceProvider services,
+ public WelcomeHook(
+ IServiceProvider services,
IHubContext chatHub,
ILogger logger,
IUserIdentity user,
@@ -64,7 +63,7 @@ public class WelcomeHook : ConversationHookBase
RichContent = richContent
};
- var json = JsonSerializer.Serialize(new ChatResponseDto()
+ var data = new ChatResponseDto()
{
ConversationId = conversation.Id,
MessageId = dialog.MessageId,
@@ -76,35 +75,20 @@ public class WelcomeHook : ConversationHookBase
LastName = "",
Role = AgentRole.Assistant
}
- }, _options.JsonSerializerOptions);
+ };
await Task.Delay(300);
-
_storage.Append(conversation.Id, dialog);
-
- await SendEvent(conversation.Id, json);
+ await SendEvent(ChatEvent.OnMessageReceivedFromAssistant, conversation.Id, data);
}
}
await base.OnUserAgentConnectedInitially(conversation);
}
- private async Task SendEvent(string conversationId, string json)
+ private async Task SendEvent(string @event, string conversationId, T data, [CallerMemberName] string callerName = "")
{
- try
- {
- if (_settings.EventDispatchBy == EventDispatchType.Group)
- {
- await _chatHub.Clients.Group(conversationId).SendAsync(RECEIVE_ASSISTANT_MESSAGE, json);
- }
- else
- {
- await _chatHub.Clients.User(_user.Id).SendAsync(RECEIVE_ASSISTANT_MESSAGE, json);
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to send event in {nameof(WelcomeHook)} (conversation id: {conversationId}).");
- }
+ var user = _services.GetRequiredService();
+ await ChatHubHelper.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, data, nameof(WelcomeHook), callerName);
}
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
index c15419ed..71b07b9d 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
@@ -1,5 +1,6 @@
using BotSharp.Abstraction.Conversations.Dtos;
-using BotSharp.Abstraction.Observables.Models;
+using BotSharp.Abstraction.Conversations.Enums;
+using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Abstraction.SideCar;
using BotSharp.Plugin.ChatHub.Hooks;
using Microsoft.AspNetCore.SignalR;
@@ -11,11 +12,6 @@ public class ChatHubObserver : IObserver
private readonly ILogger _logger;
private IServiceProvider _services;
- private const string BEFORE_RECEIVE_LLM_STREAM_MESSAGE = "BeforeReceiveLlmStreamMessage";
- private const string ON_RECEIVE_LLM_STREAM_MESSAGE = "OnReceiveLlmStreamMessage";
- private const string AFTER_RECEIVE_LLM_STREAM_MESSAGE = "AfterReceiveLlmStreamMessage";
- private const string GENERATE_SENDER_ACTION = "OnSenderActionGenerated";
-
public ChatHubObserver(ILogger logger)
{
_logger = logger;
@@ -39,72 +35,86 @@ public class ChatHubObserver : IObserver
var message = value.Data;
var model = new ChatResponseDto();
- if (value.EventName == BEFORE_RECEIVE_LLM_STREAM_MESSAGE)
+ var action = new ConversationSenderActionModel();
+ var conv = _services.GetRequiredService();
+
+ switch (value.EventName)
{
- var conv = _services.GetRequiredService();
- model = new ChatResponseDto()
- {
- ConversationId = conv.ConversationId,
- MessageId = message.MessageId,
- Text = string.Empty,
- Sender = new()
+ case ChatEvent.BeforeReceiveLlmStreamMessage:
+ model = new ChatResponseDto()
{
- FirstName = "AI",
- LastName = "Assistant",
- Role = AgentRole.Assistant
- }
- };
+ ConversationId = conv.ConversationId,
+ MessageId = message.MessageId,
+ Text = string.Empty,
+ Sender = new()
+ {
+ FirstName = "AI",
+ LastName = "Assistant",
+ Role = AgentRole.Assistant
+ }
+ };
- var action = new ConversationSenderActionModel
- {
- ConversationId = conv.ConversationId,
- SenderAction = SenderActionEnum.TypingOn
- };
-
- GenerateSenderAction(conv.ConversationId, action);
- }
- else if (value.EventName == AFTER_RECEIVE_LLM_STREAM_MESSAGE && message.IsStreaming)
- {
- var conv = _services.GetRequiredService();
- model = new ChatResponseDto()
- {
- ConversationId = conv.ConversationId,
- MessageId = message.MessageId,
- Text = message.Content,
- Sender = new()
+ action = new ConversationSenderActionModel
{
- FirstName = "AI",
- LastName = "Assistant",
- Role = AgentRole.Assistant
- }
- };
+ ConversationId = conv.ConversationId,
+ SenderAction = SenderActionEnum.TypingOn
+ };
- var action = new ConversationSenderActionModel
- {
- ConversationId = conv.ConversationId,
- SenderAction = SenderActionEnum.TypingOff
- };
-
- GenerateSenderAction(conv.ConversationId, action);
- }
- else if (value.EventName == ON_RECEIVE_LLM_STREAM_MESSAGE)
- {
- var conv = _services.GetRequiredService();
- model = new ChatResponseDto()
- {
- ConversationId = conv.ConversationId,
- MessageId = message.MessageId,
- Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
- Function = message.FunctionName,
- RichContent = message.SecondaryRichContent ?? message.RichContent,
- Data = message.Data,
- Sender = new()
+ GenerateSenderAction(conv.ConversationId, action);
+ break;
+ case ChatEvent.OnReceiveLlmStreamMessage:
+ model = new ChatResponseDto()
{
- FirstName = "AI",
- LastName = "Assistant",
- Role = AgentRole.Assistant
- }
- };
+ ConversationId = conv.ConversationId,
+ MessageId = message.MessageId,
+ Text = !string.IsNullOrEmpty(message.SecondaryContent) ? message.SecondaryContent : message.Content,
+ Function = message.FunctionName,
+ RichContent = message.SecondaryRichContent ?? message.RichContent,
+ Data = message.Data,
+ Sender = new()
+ {
+ FirstName = "AI",
+ LastName = "Assistant",
+ Role = AgentRole.Assistant
+ }
+ };
+ break;
+ case ChatEvent.AfterReceiveLlmStreamMessage:
+ model = new ChatResponseDto()
+ {
+ ConversationId = conv.ConversationId,
+ MessageId = message.MessageId,
+ Text = message.Content,
+ Sender = new()
+ {
+ FirstName = "AI",
+ LastName = "Assistant",
+ Role = AgentRole.Assistant
+ }
+ };
+
+ action = new ConversationSenderActionModel
+ {
+ ConversationId = conv.ConversationId,
+ SenderAction = SenderActionEnum.TypingOff
+ };
+
+ GenerateSenderAction(conv.ConversationId, action);
+ break;
+ case ChatEvent.OnIndicationReceived:
+ model = new ChatResponseDto
+ {
+ ConversationId = conv.ConversationId,
+ MessageId = message.MessageId,
+ Indication = message.Indication,
+ Sender = new()
+ {
+ FirstName = "AI",
+ LastName = "Assistant",
+ Role = AgentRole.Assistant
+ }
+ };
+ break;
}
OnReceiveAssistantMessage(value.EventName, model.ConversationId, model);
@@ -147,12 +157,12 @@ public class ChatHubObserver : IObserver
var chatHub = _services.GetRequiredService>();
if (settings.EventDispatchBy == EventDispatchType.Group)
{
- chatHub.Clients.Group(conversationId).SendAsync(GENERATE_SENDER_ACTION, action).ConfigureAwait(false).GetAwaiter().GetResult();
+ chatHub.Clients.Group(conversationId).SendAsync(ChatEvent.OnSenderActionGenerated, action).ConfigureAwait(false).GetAwaiter().GetResult();
}
else
{
var user = _services.GetRequiredService();
- chatHub.Clients.User(user.Id).SendAsync(GENERATE_SENDER_ACTION, action).ConfigureAwait(false).GetAwaiter().GetResult();
+ chatHub.Clients.User(user.Id).SendAsync(ChatEvent.OnSenderActionGenerated, action).ConfigureAwait(false).GetAwaiter().GetResult();
}
}
catch (Exception ex)
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Using.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Using.cs
index 4710509c..3473bb6c 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Using.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Using.cs
@@ -34,4 +34,5 @@ global using BotSharp.Abstraction.Realtime;
global using BotSharp.Abstraction.Realtime.Models;
global using BotSharp.Plugin.ChatHub.Settings;
global using BotSharp.Plugin.ChatHub.Enums;
-global using BotSharp.Plugin.ChatHub.Models.Stream;
\ No newline at end of file
+global using BotSharp.Plugin.ChatHub.Models.Stream;
+global using BotSharp.Plugin.ChatHub.Helpers;
\ No newline at end of file
diff --git a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs
index 1335d4dc..36a5126e 100644
--- a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs
@@ -1,8 +1,8 @@
using BotSharp.Abstraction.Files;
using BotSharp.Abstraction.Hooks;
-using BotSharp.Abstraction.Observables.Models;
+using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Core.Infrastructures.Streams;
-using BotSharp.Core.Observables.Queues;
+using BotSharp.Core.MessageHub;
using BotSharp.Plugin.DeepSeek.Providers;
using Microsoft.Extensions.Logging;
using OpenAI.Chat;
diff --git a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs
index 321b9aee..edeb7f58 100644
--- a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs
@@ -1,9 +1,9 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Hooks;
using BotSharp.Abstraction.Loggers;
-using BotSharp.Abstraction.Observables.Models;
+using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Core.Infrastructures.Streams;
-using BotSharp.Core.Observables.Queues;
+using BotSharp.Core.MessageHub;
using Microsoft.AspNetCore.SignalR;
using static LLama.Common.ChatHistory;
using static System.Net.Mime.MediaTypeNames;
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
index 57b44252..0e39cb34 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
@@ -1,8 +1,8 @@
using Azure;
using BotSharp.Abstraction.Hooks;
-using BotSharp.Abstraction.Observables.Models;
+using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Core.Infrastructures.Streams;
-using BotSharp.Core.Observables.Queues;
+using BotSharp.Core.MessageHub;
using BotSharp.Plugin.OpenAI.Models.Realtime;
using Fluid;
using OpenAI.Chat;
diff --git a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs
index 035e03e0..6e4a58f2 100644
--- a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs
@@ -1,9 +1,9 @@
using BotSharp.Abstraction.Agents;
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Loggers;
-using BotSharp.Abstraction.Observables.Models;
+using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Core.Infrastructures.Streams;
-using BotSharp.Core.Observables.Queues;
+using BotSharp.Core.MessageHub;
using Microsoft.AspNetCore.SignalR;
namespace BotSharp.Plugin.SparkDesk.Providers;
From 9a966c151cd4434b68913094097a431a918d3d23 Mon Sep 17 00:00:00 2001
From: Jicheng Lu
Date: Mon, 28 Jul 2025 22:08:28 -0500
Subject: [PATCH 11/51] fix
---
tests/BotSharp.LLM.Tests/Core/TestAgentService.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs
index 50d34701..5d4b07d0 100644
--- a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs
+++ b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs
@@ -16,7 +16,7 @@ namespace BotSharp.Plugin.Google.Core
return Task.FromResult(new Agent());
}
- public Task RefreshAgents()
+ public Task RefreshAgents(IEnumerable? agentIds = null)
{
return Task.FromResult("Refreshed successfully");
}
From dd024661d5cdab162edcbd052ffabe9186289e11 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Tue, 29 Jul 2025 10:32:55 -0500
Subject: [PATCH 12/51] rename
---
.../{ChatHubHelper.cs => EventEmitter.cs} | 17 +++---
.../Hooks/ChatHubConversationHook.cs | 2 +-
.../Hooks/StreamingLogHook.cs | 18 +-----
.../Hooks/WelcomeHook.cs | 2 +-
.../Observers/ChatHubObserver.cs | 55 ++++---------------
.../Core/TestAgentService.cs | 2 +-
6 files changed, 23 insertions(+), 73 deletions(-)
rename src/Plugins/BotSharp.Plugin.ChatHub/Helpers/{ChatHubHelper.cs => EventEmitter.cs} (61%)
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Helpers/ChatHubHelper.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Helpers/EventEmitter.cs
similarity index 61%
rename from src/Plugins/BotSharp.Plugin.ChatHub/Helpers/ChatHubHelper.cs
rename to src/Plugins/BotSharp.Plugin.ChatHub/Helpers/EventEmitter.cs
index 94bbc90d..821b173e 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Helpers/ChatHubHelper.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Helpers/EventEmitter.cs
@@ -3,9 +3,9 @@ using System.Runtime.CompilerServices;
namespace BotSharp.Plugin.ChatHub.Helpers;
-public class ChatHubHelper
+internal class EventEmitter
{
- public static async Task SendChatEvent(
+ internal static async Task SendChatEvent(
IServiceProvider services,
ILogger logger,
string @event,
@@ -21,13 +21,14 @@ public class ChatHubHelper
var settings = services.GetRequiredService();
var chatHub = services.GetRequiredService>();
- if (settings.EventDispatchBy == EventDispatchType.Group)
+ switch (settings.EventDispatchBy)
{
- await chatHub.Clients.Group(conversationId).SendAsync(@event, data);
- }
- else
- {
- await chatHub.Clients.User(userId).SendAsync(@event, data);
+ case EventDispatchType.Group when !string.IsNullOrEmpty(conversationId):
+ await chatHub.Clients.Group(conversationId).SendAsync(@event, data);
+ break;
+ case EventDispatchType.User when !string.IsNullOrEmpty(userId):
+ await chatHub.Clients.User(userId).SendAsync(@event, data);
+ break;
}
}
catch (Exception ex)
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
index 5cbc117e..52dd4f7c 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubConversationHook.cs
@@ -174,7 +174,7 @@ public class ChatHubConversationHook : ConversationHookBase
private async Task SendEvent(string @event, string conversationId, T data, [CallerMemberName] string callerName = "")
{
var user = _services.GetRequiredService();
- await ChatHubHelper.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, data, nameof(ChatHubConversationHook), callerName);
+ await EventEmitter.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, data, nameof(ChatHubConversationHook), callerName);
}
#endregion
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
index f036deab..73365115 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/StreamingLogHook.cs
@@ -228,7 +228,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
var conv = _services.GetRequiredService();
var routingCtx = _services.GetRequiredService();
var stateLog = BuildStateLog(conv.ConversationId, routingCtx.EntryAgentId, _state.GetStates(), message);
- //await SendStateLog(conv.ConversationId, routingCtx.EntryAgentId, _state.GetStates(), message);
await SendEvent(ChatEvent.OnConversateStateLogGenerated, conv.ConversationId, stateLog);
if (message.Role == AgentRole.Assistant)
@@ -248,7 +247,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.AgentResponse,
Log = log
};
- //await SendContentLog(conversationId, input);
await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
}
@@ -267,7 +265,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
- //await SendContentLog(conversationId, input);
await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
@@ -285,7 +282,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.FunctionCall,
Log = log
};
- //await SendContentLog(conversationId, input);
await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
@@ -314,7 +310,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
},
Log = log
};
- //await SendContentLog(conversationId, input);
await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
@@ -325,7 +320,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
if (stateChange == null) return;
- //await SendStateChange(conversationId, stateChange);
await SendEvent(ChatEvent.OnStateChangeGenerated, conversationId, BuildStateChangeLog(stateChange));
}
#endregion
@@ -340,7 +334,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"{agent.Name} is enqueued";
- //await SendAgentQueueLog(conversationId, log);
await SendEvent(ChatEvent.OnAgentQueueChanged, conversationId, BuildAgentQueueChangedLog(conversationId, log));
// Content log
@@ -356,7 +349,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- //await SendContentLog(conversationId, input);
await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
@@ -370,7 +362,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"{agent.Name} is dequeued";
- //await SendAgentQueueLog(conversationId, log);
await SendEvent(ChatEvent.OnAgentQueueChanged, conversationId, BuildAgentQueueChangedLog(conversationId, log));
// Content log
@@ -386,7 +377,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- //await SendContentLog(conversationId, input);
await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
@@ -400,7 +390,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"Agent queue is replaced from {fromAgent.Name} to {toAgent.Name}";
- //await SendAgentQueueLog(conversationId, log);
await SendEvent(ChatEvent.OnAgentQueueChanged, conversationId, BuildAgentQueueChangedLog(conversationId, log));
// Content log
@@ -416,7 +405,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- //await SendContentLog(conversationId, input);
await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
@@ -427,7 +415,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
// Agent queue log
var log = $"Agent queue is empty";
- //await SendAgentQueueLog(conversationId, log);
await SendEvent(ChatEvent.OnAgentQueueChanged, conversationId, BuildAgentQueueChangedLog(conversationId, log));
// Content log
@@ -443,7 +430,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- //await SendContentLog(conversationId, input);
await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
@@ -463,7 +449,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.AgentResponse,
Log = log
};
- //await SendContentLog(conversationId, input);
await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
@@ -482,7 +467,6 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
Source = ContentLogSource.HardRule,
Log = log
};
- //await SendContentLog(conversationId, input);
await SendEvent(ChatEvent.OnConversationContentLogGenerated, conversationId, BuildContentLog(input));
}
#endregion
@@ -492,7 +476,7 @@ public class StreamingLogHook : ConversationHookBase, IContentGeneratingHook, IR
private async Task SendEvent(string @event, string conversationId, T data, [CallerMemberName] string callerName = "")
{
var user = _services.GetRequiredService();
- await ChatHubHelper.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, data, nameof(StreamingLogHook), callerName);
+ await EventEmitter.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, data, nameof(StreamingLogHook), callerName);
}
private ContentLogOutputModel BuildContentLog(ContentLogInputModel input)
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs
index 3db8017b..8a184568 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/WelcomeHook.cs
@@ -89,6 +89,6 @@ public class WelcomeHook : ConversationHookBase
private async Task SendEvent(string @event, string conversationId, T data, [CallerMemberName] string callerName = "")
{
var user = _services.GetRequiredService();
- await ChatHubHelper.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, data, nameof(WelcomeHook), callerName);
+ await EventEmitter.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, data, nameof(WelcomeHook), callerName);
}
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
index 71b07b9d..3c1799ea 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
@@ -4,6 +4,7 @@ using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Abstraction.SideCar;
using BotSharp.Plugin.ChatHub.Hooks;
using Microsoft.AspNetCore.SignalR;
+using System.Runtime.CompilerServices;
namespace BotSharp.Plugin.ChatHub.Observers;
@@ -60,7 +61,7 @@ public class ChatHubObserver : IObserver
SenderAction = SenderActionEnum.TypingOn
};
- GenerateSenderAction(conv.ConversationId, action);
+ SendEvent(ChatEvent.OnSenderActionGenerated, conv.ConversationId, action);
break;
case ChatEvent.OnReceiveLlmStreamMessage:
model = new ChatResponseDto()
@@ -99,7 +100,7 @@ public class ChatHubObserver : IObserver
SenderAction = SenderActionEnum.TypingOff
};
- GenerateSenderAction(conv.ConversationId, action);
+ SendEvent(ChatEvent.OnSenderActionGenerated, conv.ConversationId, action);
break;
case ChatEvent.OnIndicationReceived:
model = new ChatResponseDto
@@ -117,7 +118,7 @@ public class ChatHubObserver : IObserver
break;
}
- OnReceiveAssistantMessage(value.EventName, model.ConversationId, model);
+ SendEvent(value.EventName, model.ConversationId, model);
}
private bool AllowSendingMessage()
@@ -126,48 +127,12 @@ public class ChatHubObserver : IObserver
return sidecar == null || !sidecar.IsEnabled;
}
- private void OnReceiveAssistantMessage(string @event, string conversationId, ChatResponseDto model)
+ #region Private methods
+ private void SendEvent(string @event, string conversationId, T data, [CallerMemberName] string callerName = "")
{
- try
- {
- var settings = _services.GetRequiredService();
- var chatHub = _services.GetRequiredService>();
-
- if (settings.EventDispatchBy == EventDispatchType.Group)
- {
- chatHub.Clients.Group(conversationId).SendAsync(@event, model).ConfigureAwait(false).GetAwaiter().GetResult();
- }
- else
- {
- var user = _services.GetRequiredService();
- chatHub.Clients.User(user.Id).SendAsync(@event, model).ConfigureAwait(false).GetAwaiter().GetResult();
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to receive assistant message in {nameof(ChatHubConversationHook)} (conversation id: {conversationId})");
- }
- }
-
- private void GenerateSenderAction(string conversationId, ConversationSenderActionModel action)
- {
- try
- {
- var settings = _services.GetRequiredService();
- var chatHub = _services.GetRequiredService>();
- if (settings.EventDispatchBy == EventDispatchType.Group)
- {
- chatHub.Clients.Group(conversationId).SendAsync(ChatEvent.OnSenderActionGenerated, action).ConfigureAwait(false).GetAwaiter().GetResult();
- }
- else
- {
- var user = _services.GetRequiredService();
- chatHub.Clients.User(user.Id).SendAsync(ChatEvent.OnSenderActionGenerated, action).ConfigureAwait(false).GetAwaiter().GetResult();
- }
- }
- catch (Exception ex)
- {
- _logger.LogWarning(ex, $"Failed to generate sender action in {nameof(ChatHubConversationHook)} (conversation id: {conversationId})");
- }
+ var user = _services.GetRequiredService();
+ EventEmitter.SendChatEvent(_services, _logger, @event, conversationId, user?.Id, data, nameof(ChatHubObserver), callerName)
+ .ConfigureAwait(false).GetAwaiter().GetResult();
}
+ #endregion
}
diff --git a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs
index 50d34701..5d4b07d0 100644
--- a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs
+++ b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs
@@ -16,7 +16,7 @@ namespace BotSharp.Plugin.Google.Core
return Task.FromResult(new Agent());
}
- public Task RefreshAgents()
+ public Task RefreshAgents(IEnumerable? agentIds = null)
{
return Task.FromResult("Refreshed successfully");
}
From d1b5ad592dfe2cdbaba45bb285764b3546c51db9 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Tue, 29 Jul 2025 11:17:33 -0500
Subject: [PATCH 13/51] add conversation observer
---
.../Conversations/ConversationPlugin.cs | 15 ++++++--
.../Observers/ConversationObserver.cs | 34 +++++++++++++++++++
.../Routing/RoutingService.InvokeFunction.cs | 6 ----
.../Observers/ChatHubObserver.cs | 10 +++---
.../Providers/Chat/ChatCompletionProvider.cs | 9 ++---
5 files changed, 55 insertions(+), 19 deletions(-)
create mode 100644 src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
index 2f0af290..cef32cad 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
@@ -6,17 +6,18 @@ using BotSharp.Abstraction.Plugins.Models;
using BotSharp.Abstraction.Settings;
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Instructs;
+using BotSharp.Core.MessageHub;
+using BotSharp.Core.MessageHub.Observers;
using BotSharp.Core.Messaging;
using BotSharp.Core.Routing.Reasoning;
using BotSharp.Core.Templating;
using BotSharp.Core.Translation;
+using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
-using BotSharp.Abstraction.MessageHub.Models;
-using BotSharp.Core.MessageHub;
namespace BotSharp.Core.Conversations;
-public class ConversationPlugin : IBotSharpPlugin
+public class ConversationPlugin : IBotSharpPlugin, IBotSharpAppPlugin
{
public string Id => "99e9b971-a9f1-4273-84da-876d2873d192";
public string Name => "Conversation";
@@ -67,4 +68,12 @@ public class ConversationPlugin : IBotSharpPlugin
menu.Add(new PluginMenuDef("Conversation", link: "page/conversation", icon: "bx bx-conversation", weight: section.Weight + 5));
return true;
}
+
+ public void Configure(IApplicationBuilder app)
+ {
+ var services = app.ApplicationServices;
+ var queue = services.GetRequiredService>();
+ var logger = services.GetRequiredService>>();
+ queue.Events.Subscribe(new ConversationObserver(logger));
+ }
}
diff --git a/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs b/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
new file mode 100644
index 00000000..dd4b528c
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
@@ -0,0 +1,34 @@
+namespace BotSharp.Core.MessageHub.Observers;
+
+public class ConversationObserver : IObserver
+{
+ private readonly ILogger _logger;
+ private IServiceProvider _services;
+
+ public ConversationObserver(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public void OnCompleted()
+ {
+ _logger.LogWarning($"{nameof(ConversationObserver)} receives complete notification.");
+ }
+
+ public void OnError(Exception error)
+ {
+ _logger.LogError(error, $"{nameof(ConversationObserver)} receives error notification: {error.Message}");
+ }
+
+ public void OnNext(HubObserveData value)
+ {
+ _services = value.ServiceProvider;
+
+ var progress = _services.GetRequiredService();
+ if (value.EventName == ChatEvent.OnIndicationReceived
+ && progress.OnFunctionExecuting != null)
+ {
+ progress.OnFunctionExecuting(value.Data).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
index 0922dcdb..3660f61f 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
@@ -25,12 +25,6 @@ public partial class RoutingService
clonedMessage.FunctionName = name;
clonedMessage.Indication = await funcExecutor.GetIndicatorAsync(message);
- //var progressService = _services.GetService();
- //if (progressService?.OnFunctionExecuting != null)
- //{
- // await progressService.OnFunctionExecuting(clonedMessage);
- //}
-
var messageHub = _services.GetRequiredService>();
messageHub.Push(new()
{
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
index 3c1799ea..8f467908 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
@@ -2,8 +2,6 @@ using BotSharp.Abstraction.Conversations.Dtos;
using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Abstraction.SideCar;
-using BotSharp.Plugin.ChatHub.Hooks;
-using Microsoft.AspNetCore.SignalR;
using System.Runtime.CompilerServices;
namespace BotSharp.Plugin.ChatHub.Observers;
@@ -32,8 +30,6 @@ public class ChatHubObserver : IObserver
{
_services = value.ServiceProvider;
- if (!AllowSendingMessage()) return;
-
var message = value.Data;
var model = new ChatResponseDto();
var action = new ConversationSenderActionModel();
@@ -42,6 +38,8 @@ public class ChatHubObserver : IObserver
switch (value.EventName)
{
case ChatEvent.BeforeReceiveLlmStreamMessage:
+ if (!AllowSendingMessage()) return;
+
model = new ChatResponseDto()
{
ConversationId = conv.ConversationId,
@@ -64,6 +62,8 @@ public class ChatHubObserver : IObserver
SendEvent(ChatEvent.OnSenderActionGenerated, conv.ConversationId, action);
break;
case ChatEvent.OnReceiveLlmStreamMessage:
+ if (!AllowSendingMessage()) return;
+
model = new ChatResponseDto()
{
ConversationId = conv.ConversationId,
@@ -81,6 +81,8 @@ public class ChatHubObserver : IObserver
};
break;
case ChatEvent.AfterReceiveLlmStreamMessage:
+ if (!AllowSendingMessage()) return;
+
model = new ChatResponseDto()
{
ConversationId = conv.ConversationId,
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
index 0e39cb34..e4567cfb 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
@@ -1,10 +1,7 @@
-using Azure;
using BotSharp.Abstraction.Hooks;
using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Core.Infrastructures.Streams;
using BotSharp.Core.MessageHub;
-using BotSharp.Plugin.OpenAI.Models.Realtime;
-using Fluid;
using OpenAI.Chat;
namespace BotSharp.Plugin.OpenAI.Providers.Chat;
@@ -204,7 +201,7 @@ public class ChatCompletionProvider : IChatCompletion
hub.Push(new()
{
ServiceProvider = _services,
- EventName = "BeforeReceiveLlmStreamMessage",
+ EventName = ChatEvent.BeforeReceiveLlmStreamMessage,
Data = new RoleDialogModel(AgentRole.Assistant, string.Empty)
{
CurrentAgentId = agent.Id,
@@ -248,7 +245,7 @@ public class ChatCompletionProvider : IChatCompletion
hub.Push(new()
{
ServiceProvider = _services,
- EventName = "OnReceiveLlmStreamMessage",
+ EventName = ChatEvent.OnReceiveLlmStreamMessage,
Data = content
});
}
@@ -291,7 +288,7 @@ public class ChatCompletionProvider : IChatCompletion
hub.Push(new()
{
ServiceProvider = _services,
- EventName = "AfterReceiveLlmStreamMessage",
+ EventName = ChatEvent.AfterReceiveLlmStreamMessage,
Data = responseMessage
});
From 7d3579c1ec36bc38cd3c67906b9ab7cacc6ef699 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Tue, 29 Jul 2025 13:55:53 -0500
Subject: [PATCH 14/51] refine message hub
---
.../Conversations/Models/RoleDialogModel.cs | 2 +-
.../MessageHub/Models/HubObserveData.cs | 4 ++--
.../BotSharp.Core/Conversations/ConversationPlugin.cs | 6 +++---
.../BotSharp.Core/Demo/Functions/GetWeatherFn.cs | 2 +-
src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs | 2 +-
.../MessageHub/Observers/ConversationObserver.cs | 6 +++---
.../BotSharp.Core/Routing/RoutingService.InvokeFunction.cs | 2 +-
.../Providers/Chat/ChatCompletionProvider.cs | 2 +-
src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs | 4 ++--
.../BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs | 4 ++--
.../Providers/Chat/ChatCompletionProvider.cs | 2 +-
.../Providers/ChatCompletionProvider.cs | 2 +-
.../Providers/Chat/ChatCompletionProvider.cs | 2 +-
.../Providers/ChatCompletionProvider.cs | 2 +-
14 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
index 6a8af2d6..6bc82f81 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs
@@ -120,7 +120,7 @@ public class RoleDialogModel : ITrackableMessage
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
public bool IsStreaming { get; set; }
- private RoleDialogModel()
+ public RoleDialogModel()
{
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
index 403adbc6..277893ad 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
@@ -1,7 +1,7 @@
namespace BotSharp.Abstraction.MessageHub.Models;
-public class HubObserveData : ObserveDataBase
+public class HubObserveData : ObserveDataBase where TData : class, new()
{
public string EventName { get; set; } = null!;
- public RoleDialogModel Data { get; set; } = null!;
+ public TData Data { get; set; } = null!;
}
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
index cef32cad..1a36875c 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
@@ -44,7 +44,7 @@ public class ConversationPlugin : IBotSharpPlugin, IBotSharpAppPlugin
return settingService.Bind("GoogleApi");
});
- services.AddSingleton>();
+ services.AddSingleton>>();
services.AddScoped();
services.AddScoped();
@@ -72,8 +72,8 @@ public class ConversationPlugin : IBotSharpPlugin, IBotSharpAppPlugin
public void Configure(IApplicationBuilder app)
{
var services = app.ApplicationServices;
- var queue = services.GetRequiredService>();
- var logger = services.GetRequiredService>>();
+ var queue = services.GetRequiredService>>();
+ var logger = services.GetRequiredService>>>();
queue.Events.Subscribe(new ConversationObserver(logger));
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs b/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
index b8bb0f39..a22f5cec 100644
--- a/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
+++ b/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
@@ -18,7 +18,7 @@ public class GetWeatherFn : IFunctionCallback
public async Task Execute(RoleDialogModel message)
{
var conv = _services.GetRequiredService();
- var messageHub = _services.GetRequiredService>();
+ var messageHub = _services.GetRequiredService>>();
await Task.Delay(1000);
diff --git a/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs b/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
index a104f4f6..8e99f898 100644
--- a/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
@@ -2,7 +2,7 @@ using System.Reactive.Subjects;
namespace BotSharp.Core.MessageHub;
-public class MessageHub where T : class
+public class MessageHub where T : class, new()
{
private readonly ILogger> _logger;
private readonly ISubject _observable = Subject.Synchronize(new Subject());
diff --git a/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs b/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
index dd4b528c..cad778b4 100644
--- a/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
@@ -1,6 +1,6 @@
namespace BotSharp.Core.MessageHub.Observers;
-public class ConversationObserver : IObserver
+public class ConversationObserver : IObserver>
{
private readonly ILogger _logger;
private IServiceProvider _services;
@@ -20,11 +20,11 @@ public class ConversationObserver : IObserver
_logger.LogError(error, $"{nameof(ConversationObserver)} receives error notification: {error.Message}");
}
- public void OnNext(HubObserveData value)
+ public void OnNext(HubObserveData value)
{
_services = value.ServiceProvider;
-
var progress = _services.GetRequiredService();
+
if (value.EventName == ChatEvent.OnIndicationReceived
&& progress.OnFunctionExecuting != null)
{
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
index 3660f61f..f693f4dd 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
@@ -25,7 +25,7 @@ public partial class RoutingService
clonedMessage.FunctionName = name;
clonedMessage.Indication = await funcExecutor.GetIndicatorAsync(message);
- var messageHub = _services.GetRequiredService>();
+ var messageHub = _services.GetRequiredService>>();
messageHub.Push(new()
{
EventName = ChatEvent.OnIndicationReceived,
diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
index d3c6e14a..b19bdaef 100644
--- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs
@@ -212,7 +212,7 @@ public class ChatCompletionProvider : IChatCompletion
var chatClient = client.GetChatClient(_model);
var (prompt, messages, options) = PrepareOptions(agent, conversations);
- var hub = _services.GetRequiredService>();
+ var hub = _services.GetRequiredService>>();
var messageId = conversations.LastOrDefault()?.MessageId ?? string.Empty;
var contentHooks = _services.GetHooks(agent.Id);
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
index d771d0a2..38bd936c 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
@@ -36,8 +36,8 @@ public class ChatHubPlugin : IBotSharpPlugin, IBotSharpAppPlugin
public void Configure(IApplicationBuilder app)
{
var services = app.ApplicationServices;
- var queue = services.GetRequiredService>();
- var logger = services.GetRequiredService>>();
+ var queue = services.GetRequiredService>>();
+ var logger = services.GetRequiredService>>>();
queue.Events.Subscribe(new ChatHubObserver(logger));
}
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
index 8f467908..75aa0cd3 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
@@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
namespace BotSharp.Plugin.ChatHub.Observers;
-public class ChatHubObserver : IObserver
+public class ChatHubObserver : IObserver>
{
private readonly ILogger _logger;
private IServiceProvider _services;
@@ -26,7 +26,7 @@ public class ChatHubObserver : IObserver
_logger.LogError(error, $"{nameof(ChatHubObserver)} receives error notification: {error.Message}");
}
- public void OnNext(HubObserveData value)
+ public void OnNext(HubObserveData value)
{
_services = value.ServiceProvider;
diff --git a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs
index 36a5126e..a404aef1 100644
--- a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs
@@ -179,7 +179,7 @@ public class ChatCompletionProvider : IChatCompletion
var chatClient = client.GetChatClient(_model);
var (prompt, messages, options) = PrepareOptions(agent, conversations);
- var hub = _services.GetRequiredService>();
+ var hub = _services.GetRequiredService>>();
var messageId = conversations.LastOrDefault()?.MessageId ?? string.Empty;
var contentHooks = _services.GetHooks(agent.Id);
diff --git a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs
index edeb7f58..a4f18d31 100644
--- a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs
@@ -183,7 +183,7 @@ public class ChatCompletionProvider : IChatCompletion
_logger.LogInformation(agent.Instruction);
}
- var hub = _services.GetRequiredService>();
+ var hub = _services.GetRequiredService>>();
var messageId = conversations.LastOrDefault()?.MessageId ?? string.Empty;
hub.Push(new()
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
index e4567cfb..86447dca 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs
@@ -188,7 +188,7 @@ public class ChatCompletionProvider : IChatCompletion
var chatClient = client.GetChatClient(_model);
var (prompt, messages, options) = PrepareOptions(agent, conversations);
- var hub = _services.GetRequiredService>();
+ var hub = _services.GetRequiredService>>();
var messageId = conversations.LastOrDefault()?.MessageId ?? string.Empty;
var contentHooks = _services.GetHooks(agent.Id);
diff --git a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs
index 6e4a58f2..c2e10def 100644
--- a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs
@@ -152,7 +152,7 @@ public class ChatCompletionProvider : IChatCompletion
var client = new SparkDeskClient(appId: _settings.AppId, apiKey: _settings.ApiKey, apiSecret: _settings.ApiSecret);
var (prompt, messages, funcall) = PrepareOptions(agent, conversations);
var messageId = conversations.LastOrDefault()?.MessageId ?? string.Empty;
- var hub = _services.GetRequiredService>();
+ var hub = _services.GetRequiredService>>();
hub.Push(new()
{
From 6419ca3f8fb470a798c797982acfebf43976ff64 Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Tue, 29 Jul 2025 13:58:27 -0500
Subject: [PATCH 15/51] add logging
---
.../BotSharp.Core/MessageHub/Observers/ConversationObserver.cs | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs b/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
index cad778b4..15b44157 100644
--- a/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
@@ -28,6 +28,9 @@ public class ConversationObserver : IObserver>
if (value.EventName == ChatEvent.OnIndicationReceived
&& progress.OnFunctionExecuting != null)
{
+#if DEBUG
+ _logger.LogCritical($"Receiving {value.EventName} in {nameof(ConversationObserver)}");
+#endif
progress.OnFunctionExecuting(value.Data).ConfigureAwait(false).GetAwaiter().GetResult();
}
}
From d523798d41e0b488dcb8304efea770ede6292c3d Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Thu, 31 Jul 2025 17:48:51 -0500
Subject: [PATCH 16/51] refine observers
---
.../MessageHub/Models/ObserveDataBase.cs | 3 +-
.../MessageHub/Models/ObserverSubscription.cs | 22 +++++++++
.../Observers/BotSharpObserverBase.cs | 9 ++++
.../MessageHub/Observers/IBotSharpObserver.cs | 9 ++++
.../MessageHub/Services/IObserverService.cs | 8 +++
.../Services/ObserverSubscriptionContainer.cs | 49 +++++++++++++++++++
.../Conversations/ConversationPlugin.cs | 13 +++--
.../Demo/Functions/GetWeatherFn.cs | 14 +++++-
.../BotSharp.Core/MessageHub/MessageHub.cs | 2 +-
.../Observers/ConversationObserver.cs | 33 +++++++++----
.../MessageHub/Services/ObserverService.cs | 43 ++++++++++++++++
.../Routing/RoutingService.InvokeFunction.cs | 2 +
.../Controllers/ConversationController.cs | 6 +++
.../BotSharp.Plugin.ChatHub/ChatHubPlugin.cs | 11 +++--
.../Observers/ChatHubObserver.cs | 22 ++++++++-
15 files changed, 223 insertions(+), 23 deletions(-)
create mode 100644 src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs
create mode 100644 src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs
create mode 100644 src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs
create mode 100644 src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs
create mode 100644 src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/ObserverSubscriptionContainer.cs
create mode 100644 src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverService.cs
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
index 62c2950d..c881b49d 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
@@ -1,6 +1,7 @@
namespace BotSharp.Abstraction.MessageHub.Models;
-public abstract class ObserveDataBase
+public class ObserveDataBase
{
public IServiceProvider ServiceProvider { get; set; } = null!;
+ public string RefId { get; set; } = null!;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs
new file mode 100644
index 00000000..ee8345cd
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs
@@ -0,0 +1,22 @@
+using BotSharp.Abstraction.MessageHub.Observers;
+
+namespace BotSharp.Abstraction.MessageHub.Models;
+
+public class ObserverSubscription
+{
+ public IBotSharpObserver Observer { get; set; }
+ public IDisposable Subscription { get; set; }
+
+ public ObserverSubscription()
+ {
+
+ }
+
+ public ObserverSubscription(
+ IBotSharpObserver observer,
+ IDisposable subscription)
+ {
+ Observer = observer;
+ Subscription = subscription;
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs
new file mode 100644
index 00000000..7b824075
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs
@@ -0,0 +1,9 @@
+namespace BotSharp.Abstraction.MessageHub.Observers;
+
+public abstract class BotSharpObserverBase
+{
+ protected BotSharpObserverBase()
+ {
+
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs
new file mode 100644
index 00000000..edbb4df2
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs
@@ -0,0 +1,9 @@
+namespace BotSharp.Abstraction.MessageHub.Observers;
+
+public interface IBotSharpObserver : IObserver
+{
+ //string Name { get; }
+ bool IsActive { get; }
+ void Activate();
+ void Deactivate();
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs
new file mode 100644
index 00000000..337f2bbf
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs
@@ -0,0 +1,8 @@
+using BotSharp.Abstraction.MessageHub.Models;
+
+namespace BotSharp.Abstraction.MessageHub.Services;
+
+public interface IObserverService
+{
+ ObserverSubscriptionContainer RegisterObservers(string refId) where T : ObserveDataBase;
+}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/ObserverSubscriptionContainer.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/ObserverSubscriptionContainer.cs
new file mode 100644
index 00000000..860da06d
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/ObserverSubscriptionContainer.cs
@@ -0,0 +1,49 @@
+using BotSharp.Abstraction.MessageHub.Models;
+
+namespace BotSharp.Abstraction.MessageHub.Services;
+
+public class ObserverSubscriptionContainer : IDisposable
+{
+ private IList> _subscriptions = [];
+ private bool _disposed = false;
+
+ public ObserverSubscriptionContainer()
+ {
+
+ }
+
+ public ObserverSubscriptionContainer(
+ IList> subscriptions)
+ {
+ _subscriptions = subscriptions;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposed)
+ {
+ if (disposing)
+ {
+#if DEBUG
+ Console.WriteLine($"Start disposing subscriptions...");
+#endif
+ // Unregister all observers
+ foreach (var item in _subscriptions)
+ {
+ item.Observer.Deactivate();
+ item.Subscription.Dispose();
+ }
+ _subscriptions.Clear();
+#if DEBUG
+ Console.WriteLine($"End disposing subscriptions...");
+#endif
+ }
+ _disposed = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
index 1a36875c..d3797466 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
@@ -1,5 +1,8 @@
using BotSharp.Abstraction.Google.Settings;
using BotSharp.Abstraction.Instructs;
+using BotSharp.Abstraction.MessageHub;
+using BotSharp.Abstraction.MessageHub.Observers;
+using BotSharp.Abstraction.MessageHub.Services;
using BotSharp.Abstraction.Messaging;
using BotSharp.Abstraction.Planning;
using BotSharp.Abstraction.Plugins.Models;
@@ -8,6 +11,7 @@ using BotSharp.Abstraction.Templating;
using BotSharp.Core.Instructs;
using BotSharp.Core.MessageHub;
using BotSharp.Core.MessageHub.Observers;
+using BotSharp.Core.MessageHub.Services;
using BotSharp.Core.Messaging;
using BotSharp.Core.Routing.Reasoning;
using BotSharp.Core.Templating;
@@ -45,6 +49,8 @@ public class ConversationPlugin : IBotSharpPlugin, IBotSharpAppPlugin
});
services.AddSingleton>>();
+ //services.AddScoped>, ConversationObserver>();
+ services.AddScoped();
services.AddScoped();
services.AddScoped();
@@ -71,9 +77,8 @@ public class ConversationPlugin : IBotSharpPlugin, IBotSharpAppPlugin
public void Configure(IApplicationBuilder app)
{
- var services = app.ApplicationServices;
- var queue = services.GetRequiredService>>();
- var logger = services.GetRequiredService>>>();
- queue.Events.Subscribe(new ConversationObserver(logger));
+ //var services = app.ApplicationServices;
+ //var queue = services.GetRequiredService>>();
+ //var logger = services.GetRequiredService>>>();
}
}
diff --git a/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs b/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
index a22f5cec..41b96399 100644
--- a/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
+++ b/src/Infrastructure/BotSharp.Core/Demo/Functions/GetWeatherFn.cs
@@ -1,5 +1,6 @@
using BotSharp.Abstraction.Functions;
using BotSharp.Core.MessageHub;
+using System.Text.Json.Serialization;
namespace BotSharp.Core.Demo.Functions;
@@ -17,26 +18,29 @@ public class GetWeatherFn : IFunctionCallback
public async Task Execute(RoleDialogModel message)
{
+ var args = JsonSerializer.Deserialize(message.FunctionArgs);
var conv = _services.GetRequiredService();
var messageHub = _services.GetRequiredService>>();
await Task.Delay(1000);
- message.Indication = "Start querying weather data";
+ message.Indication = $"Start querying weather data in {args?.City}";
messageHub.Push(new()
{
EventName = ChatEvent.OnIndicationReceived,
Data = message,
+ RefId = conv.ConversationId,
ServiceProvider = _services
});
await Task.Delay(1500);
- message.Indication = "Still working on it";
+ message.Indication = $"Still working on it, {args?.City}";
messageHub.Push(new()
{
EventName = ChatEvent.OnIndicationReceived,
Data = message,
+ RefId = conv.ConversationId,
ServiceProvider = _services
});
@@ -46,4 +50,10 @@ public class GetWeatherFn : IFunctionCallback
message.StopCompletion = false;
return true;
}
+}
+
+class WeatherLocation
+{
+ [JsonPropertyName("city")]
+ public string City { get; set; }
}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs b/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
index 8e99f898..8f01612d 100644
--- a/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/MessageHub.cs
@@ -2,7 +2,7 @@ using System.Reactive.Subjects;
namespace BotSharp.Core.MessageHub;
-public class MessageHub where T : class, new()
+public class MessageHub where T : ObserveDataBase
{
private readonly ILogger> _logger;
private readonly ISubject _observable = Subject.Synchronize(new Subject());
diff --git a/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs b/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
index 15b44157..6c22f753 100644
--- a/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/Observers/ConversationObserver.cs
@@ -1,15 +1,31 @@
+using BotSharp.Abstraction.MessageHub.Observers;
+
namespace BotSharp.Core.MessageHub.Observers;
-public class ConversationObserver : IObserver>
+public class ConversationObserver : IBotSharpObserver>
{
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
private IServiceProvider _services;
+ private bool _isActive;
- public ConversationObserver(ILogger logger)
+ public ConversationObserver(
+ ILogger logger)
{
_logger = logger;
}
+ public bool IsActive => _isActive;
+
+ public void Activate()
+ {
+ _isActive = true;
+ }
+
+ public void Deactivate()
+ {
+ _isActive = false;
+ }
+
public void OnCompleted()
{
_logger.LogWarning($"{nameof(ConversationObserver)} receives complete notification.");
@@ -23,15 +39,14 @@ public class ConversationObserver : IObserver>
public void OnNext(HubObserveData value)
{
_services = value.ServiceProvider;
- var progress = _services.GetRequiredService();
+ //var progress = _services.GetRequiredService();
- if (value.EventName == ChatEvent.OnIndicationReceived
- && progress.OnFunctionExecuting != null)
+ if (value.EventName == ChatEvent.OnIndicationReceived)
{
-#if DEBUG
- _logger.LogCritical($"Receiving {value.EventName} in {nameof(ConversationObserver)}");
+#if !DEBUG
+ _logger.LogCritical($"Receiving {value.EventName} ({value.Data.Indication}) in {nameof(ConversationObserver)}");
#endif
- progress.OnFunctionExecuting(value.Data).ConfigureAwait(false).GetAwaiter().GetResult();
+ //progress.OnFunctionExecuting(value.Data).ConfigureAwait(false).GetAwaiter().GetResult();
}
}
}
diff --git a/src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverService.cs b/src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverService.cs
new file mode 100644
index 00000000..c2165fc6
--- /dev/null
+++ b/src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverService.cs
@@ -0,0 +1,43 @@
+using BotSharp.Abstraction.MessageHub.Observers;
+using BotSharp.Abstraction.MessageHub.Services;
+using System.Reactive.Linq;
+
+namespace BotSharp.Core.MessageHub.Services;
+
+public class ObserverService : IObserverService
+{
+ private readonly IServiceProvider _services;
+
+ public ObserverService(
+ IServiceProvider services)
+ {
+ _services = services;
+ }
+
+ public ObserverSubscriptionContainer RegisterObservers(string refId) where T : ObserveDataBase
+ {
+ var subscriptions = new List>();
+ var observers = _services.GetServices>()
+ .Where(x => !x.IsActive)
+ .ToList();
+
+ if (observers.IsNullOrEmpty())
+ {
+ return new();
+ }
+
+ var messageHub = _services.GetRequiredService>();
+ foreach (var observer in observers)
+ {
+ observer.Activate();
+ var sub = messageHub.Events.Where(x => x.RefId == refId).Subscribe(observer);
+ subscriptions.Add(new()
+ {
+ Observer = observer,
+ Subscription = sub
+ });
+ }
+
+ return new(subscriptions);
+ }
+}
diff --git a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
index f693f4dd..a009b88f 100644
--- a/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
+++ b/src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs
@@ -25,11 +25,13 @@ public partial class RoutingService
clonedMessage.FunctionName = name;
clonedMessage.Indication = await funcExecutor.GetIndicatorAsync(message);
+ var conv = _services.GetRequiredService();
var messageHub = _services.GetRequiredService>>();
messageHub.Push(new()
{
EventName = ChatEvent.OnIndicationReceived,
Data = clonedMessage,
+ RefId = conv.ConversationId,
ServiceProvider = _services
});
diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
index bd131ca1..345b926d 100644
--- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
+++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs
@@ -1,10 +1,13 @@
using BotSharp.Abstraction.Files.Constants;
using BotSharp.Abstraction.Files.Enums;
using BotSharp.Abstraction.Files.Utilities;
+using BotSharp.Abstraction.MessageHub.Models;
+using BotSharp.Abstraction.MessageHub.Services;
using BotSharp.Abstraction.Options;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Users.Dtos;
using BotSharp.Core.Infrastructures;
+using System.ComponentModel;
namespace BotSharp.OpenAPI.Controllers;
@@ -343,6 +346,9 @@ public class ConversationController : ControllerBase
[FromRoute] string conversationId,
[FromBody] NewMessageModel input)
{
+ var observerService = _services.GetRequiredService();
+ using var container = observerService.RegisterObservers>(conversationId);
+
var conv = _services.GetRequiredService();
var inputMsg = new RoleDialogModel(AgentRole.User, input.Text)
{
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
index 38bd936c..4c48ef74 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatHubPlugin.cs
@@ -1,6 +1,8 @@
using BotSharp.Abstraction.Crontab;
using BotSharp.Abstraction.MessageHub.Models;
+using BotSharp.Abstraction.MessageHub.Observers;
using BotSharp.Core.MessageHub;
+using BotSharp.Core.MessageHub.Observers;
using BotSharp.Plugin.ChatHub.Hooks;
using BotSharp.Plugin.ChatHub.Observers;
using Microsoft.AspNetCore.Builder;
@@ -24,6 +26,8 @@ public class ChatHubPlugin : IBotSharpPlugin, IBotSharpAppPlugin
config.Bind("ChatHub", settings);
services.AddSingleton(x => settings);
+ services.AddScoped>, ChatHubObserver>();
+
// Register hooks
services.AddScoped();
services.AddScoped();
@@ -35,9 +39,8 @@ public class ChatHubPlugin : IBotSharpPlugin, IBotSharpAppPlugin
public void Configure(IApplicationBuilder app)
{
- var services = app.ApplicationServices;
- var queue = services.GetRequiredService>>();
- var logger = services.GetRequiredService>>>();
- queue.Events.Subscribe(new ChatHubObserver(logger));
+ //var services = app.ApplicationServices;
+ //var queue = services.GetRequiredService>>();
+ //var logger = services.GetRequiredService>>>();
}
}
diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
index 75aa0cd3..bf2b1ed8 100644
--- a/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
+++ b/src/Plugins/BotSharp.Plugin.ChatHub/Observers/ChatHubObserver.cs
@@ -1,21 +1,37 @@
using BotSharp.Abstraction.Conversations.Dtos;
using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.MessageHub.Models;
+using BotSharp.Abstraction.MessageHub.Observers;
using BotSharp.Abstraction.SideCar;
+using BotSharp.Core.MessageHub.Observers;
using System.Runtime.CompilerServices;
namespace BotSharp.Plugin.ChatHub.Observers;
-public class ChatHubObserver : IObserver>
+public class ChatHubObserver : IBotSharpObserver>
{
private readonly ILogger _logger;
private IServiceProvider _services;
+ private bool _isActive = false;
- public ChatHubObserver(ILogger logger)
+ public ChatHubObserver(
+ ILogger logger)
{
_logger = logger;
}
+ public bool IsActive => _isActive;
+
+ public void Activate()
+ {
+ _isActive = true;
+ }
+
+ public void Deactivate()
+ {
+ _isActive = false;
+ }
+
public void OnCompleted()
{
_logger.LogWarning($"{nameof(ChatHubObserver)} receives complete notification.");
@@ -117,6 +133,8 @@ public class ChatHubObserver : IObserver>
Role = AgentRole.Assistant
}
};
+
+ _logger.LogCritical($"Receiving {value.EventName} ({value.Data.Indication}) in {nameof(ChatHubObserver)} - {conv.ConversationId}");
break;
}
From e32d82044051c228760e9edf7a902af4959b5c1b Mon Sep 17 00:00:00 2001
From: Jicheng Lu <103353@smsassist.com>
Date: Fri, 1 Aug 2025 13:23:46 -0500
Subject: [PATCH 17/51] refine observer
---
.../MessageHub/Models/HubObserveData.cs | 1 -
.../MessageHub/Models/ObserveDataBase.cs | 2 +-
.../MessageHub/Models/ObserverSubscription.cs | 6 ++
.../Observers/BotSharpObserverBase.cs | 34 +++++++++-
.../MessageHub/Observers/IBotSharpObserver.cs | 4 +-
.../MessageHub/Services/IObserverService.cs | 4 +-
.../Services/ObserverSubscriptionContainer.cs | 49 --------------
.../Conversations/ConversationPlugin.cs | 7 +-
.../Demo/Functions/GetWeatherFn.cs | 8 +--
.../Observers/ConversationObserver.cs | 34 ++++------
.../MessageHub/Services/ObserverService.cs | 41 ++++++++++--
.../Services/ObserverSubscriptionContainer.cs | 64 +++++++++++++++++++
.../Routing/RoutingService.InvokeFunction.cs | 7 +-
.../Controllers/ConversationController.cs | 7 +-
.../Providers/Chat/ChatCompletionProvider.cs | 15 +++--
.../BotSharp.Plugin.ChatHub/ChatHubPlugin.cs | 4 +-
.../Observers/ChatHubObserver.cs | 30 +++------
.../Providers/Chat/ChatCompletionProvider.cs | 14 ++--
.../Providers/ChatCompletionProvider.cs | 14 ++--
.../Providers/Chat/ChatCompletionProvider.cs | 7 +-
.../Providers/ChatCompletionProvider.cs | 16 +++--
21 files changed, 215 insertions(+), 153 deletions(-)
delete mode 100644 src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/ObserverSubscriptionContainer.cs
create mode 100644 src/Infrastructure/BotSharp.Core/MessageHub/Services/ObserverSubscriptionContainer.cs
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
index 277893ad..d345ab86 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/HubObserveData.cs
@@ -2,6 +2,5 @@ namespace BotSharp.Abstraction.MessageHub.Models;
public class HubObserveData : ObserveDataBase where TData : class, new()
{
- public string EventName { get; set; } = null!;
public TData Data { get; set; } = null!;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
index c881b49d..c7d5c0f6 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserveDataBase.cs
@@ -2,6 +2,6 @@ namespace BotSharp.Abstraction.MessageHub.Models;
public class ObserveDataBase
{
- public IServiceProvider ServiceProvider { get; set; } = null!;
+ public string EventName { get; set; } = null!;
public string RefId { get; set; } = null!;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs
index ee8345cd..0ba75531 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Models/ObserverSubscription.cs
@@ -19,4 +19,10 @@ public class ObserverSubscription
Observer = observer;
Subscription = subscription;
}
+
+ public void UnSubscribe()
+ {
+ Observer.Deactivate();
+ Subscription.Dispose();
+ }
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs
index 7b824075..5afe9915 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/BotSharpObserverBase.cs
@@ -1,9 +1,41 @@
+
namespace BotSharp.Abstraction.MessageHub.Observers;
-public abstract class BotSharpObserverBase
+public abstract class BotSharpObserverBase : IBotSharpObserver
{
+ protected bool _active = false;
+
protected BotSharpObserverBase()
{
}
+
+ public virtual string Name => string.Empty;
+
+ public virtual bool Active => _active;
+
+ public virtual void Activate()
+ {
+ _active = true;
+ }
+
+ public virtual void Deactivate()
+ {
+ _active = false;
+ }
+
+ public virtual void OnCompleted()
+ {
+
+ }
+
+ public virtual void OnError(Exception error)
+ {
+
+ }
+
+ public virtual void OnNext(T value)
+ {
+
+ }
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs
index edbb4df2..1c160765 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Observers/IBotSharpObserver.cs
@@ -2,8 +2,8 @@ namespace BotSharp.Abstraction.MessageHub.Observers;
public interface IBotSharpObserver : IObserver
{
- //string Name { get; }
- bool IsActive { get; }
+ string Name { get; }
+ bool Active { get; }
void Activate();
void Deactivate();
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs
index 337f2bbf..bc4ad3bb 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/IObserverService.cs
@@ -4,5 +4,7 @@ namespace BotSharp.Abstraction.MessageHub.Services;
public interface IObserverService
{
- ObserverSubscriptionContainer RegisterObservers(string refId) where T : ObserveDataBase;
+ IDisposable SubscribeObservers(string refId, IEnumerable? names = null) where T : ObserveDataBase;
+
+ void UnSubscribeObservers(IEnumerable? names = null) where T : ObserveDataBase;
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/ObserverSubscriptionContainer.cs b/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/ObserverSubscriptionContainer.cs
deleted file mode 100644
index 860da06d..00000000
--- a/src/Infrastructure/BotSharp.Abstraction/MessageHub/Services/ObserverSubscriptionContainer.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using BotSharp.Abstraction.MessageHub.Models;
-
-namespace BotSharp.Abstraction.MessageHub.Services;
-
-public class ObserverSubscriptionContainer : IDisposable
-{
- private IList> _subscriptions = [];
- private bool _disposed = false;
-
- public ObserverSubscriptionContainer()
- {
-
- }
-
- public ObserverSubscriptionContainer(
- IList> subscriptions)
- {
- _subscriptions = subscriptions;
- }
-
- public void Dispose()
- {
- Dispose(true);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (!_disposed)
- {
- if (disposing)
- {
-#if DEBUG
- Console.WriteLine($"Start disposing subscriptions...");
-#endif
- // Unregister all observers
- foreach (var item in _subscriptions)
- {
- item.Observer.Deactivate();
- item.Subscription.Dispose();
- }
- _subscriptions.Clear();
-#if DEBUG
- Console.WriteLine($"End disposing subscriptions...");
-#endif
- }
- _disposed = true;
- }
- }
-}
\ No newline at end of file
diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
index d3797466..0759f104 100644
--- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
+++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs
@@ -49,7 +49,8 @@ public class ConversationPlugin : IBotSharpPlugin, IBotSharpAppPlugin
});
services.AddSingleton>>();
- //services.AddScoped>, ConversationObserver>();
+ services.AddScoped>>();
+ services.AddScoped>, ConversationObserver>();
services.AddScoped();
services.AddScoped();
@@ -77,8 +78,6 @@ public class ConversationPlugin : IBotSharpPlugin, IBotSharpAppPlugin
public void Configure(IApplicationBuilder app)
{
- //var services = app.ApplicationServices;
- //var queue = services.GetRequiredService>>();
- //var logger = services.GetRequiredService