From f452aad4f5d03a3b5b81b4746beee8ccb4880088 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Mon, 25 Aug 2025 23:42:21 -0500 Subject: [PATCH 01/10] refine instruction --- .../Instructs/Services/InstructService.Execute.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index c1b16ee5..9ebbd807 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Hooks; using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Instructs.Models; using BotSharp.Abstraction.MLTasks; @@ -13,6 +12,15 @@ public partial class InstructService var agentService = _services.GetRequiredService(); Agent agent = await agentService.LoadAgent(agentId); + if (agent == null) + { + return new InstructResult + { + MessageId = message.MessageId, + Text = $"Agent (id: {agentId}) does not exist!" + }; + } + if (agent.Disabled) { var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent."; @@ -55,6 +63,7 @@ public partial class InstructService { MessageId = message.MessageId }; + if (completer is ITextCompletion textCompleter) { instruction = null; @@ -86,7 +95,7 @@ public partial class InstructService { CurrentAgentId = agentId, MessageId = message.MessageId, - Files = files?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData }).ToList() ?? [] + Files = files?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData, ContentType = x.ContentType }).ToList() ?? [] } }); response.Text = result.Content; From d01c15b546fbf1d83650d01f1363d58201461985 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 28 Aug 2025 13:42:51 -0500 Subject: [PATCH 02/10] add chart handler setting --- .../Utilities/StringExtensions.cs | 2 +- .../ChartHandlerPlugin.cs | 7 ++++++- .../Functions/PlotChartFn.cs | 21 +++++++++++++++++-- .../Settings/ChartHandlerSettings.cs | 12 +++++++++++ .../BotSharp.Plugin.ChartHandler/Using.cs | 3 ++- src/WebStarter/appsettings.json | 7 +++++++ 6 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.ChartHandler/Settings/ChartHandlerSettings.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs index 0270133c..8e2fd9bb 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/StringExtensions.cs @@ -5,7 +5,7 @@ namespace BotSharp.Abstraction.Utilities; public static class StringExtensions { - public static string IfNullOrEmptyAs(this string? str, string defaultValue) + public static string? IfNullOrEmptyAs(this string? str, string? defaultValue) => string.IsNullOrEmpty(str) ? defaultValue : str; public static string SubstringMax(this string str, int maxLength) diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/ChartHandlerPlugin.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/ChartHandlerPlugin.cs index 1161f6e4..4469f08a 100644 --- a/src/Plugins/BotSharp.Plugin.ChartHandler/ChartHandlerPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.ChartHandler/ChartHandlerPlugin.cs @@ -12,7 +12,12 @@ public class ChartHandlerPlugin : IBotSharpPlugin public void RegisterDI(IServiceCollection services, IConfiguration config) { + services.AddScoped(provider => + { + var settingService = provider.GetRequiredService(); + return settingService.Bind("ChartHandler"); + }); + services.AddScoped(); } - } diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs index ad8de0d7..40277297 100644 --- a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs +++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs @@ -67,8 +67,8 @@ public class PlotChartFn : IFunctionCallback { try { - var llmProviderService = _services.GetRequiredService(); - var completion = CompletionProvider.GetChatCompletion(_services, provider: "openai", model: "gpt-4.1"); + var (provider, model) = GetLlmProviderModel(); + var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model); var response = await completion.GetChatCompletions(agent, dialogs); return response.Content; } @@ -79,4 +79,21 @@ public class PlotChartFn : IFunctionCallback return error; } } + + private (string, string) GetLlmProviderModel() + { + var provider = "openai"; + var model = "gpt-5"; + + var state = _services.GetRequiredService(); + var settings = _services.GetRequiredService(); + provider = state.GetState("chart_plot_llm_provider") + .IfNullOrEmptyAs(settings.ChartPlot?.LlmProvider) + .IfNullOrEmptyAs(provider); + model = state.GetState("chart_plot_llm_model") + .IfNullOrEmptyAs(settings.ChartPlot?.LlmModel) + .IfNullOrEmptyAs(model); + + return (provider, model); + } } diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Settings/ChartHandlerSettings.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Settings/ChartHandlerSettings.cs new file mode 100644 index 00000000..61b0a6be --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Settings/ChartHandlerSettings.cs @@ -0,0 +1,12 @@ +namespace BotSharp.Plugin.ChartHandler.Settings; + +public class ChartHandlerSettings +{ + public ChartPlotSetting ChartPlot { get; set; } +} + +public class ChartPlotSetting +{ + public string LlmProvider { get; set; } + public string LlmModel { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Using.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Using.cs index fa94465c..d2ab32a1 100644 --- a/src/Plugins/BotSharp.Plugin.ChartHandler/Using.cs +++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Using.cs @@ -29,4 +29,5 @@ 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 +global using BotSharp.Plugin.ChartHandler.Hooks; +global using BotSharp.Plugin.ChartHandler.Settings; \ No newline at end of file diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index f7f4d342..c5b226e2 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -317,6 +317,13 @@ "Origin": "" }, + "ChartHandler": { + "ChartPlot": { + "LlmProvider": "", + "LlmModel": "" + } + }, + "SqlDriver": { "MySqlConnectionString": "", "SqlServerConnectionString": "", From 2d9de9c4791793a3786d5ce5cfa802286df609cf Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 28 Aug 2025 13:44:11 -0500 Subject: [PATCH 03/10] update settings --- src/WebStarter/appsettings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index c5b226e2..8124a4a8 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -319,8 +319,8 @@ "ChartHandler": { "ChartPlot": { - "LlmProvider": "", - "LlmModel": "" + "LlmProvider": "openai", + "LlmModel": "gpt-5" } }, From abfc7a8b0fd8ee85cf284f45a2010aefc24d622f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Thu, 28 Aug 2025 17:16:16 -0500 Subject: [PATCH 04/10] refine chart plot instruction --- .../templates/util-chart-plot_instruction.liquid | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 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 9bffa73f..dc4523f8 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,18 +1,26 @@ 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 ***** +***** Hard Requirements ***** ** 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 render the charts on the div html element with id {{ chart_element_id }}. +** Add a custom mode bar button named "Fullscreen" that toggles the chart container in and out of fullscreen using the Fullscreen API. Requirements for this button: + * Always call the Fullscreen API on the chart container div itself (document.getElementById("{{ chart_element_id }}")), not on the document or on Plotly’s SVG. + * Use el.requestFullscreen() with fallbacks to el.webkitRequestFullscreen || el.msRequestFullscreen. + * Exit fullscreen with document.exitFullscreen() and vendor fallbacks. + * Listen for fullscreenchange, webkitfullscreenchange, and msfullscreenchange to keep the button working across repeated clicks and ESC exits. + * Ensure the chart fully expands and scales to the entire screen when fullscreen is active. + * Provide a simple inline SVG path icon for the button (no external assets). + * Use Plotly.newPlot(container, data, layout, {displayModeBar:true, modeBarButtonsToAdd:[fullscreenBtn]}); + * fullscreenBtn must be a fully-formed object {name, title, icon, click}. ** 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. +** Keep code compact (few tokens), but fix all errors before returning. +** Please fix any code errors before returning the final response. *** Response Format *** You must output the response in the following JSON format: From 3d07fa54f41b11ccebb7b80e3b477f77b9ca0a22 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Sun, 31 Aug 2025 20:43:45 -0500 Subject: [PATCH 05/10] fix potential null reference --- src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs index cd14e375..736ed29f 100644 --- a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs +++ b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs @@ -230,8 +230,13 @@ public class QdrantDb : IVectorDb { foreach (var item in payload) { + if (item.Value == null) + { + continue; + } + var value = item.Value.DataValue?.ConvertToString(); - if (value == null || item.Key.IsEqualTo(KnowledgePayloadName.Text)) + if (string.IsNullOrEmpty(value) || item.Key.IsEqualTo(KnowledgePayloadName.Text)) { continue; } From 690590f60e45794f6e10b53001baf898dd39e0af Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Sun, 31 Aug 2025 20:44:40 -0500 Subject: [PATCH 06/10] minor change --- src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs index 736ed29f..2ffbdb53 100644 --- a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs +++ b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs @@ -230,13 +230,13 @@ public class QdrantDb : IVectorDb { foreach (var item in payload) { - if (item.Value == null) + if (item.Value == null || item.Key.IsEqualTo(KnowledgePayloadName.Text)) { continue; } var value = item.Value.DataValue?.ConvertToString(); - if (string.IsNullOrEmpty(value) || item.Key.IsEqualTo(KnowledgePayloadName.Text)) + if (string.IsNullOrEmpty(value)) { continue; } From a03bb4b00b37d973b4a5b85c73815ab0f687d12b Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 2 Sep 2025 11:52:20 -0500 Subject: [PATCH 07/10] refine chart plot template --- .../Functions/PlotChartFn.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs index 40277297..20047b76 100644 --- a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs +++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs @@ -21,13 +21,13 @@ public class PlotChartFn : IFunctionCallback 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 inst = GetChartPlotInstruction(message.CurrentAgentId); var innerAgent = new Agent { Id = agent.Id, @@ -80,6 +80,27 @@ public class PlotChartFn : IFunctionCallback } } + private string GetChartPlotInstruction(string agentId) + { + var db = _services.GetRequiredService(); + var state = _services.GetRequiredService(); + + var templateContent = string.Empty; + var templateName = state.GetState("chart_plot_template"); + + if (!string.IsNullOrEmpty(templateName)) + { + templateContent = db.GetAgentTemplate(agentId, templateName); + } + else + { + templateName = "util-chart-plot_instruction"; + templateContent = db.GetAgentTemplate(BuiltInAgentId.UtilityAssistant, templateName); + } + + return templateContent; + } + private (string, string) GetLlmProviderModel() { var provider = "openai"; From 877fc3ece644ea9efd7004ef28cea838d979b4f0 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 2 Sep 2025 16:59:49 -0500 Subject: [PATCH 08/10] temp model for image reading --- .../BotSharp.Plugin.FileHandler/Functions/ReadImageFn.cs | 7 +++---- .../BotSharp.Plugin.FileHandler/Functions/ReadPdfFn.cs | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadImageFn.cs b/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadImageFn.cs index f8a0f593..2e30929e 100644 --- a/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadImageFn.cs +++ b/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadImageFn.cs @@ -98,10 +98,9 @@ public class ReadImageFn : IFunctionCallback { try { - var llmProviderService = _services.GetRequiredService(); - var provider = llmProviderService.GetProviders().FirstOrDefault(x => x == "openai"); - var model = llmProviderService.GetProviderModel(provider: provider, id: "gpt-4o", multiModal: true); - var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model.Name); + var provider = "openai"; + var model = "gpt-5-mini"; + var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model); var response = await completion.GetChatCompletions(agent, dialogs); return response.Content; } diff --git a/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadPdfFn.cs b/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadPdfFn.cs index ab8ab8a9..7436fce5 100644 --- a/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadPdfFn.cs +++ b/src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadPdfFn.cs @@ -89,10 +89,9 @@ public class ReadPdfFn : IFunctionCallback { try { - var llmProviderService = _services.GetRequiredService(); - var provider = llmProviderService.GetProviders().FirstOrDefault(x => x == "openai"); - var model = llmProviderService.GetProviderModel(provider: provider, id: "gpt-4o", multiModal: true); - var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model.Name); + var provider = "openai"; + var model = "gpt-5-mini"; + var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model); var response = await completion.GetChatCompletions(agent, dialogs); return response.Content; } From 59414543f622e15bdc2ca8e78ed4f4abea64d5fe Mon Sep 17 00:00:00 2001 From: Joanna Ren <101223@smsassist.com> Date: Tue, 2 Sep 2025 18:24:00 -0500 Subject: [PATCH 09/10] add business intelligence agent --- .../Functions/PlotChartFn.cs | 6 ++++- .../util-chart-plot_instruction.liquid | 23 +++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs index 40277297..ae8e7fdc 100644 --- a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs +++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs @@ -33,6 +33,10 @@ public class PlotChartFn : IFunctionCallback Id = agent.Id, Name = agent.Name, Instruction = inst, + LlmConfig = new AgentLlmConfig + { + MaxOutputTokens = 8192 + }, TemplateDict = new Dictionary { { "plotting_requirement", args?.PlottingRequirement ?? string.Empty }, @@ -48,7 +52,7 @@ public class PlotChartFn : IFunctionCallback } ]); - var obj = response.JsonContent(); + var obj = response.JsonContent(); message.Content = obj?.GreetingMessage ?? "Here is the chart you ask for:"; message.RichContent = new RichContent { 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 9bffa73f..0d631b51 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,18 +1,27 @@ 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 ***** -** 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". +***** Hard Requirements ***** +** Your output javascript code must be wrapped in one or multiple blocks with everything needed inside. +** You need to import ECharts.js to plot the charts. The script source is "https://cdnjs.cloudflare.com/ajax/libs/echarts/6.0.0/echarts.min.js". ** You need to add the MODE bar for each chart you plot. -** You must render the charts on the div html element with id {{ chart_element_id }}. +** You must render the charts under the div html element with id {{ chart_element_id }}. +** Add a custom mode bar button named "Fullscreen" that toggles the chart container in and out of fullscreen using the Fullscreen API. Requirements for this button: + * Always call the Fullscreen API on the chart container div itself (document.getElementById("{{ chart_element_id }}")), not on the document or on Plotly’s SVG. + * Use el.requestFullscreen() with fallbacks to el.webkitRequestFullscreen || el.msRequestFullscreen. + * Exit fullscreen with document.exitFullscreen() and vendor fallbacks. + * Listen for fullscreenchange, webkitfullscreenchange, and msfullscreenchange to keep the button working across repeated clicks and ESC exits. + * Ensure the chart fully expands and scales to the entire screen when fullscreen is active. + * Provide a simple inline SVG path icon for the button (no external assets). + * Use Plotly.newPlot(container, data, layout, {displayModeBar:true, modeBarButtonsToAdd:[fullscreenBtn]}); + * fullscreenBtn must be a fully-formed object {name, title, icon, click}. ** 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. +** Keep code compact (few tokens), but fix all errors before returning. +** Do not generate charts with zero height and zero width. +** Please ensure the code can be executed and the charts are rendered correctly. *** Response Format *** You must output the response in the following JSON format: From 7c2ed704ab46783ccb0f37b0a1be5c528acebb3d Mon Sep 17 00:00:00 2001 From: Joanna Ren Date: Tue, 2 Sep 2025 18:27:33 -0500 Subject: [PATCH 10/10] Update PlotChartFn.cs --- .../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 ae8e7fdc..8958ecca 100644 --- a/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs +++ b/src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs @@ -52,7 +52,7 @@ public class PlotChartFn : IFunctionCallback } ]); - var obj = response.JsonContent(); + var obj = response.JsonContent(); message.Content = obj?.GreetingMessage ?? "Here is the chart you ask for:"; message.RichContent = new RichContent {