Merge branch 'master' of https://github.com/SciSharp/BotSharp into features/refine-model-settings

This commit is contained in:
Jicheng Lu 2025-09-04 14:14:29 -05:00
commit d1118a691c
11 changed files with 113 additions and 25 deletions

View file

@ -5,7 +5,7 @@ namespace BotSharp.Abstraction.Utilities;
public static class StringExtensions 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; => string.IsNullOrEmpty(str) ? defaultValue : str;
public static string SubstringMax(this string str, int maxLength) public static string SubstringMax(this string str, int maxLength)

View file

@ -1,4 +1,3 @@
using BotSharp.Abstraction.Hooks;
using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Instructs.Models; using BotSharp.Abstraction.Instructs.Models;
using BotSharp.Abstraction.MLTasks; using BotSharp.Abstraction.MLTasks;
@ -13,6 +12,15 @@ public partial class InstructService
var agentService = _services.GetRequiredService<IAgentService>(); var agentService = _services.GetRequiredService<IAgentService>();
Agent agent = await agentService.LoadAgent(agentId); 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) if (agent.Disabled)
{ {
var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent."; 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 MessageId = message.MessageId
}; };
if (completer is ITextCompletion textCompleter) if (completer is ITextCompletion textCompleter)
{ {
instruction = null; instruction = null;
@ -86,7 +95,7 @@ public partial class InstructService
{ {
CurrentAgentId = agentId, CurrentAgentId = agentId,
MessageId = message.MessageId, 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; response.Text = result.Content;

View file

@ -12,7 +12,12 @@ public class ChartHandlerPlugin : IBotSharpPlugin
public void RegisterDI(IServiceCollection services, IConfiguration config) public void RegisterDI(IServiceCollection services, IConfiguration config)
{ {
services.AddScoped(provider =>
{
var settingService = provider.GetRequiredService<ISettingService>();
return settingService.Bind<ChartHandlerSettings>("ChartHandler");
});
services.AddScoped<IAgentUtilityHook, ChartHandlerUtilityHook>(); services.AddScoped<IAgentUtilityHook, ChartHandlerUtilityHook>();
} }
} }

View file

@ -21,18 +21,22 @@ public class PlotChartFn : IFunctionCallback
public async Task<bool> Execute(RoleDialogModel message) public async Task<bool> Execute(RoleDialogModel message)
{ {
var db = _services.GetRequiredService<IBotSharpRepository>();
var agentService = _services.GetRequiredService<IAgentService>(); var agentService = _services.GetRequiredService<IAgentService>();
var convService = _services.GetRequiredService<IConversationService>(); var convService = _services.GetRequiredService<IConversationService>();
var args = JsonSerializer.Deserialize<LlmContextIn>(message.FunctionArgs); var args = JsonSerializer.Deserialize<LlmContextIn>(message.FunctionArgs);
var agent = await agentService.GetAgent(message.CurrentAgentId); 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 var innerAgent = new Agent
{ {
Id = agent.Id, Id = agent.Id,
Name = agent.Name, Name = agent.Name,
Instruction = inst, Instruction = inst,
LlmConfig = new AgentLlmConfig
{
MaxOutputTokens = 8192
},
TemplateDict = new Dictionary<string, object> TemplateDict = new Dictionary<string, object>
{ {
{ "plotting_requirement", args?.PlottingRequirement ?? string.Empty }, { "plotting_requirement", args?.PlottingRequirement ?? string.Empty },
@ -67,8 +71,8 @@ public class PlotChartFn : IFunctionCallback
{ {
try try
{ {
var llmProviderService = _services.GetRequiredService<ILlmProviderService>(); var (provider, model) = GetLlmProviderModel();
var completion = CompletionProvider.GetChatCompletion(_services, provider: "openai", model: "gpt-4.1"); var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model);
var response = await completion.GetChatCompletions(agent, dialogs); var response = await completion.GetChatCompletions(agent, dialogs);
return response.Content; return response.Content;
} }
@ -79,4 +83,42 @@ public class PlotChartFn : IFunctionCallback
return error; return error;
} }
} }
private string GetChartPlotInstruction(string agentId)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var state = _services.GetRequiredService<IConversationStateService>();
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";
var model = "gpt-5";
var state = _services.GetRequiredService<IConversationStateService>();
var settings = _services.GetRequiredService<ChartHandlerSettings>();
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);
}
} }

View file

@ -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; }
}

View file

@ -30,3 +30,4 @@ global using BotSharp.Core.Infrastructures;
global using BotSharp.Plugin.ChartHandler.Enums; global using BotSharp.Plugin.ChartHandler.Enums;
global using BotSharp.Plugin.ChartHandler.LlmContext; global using BotSharp.Plugin.ChartHandler.LlmContext;
global using BotSharp.Plugin.ChartHandler.Hooks; global using BotSharp.Plugin.ChartHandler.Hooks;
global using BotSharp.Plugin.ChartHandler.Settings;

View file

@ -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. 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 ===
{{ plotting_requirement }} {{ plotting_requirement }}
***** Hard Requirements *****
***** Important ***** ** Your output javascript code must be wrapped in one or multiple <script>...</script> blocks with everything needed inside.
** Your output must be a single <script>...</script> block 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 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 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 Plotlys 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 create any new html element.
** You must not apply any styles on any 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 *** *** Response Format ***
You must output the response in the following JSON format: You must output the response in the following JSON format:

View file

@ -98,10 +98,9 @@ public class ReadImageFn : IFunctionCallback
{ {
try try
{ {
var llmProviderService = _services.GetRequiredService<ILlmProviderService>(); var provider = "openai";
var provider = llmProviderService.GetProviders().FirstOrDefault(x => x == "openai"); var model = "gpt-5-mini";
var model = llmProviderService.GetProviderModel(provider: provider, id: "gpt-4o", multiModal: true); var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model);
var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model.Name);
var response = await completion.GetChatCompletions(agent, dialogs); var response = await completion.GetChatCompletions(agent, dialogs);
return response.Content; return response.Content;
} }

View file

@ -89,10 +89,9 @@ public class ReadPdfFn : IFunctionCallback
{ {
try try
{ {
var llmProviderService = _services.GetRequiredService<ILlmProviderService>(); var provider = "openai";
var provider = llmProviderService.GetProviders().FirstOrDefault(x => x == "openai"); var model = "gpt-5-mini";
var model = llmProviderService.GetProviderModel(provider: provider, id: "gpt-4o", multiModal: true); var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model);
var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model.Name);
var response = await completion.GetChatCompletions(agent, dialogs); var response = await completion.GetChatCompletions(agent, dialogs);
return response.Content; return response.Content;
} }

View file

@ -230,8 +230,13 @@ public class QdrantDb : IVectorDb
{ {
foreach (var item in payload) foreach (var item in payload)
{ {
if (item.Value == null || item.Key.IsEqualTo(KnowledgePayloadName.Text))
{
continue;
}
var value = item.Value.DataValue?.ConvertToString(); var value = item.Value.DataValue?.ConvertToString();
if (value == null || item.Key.IsEqualTo(KnowledgePayloadName.Text)) if (string.IsNullOrEmpty(value))
{ {
continue; continue;
} }

View file

@ -317,6 +317,13 @@
"Origin": "" "Origin": ""
}, },
"ChartHandler": {
"ChartPlot": {
"LlmProvider": "openai",
"LlmModel": "gpt-5"
}
},
"SqlDriver": { "SqlDriver": {
"MySqlConnectionString": "", "MySqlConnectionString": "",
"SqlServerConnectionString": "", "SqlServerConnectionString": "",