From 769b29a798d7f87525676ba14a994c5873b82b2f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 26 Sep 2025 16:19:23 -0500 Subject: [PATCH 01/26] add realtime options --- .../MLTasks/IRealTimeCompletion.cs | 1 + .../BotSharp.Abstraction/Realtime/IRealtimeHub.cs | 3 ++- .../Realtime/Models/RealtimeOptions.cs | 12 ++++++++++++ .../Services/RealtimeHub.cs | 7 ++++++- .../ChatStreamMiddleware.cs | 7 +++---- .../Models/Stream/ChatStreamRequest.cs | 4 ++++ .../Realtime/RealTimeCompletionProvider.cs | 15 ++++++++++----- .../Realtime/RealTimeCompletionProvider.cs | 10 ++++++++-- 8 files changed, 46 insertions(+), 13 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Realtime/Models/RealtimeOptions.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs index bb3104d36..65ae2dbc1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs +++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/IRealTimeCompletion.cs @@ -7,6 +7,7 @@ public interface IRealTimeCompletion string Provider { get; } string Model { get; } void SetModelName(string model); + void SetOptions(RealtimeOptions? options); Task Connect( RealtimeHubConnection conn, diff --git a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs index 9d64b6785..e75f46998 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHub.cs @@ -13,5 +13,6 @@ public interface IRealtimeHub IRealTimeCompletion Completer { get; } - Task ConnectToModel(Func? responseToUser = null, Func? init = null, List? initStates = null); + Task ConnectToModel(Func? responseToUser = null, Func? init = null, + List? initStates = null, RealtimeOptions? options = null); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Realtime/Models/RealtimeOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Realtime/Models/RealtimeOptions.cs new file mode 100644 index 000000000..a720c88b0 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Realtime/Models/RealtimeOptions.cs @@ -0,0 +1,12 @@ +namespace BotSharp.Abstraction.Realtime.Models; + +public class RealtimeOptions +{ + [JsonPropertyName("input_audio_format")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? InputAudioFormat { get; set; } + + [JsonPropertyName("output_audio_format")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? OutputAudioFormat { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs index 8c057b950..b34f5fba4 100644 --- a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs +++ b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs @@ -24,7 +24,11 @@ public RealtimeHub(IServiceProvider services, ILogger logger) _logger = logger; } - public async Task ConnectToModel(Func? responseToUser = null, Func? init = null, List? initStates = null) + public async Task ConnectToModel( + Func? responseToUser = null, + Func? init = null, + List? initStates = null, + RealtimeOptions? options = null) { var convService = _services.GetRequiredService(); convService.SetConversationId(_conn.ConversationId, initStates ?? []); @@ -43,6 +47,7 @@ public async Task ConnectToModel(Func? responseToUser = null, Func var settings = _services.GetRequiredService(); _completer = _services.GetServices().First(x => x.Provider == settings.Provider); + _completer.SetOptions(options); await _completer.Connect( conn: _conn, diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs index 44a6195a7..a08fda080 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Models; using BotSharp.Abstraction.Realtime.Models.Session; using BotSharp.Core.Session; using Microsoft.AspNetCore.Http; @@ -84,7 +83,7 @@ private async Task HandleWebSocket(IServiceProvider services, string agentId, st _logger.LogCritical($"Start chat stream connection for conversation ({conversationId})"); #endif var request = InitRequest(data, conversationId); - await ConnectToModel(hub, session, request?.States); + await ConnectToModel(hub, session, request); } else if (eventType == "media") { @@ -107,7 +106,7 @@ private async Task HandleWebSocket(IServiceProvider services, string agentId, st await session.DisconnectAsync(); } - private async Task ConnectToModel(IRealtimeHub hub, BotSharpRealtimeSession session, List? states = null) + private async Task ConnectToModel(IRealtimeHub hub, BotSharpRealtimeSession session, ChatStreamRequest? request) { await hub.ConnectToModel(responseToUser: async data => { @@ -115,7 +114,7 @@ await hub.ConnectToModel(responseToUser: async data => { await session.SendEventAsync(data); } - }, initStates: states); + }, initStates: request?.States, options: request?.Options); } private (string, string) MapEvents(RealtimeHubConnection conn, string receivedText, string conversationId) diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs index 1a72d7dce..13ef8f0e7 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Models/Stream/ChatStreamRequest.cs @@ -7,4 +7,8 @@ public class ChatStreamRequest { [JsonPropertyName("states")] public List States { get; set; } = []; + + [JsonPropertyName("realtime_options")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public RealtimeOptions? Options { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs index 983b203a5..90395d49b 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -58,11 +58,6 @@ public GoogleRealTimeProvider( _logger = logger; } - public void SetModelName(string model) - { - _model = model; - } - public async Task Connect( RealtimeHubConnection conn, Func onModelReady, @@ -420,6 +415,16 @@ await SendEventToModel(new RealtimeClientPayload } } + public void SetModelName(string model) + { + _model = model; + } + + public void SetOptions(RealtimeOptions? options) + { + + } + #region Private methods private List OnFunctionCall(RealtimeHubConnection conn, RealtimeFunctionCall functionCall) { diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs index 890a8619c..1e246c630 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -18,6 +18,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion private string _model = "gpt-4o-mini-realtime-preview"; private LlmRealtimeSession _session; + private RealtimeOptions? _realtimeOptions; private bool _isBlocking = false; private RealtimeHubConnection _conn; @@ -338,8 +339,8 @@ public async Task UpdateSession(RealtimeHubConnection conn, bool isInit type = "session.update", session = new RealtimeSessionUpdateRequest { - InputAudioFormat = realtimeModelSettings.InputAudioFormat, - OutputAudioFormat = realtimeModelSettings.OutputAudioFormat, + InputAudioFormat = _realtimeOptions?.InputAudioFormat ?? realtimeModelSettings.InputAudioFormat, + OutputAudioFormat = _realtimeOptions?.OutputAudioFormat ?? realtimeModelSettings.OutputAudioFormat, Voice = realtimeModelSettings.Voice, Instructions = instruction, ToolChoice = "auto", @@ -457,6 +458,11 @@ public void SetModelName(string model) _model = model; } + public void SetOptions(RealtimeOptions? options) + { + _realtimeOptions = options; + } + #region Private methods private async Task> OnResponsedDone(RealtimeHubConnection conn, string response) { From 2f9183f7d2d8c5a4b4fc4a1e195815a30e4aa507 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 26 Sep 2025 16:45:22 -0500 Subject: [PATCH 02/26] add realtime model settings --- src/WebStarter/appsettings.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index c55e9e2c1..59d7d3f55 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -570,6 +570,20 @@ } }, + "RealtimeModel": { + "Provider": "openai", + "Model": "gpt-realtime", + "InputAudioFormat": "pcm16", + "OutputAudioFormat": "pcm16", + "InterruptResponse": true, + "MaxResponseOutputTokens": 4096, + "InputAudioTranscribe": true, + "InputAudioTranscription": { + "Model": "whisper-1", + "Language": "en" + } + }, + "PluginLoader": { "Assemblies": [ "BotSharp.Core", From 0aa633787a18ec7c8c4d3d46753bab39eac08765 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Fri, 26 Sep 2025 19:21:08 -0500 Subject: [PATCH 03/26] init --- Directory.Packages.props | 2 +- .../BotSharp.Plugin.PythonInterpreter.csproj | 14 +++++++++----- ...InterpretationFn.cs => PyInterpretationFn.cs} | 16 ++++++++++++---- .../Hooks/InterpreterUtilityHook.cs | 6 +++--- ...er.json => util-code-python_interpreter.json} | 0 .../util-code-python_generate_instruction.liquid | 0 ...id => util-code-python_interpreter.fn.liquid} | 0 src/WebStarter/WebStarter.csproj | 1 + src/WebStarter/appsettings.json | 3 ++- 9 files changed, 28 insertions(+), 14 deletions(-) rename src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/{InterpretationFn.cs => PyInterpretationFn.cs} (75%) rename src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/{python_interpreter.json => util-code-python_interpreter.json} (100%) create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid rename src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/{python_interpreter.fn.liquid => util-code-python_interpreter.fn.liquid} (100%) diff --git a/Directory.Packages.props b/Directory.Packages.props index de3730f08..d5d3954fd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -73,7 +73,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj b/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj index 391593b36..ab15acf79 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj @@ -1,4 +1,4 @@ - + $(TargetFramework) @@ -11,15 +11,19 @@ - - + + + - + PreserveNewest - + + PreserveNewest + + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/InterpretationFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs similarity index 75% rename from src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/InterpretationFn.cs rename to src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs index b12cb7390..818340b9d 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/InterpretationFn.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs @@ -8,13 +8,21 @@ namespace BotSharp.Plugin.PythonInterpreter.Functions; -public class InterpretationFn : IFunctionCallback +public class PyInterpretationFn : IFunctionCallback { - public string Name => "python_interpreter"; - public string Indication => "Interpreting python code"; + public string Name => "util-code-python_interpreter"; + public string Indication => "Executing python code"; private readonly IServiceProvider _services; - private readonly ILogger _logger; + private readonly ILogger _logger; + + public PyInterpretationFn( + IServiceProvider services, + ILogger logger) + { + _services = services; + _logger = logger; + } public async Task Execute(RoleDialogModel message) { diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs index 7b644be2b..97a0f4826 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs @@ -2,7 +2,7 @@ namespace BotSharp.Plugin.PythonInterpreter.Hooks; public class InterpreterUtilityHook : IAgentUtilityHook { - private static string FUNCTION_NAME = "python_interpreter"; + private const string PY_INTERPRETER_FN = "util-code-python_interpreter"; public void AddUtilities(List utilities) { @@ -13,8 +13,8 @@ public void AddUtilities(List utilities) Items = [ new UtilityItem { - FunctionName = FUNCTION_NAME, - TemplateName = $"{FUNCTION_NAME}.fn" + FunctionName = PY_INTERPRETER_FN, + TemplateName = $"{PY_INTERPRETER_FN}.fn" } ] }; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/python_interpreter.json b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_interpreter.json similarity index 100% rename from src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/python_interpreter.json rename to src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_interpreter.json diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid new file mode 100644 index 000000000..e69de29bb diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/python_interpreter.fn.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_interpreter.fn.liquid similarity index 100% rename from src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/python_interpreter.fn.liquid rename to src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_interpreter.fn.liquid diff --git a/src/WebStarter/WebStarter.csproj b/src/WebStarter/WebStarter.csproj index 6e0176157..e5b90ddde 100644 --- a/src/WebStarter/WebStarter.csproj +++ b/src/WebStarter/WebStarter.csproj @@ -37,6 +37,7 @@ + diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 59d7d3f55..81ac7091c 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -620,7 +620,8 @@ "BotSharp.Plugin.AudioHandler", "BotSharp.Plugin.ExcelHandler", "BotSharp.Plugin.SqlDriver", - "BotSharp.Plugin.TencentCos" + "BotSharp.Plugin.TencentCos", + "BotSharp.Plugin.PythonInterpreter" ] } } From e529b36fabce546d16de4871107133060786b0b7 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Mon, 29 Sep 2025 01:20:25 -0500 Subject: [PATCH 04/26] init --- .../BotSharp.Plugin.PythonInterpreter.csproj | 2 +- .../Functions/PyInterpretationFn.cs | 112 +++++++++++++++++- .../InterpreterPlugin.cs | 7 ++ .../LlmContext/LlmContextIn.cs | 9 ++ .../LlmContext/LlmContextOut.cs | 9 ++ .../Using.cs | 7 ++ .../util-code-python_interpreter.json | 15 +-- .../util-code-python_interpreter.fn.liquid | 2 +- 8 files changed, 146 insertions(+), 17 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj b/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj index ab15acf79..db85df01e 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj @@ -33,7 +33,7 @@ - + diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs index 818340b9d..7706f9847 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs @@ -1,8 +1,7 @@ -using BotSharp.Abstraction.Conversations.Models; -using BotSharp.Abstraction.Functions; -using BotSharp.Abstraction.Interpreters.Models; +using BotSharp.Abstraction.Routing; using Microsoft.Extensions.Logging; using Python.Runtime; +using System.Runtime; using System.Text.Json; using System.Threading.Tasks; @@ -26,7 +25,40 @@ public PyInterpretationFn( public async Task Execute(RoleDialogModel message) { - var args = JsonSerializer.Deserialize(message.FunctionArgs); + var agentService = _services.GetRequiredService(); + var convService = _services.GetRequiredService(); + var routingCtx = _services.GetRequiredService(); + + var args = JsonSerializer.Deserialize(message.FunctionArgs); + + var agent = await agentService.GetAgent(message.CurrentAgentId); + var inst = GetPyCodeGenerationInstruction(message.CurrentAgentId); + var innerAgent = new Agent + { + Id = agent.Id, + Name = agent.Name, + Instruction = inst, + LlmConfig = GetLlmConfig(), + TemplateDict = new Dictionary + { + { "user_requirement", args?.UserRquirement ?? string.Empty } + } + }; + + var dialogs = routingCtx.GetDialogs(); + if (dialogs.IsNullOrEmpty()) + { + dialogs = convService.GetDialogHistory(); + } + + dialogs.Add(new RoleDialogModel(AgentRole.User, "Please follow the instruction and chat context to generate valid python code.") + { + CurrentAgentId = message.CurrentAgentId, + MessageId = message.MessageId + }); + + var response = await GetChatCompletion(innerAgent, dialogs); + var ret = response.JsonContent(); using (Py.GIL()) { @@ -40,7 +72,7 @@ public async Task Execute(RoleDialogModel message) // Execute a simple Python script using var locals = new PyDict(); - PythonEngine.Exec(args.Script, null, locals); + PythonEngine.Exec(ret.PythonCode, null, locals); // Console.WriteLine($"Result from Python: {result}"); message.Content = stringIO.getvalue(); @@ -51,4 +83,74 @@ public async Task Execute(RoleDialogModel message) return true; } + + private async Task GetChatCompletion(Agent agent, List dialogs) + { + try + { + var (provider, model) = GetLlmProviderModel(); + var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model); + 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; + } + } + + private string GetPyCodeGenerationInstruction(string agentId) + { + var db = _services.GetRequiredService(); + var state = _services.GetRequiredService(); + + var templateContent = string.Empty; + var templateName = state.GetState("python_generate_template"); + + if (!string.IsNullOrEmpty(templateName)) + { + templateContent = db.GetAgentTemplate(agentId, templateName); + } + else + { + templateName = "util-code-python_generate_instruction"; + templateContent = db.GetAgentTemplate(BuiltInAgentId.UtilityAssistant, templateName); + } + + return templateContent; + } + + private (string, string) GetLlmProviderModel() + { + var provider = "openai"; + var model = "gpt-5"; + + var state = _services.GetRequiredService(); + provider = state.GetState("py_generator_llm_provider") + //.IfNullOrEmptyAs(_settings.ChartPlot?.LlmProvider) + .IfNullOrEmptyAs(provider); + model = state.GetState("py_generator_llm_model") + //.IfNullOrEmptyAs(_settings.ChartPlot?.LlmModel) + .IfNullOrEmptyAs(model); + + return (provider, model); + } + + private AgentLlmConfig GetLlmConfig() + { + var maxOutputTokens = 8192; + var reasoningEffortLevel = "minimal"; + + var state = _services.GetRequiredService(); + maxOutputTokens = int.TryParse(state.GetState("py_generator_max_output_tokens"), out var tokens) ? tokens : maxOutputTokens; + reasoningEffortLevel = state.GetState("py_generator_reasoning_effort_level").IfNullOrEmptyAs(reasoningEffortLevel); + + return new AgentLlmConfig + { + MaxOutputTokens = maxOutputTokens, + ReasoningEffortLevel = reasoningEffortLevel + }; + } } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs index 7d195286c..2e1e723be 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Interpreters.Settings; +using BotSharp.Abstraction.Settings; using BotSharp.Plugin.PythonInterpreter.Hooks; using Microsoft.AspNetCore.Builder; using Python.Runtime; @@ -15,6 +16,12 @@ public class InterpreterPlugin : IBotSharpAppPlugin public void RegisterDI(IServiceCollection services, IConfiguration config) { + services.AddSingleton(provider => + { + var settingService = provider.GetRequiredService(); + return settingService.Bind("Interpreter"); + }); + services.AddScoped(); } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs new file mode 100644 index 000000000..bcac0e8be --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.PythonInterpreter.LlmContext; + +public class LlmContextIn +{ + [JsonPropertyName("user_requirement")] + public string UserRquirement { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs new file mode 100644 index 000000000..b074a0048 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.PythonInterpreter.LlmContext; + +public class LlmContextOut +{ + [JsonPropertyName("python_code")] + public string PythonCode { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs index 8cc31aa17..00a172faf 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs @@ -14,5 +14,12 @@ global using BotSharp.Abstraction.Conversations; global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.Repositories; +global using BotSharp.Abstraction.Conversations.Models; +global using BotSharp.Abstraction.Functions; +global using BotSharp.Abstraction.Interpreters.Models; + + +global using BotSharp.Core.Infrastructures; global using BotSharp.Plugin.PythonInterpreter.Enums; +global using BotSharp.Plugin.PythonInterpreter.LlmContext; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_interpreter.json b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_interpreter.json index ac2b2a916..ee5a708ea 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_interpreter.json +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_interpreter.json @@ -1,19 +1,14 @@ { - "name": "python_interpreter", - "description": "write and execute python code, print the result in Console", + "name": "util-code-python_interpreter", + "description": "If the user requests you generating python code to complete tasks, you can call this function to generate python code to execute.", "parameters": { "type": "object", "properties": { - "script": { + "user_requirement": { "type": "string", - "description": "python code" - }, - "language": { - "type": "string", - "enum": [ "python" ], - "description": "python code" + "description": "The requirement that user posted for generating python code." } }, - "required": [ "language", "script" ] + "required": [ "user_requirement" ] } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_interpreter.fn.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_interpreter.fn.liquid index 8dd2425f3..9037aaffc 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_interpreter.fn.liquid +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_interpreter.fn.liquid @@ -1 +1 @@ -Write and execute Python script in python_interpreter function, and use python function print(a) to output the result in stand output. \ No newline at end of file +Please call function util-code-python_interpreter if user wants to generate python code to complete tasks. \ No newline at end of file From 09c008f76f8723ab72f096cf886b2461ab6a020e Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Mon, 29 Sep 2025 01:35:08 -0500 Subject: [PATCH 05/26] localize setting --- .../BotSharp.Core/BotSharpCoreExtensions.cs | 4 ---- .../Functions/PyInterpretationFn.cs | 19 +++++++++++-------- ...ook.cs => PythonInterpreterUtilityHook.cs} | 2 +- ...erPlugin.cs => PythonInterpreterPlugin.cs} | 15 +++++++-------- .../Settings/PythonInterpreterSettings.cs | 6 ++++++ .../Using.cs | 3 +-- src/WebStarter/appsettings.json | 6 ++---- 7 files changed, 28 insertions(+), 27 deletions(-) rename src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/{InterpreterUtilityHook.cs => PythonInterpreterUtilityHook.cs} (90%) rename src/Plugins/BotSharp.Plugin.PythonInterpreter/{InterpreterPlugin.cs => PythonInterpreterPlugin.cs} (75%) create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs index bfae45bac..a3fa39650 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs @@ -24,10 +24,6 @@ public static class BotSharpCoreExtensions { public static IServiceCollection AddBotSharpCore(this IServiceCollection services, IConfiguration config, Action? configOptions = null) { - var interpreterSettings = new InterpreterSettings(); - config.Bind("Interpreter", interpreterSettings); - services.AddSingleton(x => interpreterSettings); - services.AddSingleton(); // Register template render services.AddSingleton(); diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs index 7706f9847..f3876e8fa 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs @@ -14,13 +14,16 @@ public class PyInterpretationFn : IFunctionCallback private readonly IServiceProvider _services; private readonly ILogger _logger; + private readonly PythonInterpreterSettings _settings; public PyInterpretationFn( IServiceProvider services, - ILogger logger) + ILogger logger, + PythonInterpreterSettings settings) { _services = services; _logger = logger; + _settings = settings; } public async Task Execute(RoleDialogModel message) @@ -32,7 +35,7 @@ public async Task Execute(RoleDialogModel message) var args = JsonSerializer.Deserialize(message.FunctionArgs); var agent = await agentService.GetAgent(message.CurrentAgentId); - var inst = GetPyCodeGenerationInstruction(message.CurrentAgentId); + var inst = GetPyCodeInterpreterInstruction(message.CurrentAgentId); var innerAgent = new Agent { Id = agent.Id, @@ -95,13 +98,13 @@ private async Task GetChatCompletion(Agent agent, List } catch (Exception ex) { - var error = $"Error when plotting chart. {ex.Message}"; + var error = $"Error when generating python code. {ex.Message}"; _logger.LogWarning(ex, error); return error; } } - private string GetPyCodeGenerationInstruction(string agentId) + private string GetPyCodeInterpreterInstruction(string agentId) { var db = _services.GetRequiredService(); var state = _services.GetRequiredService(); @@ -128,10 +131,10 @@ private string GetPyCodeGenerationInstruction(string agentId) var model = "gpt-5"; var state = _services.GetRequiredService(); - provider = state.GetState("py_generator_llm_provider") + provider = state.GetState("py_intepreter_llm_provider") //.IfNullOrEmptyAs(_settings.ChartPlot?.LlmProvider) .IfNullOrEmptyAs(provider); - model = state.GetState("py_generator_llm_model") + model = state.GetState("py_intepreter_llm_model") //.IfNullOrEmptyAs(_settings.ChartPlot?.LlmModel) .IfNullOrEmptyAs(model); @@ -144,8 +147,8 @@ private AgentLlmConfig GetLlmConfig() var reasoningEffortLevel = "minimal"; var state = _services.GetRequiredService(); - maxOutputTokens = int.TryParse(state.GetState("py_generator_max_output_tokens"), out var tokens) ? tokens : maxOutputTokens; - reasoningEffortLevel = state.GetState("py_generator_reasoning_effort_level").IfNullOrEmptyAs(reasoningEffortLevel); + maxOutputTokens = int.TryParse(state.GetState("py_intepreter_max_output_tokens"), out var tokens) ? tokens : maxOutputTokens; + reasoningEffortLevel = state.GetState("py_intepreter_reasoning_effort_level").IfNullOrEmptyAs(reasoningEffortLevel); return new AgentLlmConfig { diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PythonInterpreterUtilityHook.cs similarity index 90% rename from src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs rename to src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PythonInterpreterUtilityHook.cs index 97a0f4826..2d243e35b 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/InterpreterUtilityHook.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PythonInterpreterUtilityHook.cs @@ -1,6 +1,6 @@ namespace BotSharp.Plugin.PythonInterpreter.Hooks; -public class InterpreterUtilityHook : IAgentUtilityHook +public class PythonInterpreterUtilityHook : IAgentUtilityHook { private const string PY_INTERPRETER_FN = "util-code-python_interpreter"; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs similarity index 75% rename from src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs rename to src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs index 2e1e723be..e1c1bd636 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/InterpreterPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Interpreters.Settings; using BotSharp.Abstraction.Settings; using BotSharp.Plugin.PythonInterpreter.Hooks; using Microsoft.AspNetCore.Builder; @@ -7,7 +6,7 @@ namespace BotSharp.Plugin.PythonInterpreter; -public class InterpreterPlugin : IBotSharpAppPlugin +public class PythonInterpreterPlugin : IBotSharpAppPlugin { public string Id => "23174e08-e866-4173-824a-cf1d97afa8d0"; public string Name => "Python Interpreter"; @@ -19,26 +18,26 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) services.AddSingleton(provider => { var settingService = provider.GetRequiredService(); - return settingService.Bind("Interpreter"); + return settingService.Bind("PythonInterpreter"); }); - services.AddScoped(); + services.AddScoped(); } public void Configure(IApplicationBuilder app) { - var settings = app.ApplicationServices.GetRequiredService(); + var settings = app.ApplicationServices.GetRequiredService(); // For Python interpreter plugin - if (File.Exists(settings.Python.PythonDLL)) + if (File.Exists(settings.DllLocation)) { - Runtime.PythonDLL = settings.Python.PythonDLL; + Runtime.PythonDLL = settings.DllLocation; PythonEngine.Initialize(); PythonEngine.BeginAllowThreads(); } else { - Serilog.Log.Error("Python DLL found at {PythonDLL}", settings.Python.PythonDLL); + Serilog.Log.Error("Python DLL found at {PythonDLL}", settings.DllLocation); } // Shut down the Python engine diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs new file mode 100644 index 000000000..01f5a9d13 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Plugin.PythonInterpreter.Settings; + +public class PythonInterpreterSettings +{ + public string DllLocation { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs index 00a172faf..88ed52e6a 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs @@ -16,10 +16,9 @@ global using BotSharp.Abstraction.Repositories; global using BotSharp.Abstraction.Conversations.Models; global using BotSharp.Abstraction.Functions; -global using BotSharp.Abstraction.Interpreters.Models; - global using BotSharp.Core.Infrastructures; global using BotSharp.Plugin.PythonInterpreter.Enums; global using BotSharp.Plugin.PythonInterpreter.LlmContext; +global using BotSharp.Plugin.PythonInterpreter.Settings; diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 81ac7091c..f00c4899b 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -564,10 +564,8 @@ } }, - "Interpreter": { - "Python": { - "PythonDLL": "C:/Users/xxx/AppData/Local/Programs/Python/Python311/python311.dll" - } + "PythonInterpreter": { + "DllLocation": "C:/Users/xxx/AppData/Local/Programs/Python/Python313/python313.dll" }, "RealtimeModel": { From 9abe3159523a81490ae5b96836097ee01e1afa61 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 29 Sep 2025 15:07:54 -0500 Subject: [PATCH 06/26] refine py code generation --- .../Models/InterpretationRequest.cs | 10 --- .../Settings/InterpreterSettings.cs | 11 --- .../BotSharp.Core/BotSharpCoreExtensions.cs | 1 - .../BotSharp.Plugin.PythonInterpreter.csproj | 6 +- .../Enums/UtilityName.cs | 2 +- ...yInterpretationFn.cs => PyProgrammerFn.cs} | 87 ++++++++++++------- .../Hooks/PyProgrammerUtilityHook.cs | 24 +++++ .../Hooks/PythonInterpreterUtilityHook.cs | 24 ----- .../PythonInterpreterPlugin.cs | 27 +++--- .../Settings/PythonInterpreterSettings.cs | 9 ++ .../Using.cs | 4 + ....json => util-code-python_programmer.json} | 2 +- ...il-code-python_generate_instruction.liquid | 39 +++++++++ .../util-code-python_interpreter.fn.liquid | 1 - .../util-code-python_programmer.fn.liquid | 1 + src/WebStarter/appsettings.json | 13 ++- 16 files changed, 164 insertions(+), 97 deletions(-) delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs delete mode 100644 src/Infrastructure/BotSharp.Abstraction/Interpreters/Settings/InterpreterSettings.cs rename src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/{PyInterpretationFn.cs => PyProgrammerFn.cs} (63%) create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PyProgrammerUtilityHook.cs delete mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PythonInterpreterUtilityHook.cs rename src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/{util-code-python_interpreter.json => util-code-python_programmer.json} (66%) delete mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_interpreter.fn.liquid create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_programmer.fn.liquid diff --git a/src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs b/src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs deleted file mode 100644 index 5a8f7552d..000000000 --- a/src/Infrastructure/BotSharp.Abstraction/Interpreters/Models/InterpretationRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace BotSharp.Abstraction.Interpreters.Models; - -public class InterpretationRequest -{ - [JsonPropertyName("script")] - public string Script { get; set; } = null!; - - [JsonPropertyName("language")] - public string Language { get; set; } = null!; -} diff --git a/src/Infrastructure/BotSharp.Abstraction/Interpreters/Settings/InterpreterSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Interpreters/Settings/InterpreterSettings.cs deleted file mode 100644 index e6ea6cf3f..000000000 --- a/src/Infrastructure/BotSharp.Abstraction/Interpreters/Settings/InterpreterSettings.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace BotSharp.Abstraction.Interpreters.Settings; - -public class InterpreterSettings -{ - public PythonInterpreterSetting Python { get; set; } -} - -public class PythonInterpreterSetting -{ - public string PythonDLL { get; set; } -} diff --git a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs index a3fa39650..9c34de599 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs +++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs @@ -6,7 +6,6 @@ using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Messaging.JsonConverters; using BotSharp.Abstraction.Users.Settings; -using BotSharp.Abstraction.Interpreters.Settings; using BotSharp.Abstraction.Infrastructures; using BotSharp.Core.Processors; using StackExchange.Redis; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj b/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj index db85df01e..8b0775e8c 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/BotSharp.Plugin.PythonInterpreter.csproj @@ -11,16 +11,14 @@ - - - + PreserveNewest - + PreserveNewest diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs index 95a5b8a2b..813031af1 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Enums/UtilityName.cs @@ -2,5 +2,5 @@ namespace BotSharp.Plugin.PythonInterpreter.Enums; public class UtilityName { - public const string PythonInterpreter = "python-interpreter"; + public const string PythonProgrammer = "python-programmer"; } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs similarity index 63% rename from src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs rename to src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs index f3876e8fa..c44bf5cf5 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyInterpretationFn.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs @@ -1,24 +1,22 @@ -using BotSharp.Abstraction.Routing; using Microsoft.Extensions.Logging; using Python.Runtime; -using System.Runtime; using System.Text.Json; using System.Threading.Tasks; namespace BotSharp.Plugin.PythonInterpreter.Functions; -public class PyInterpretationFn : IFunctionCallback +public class PyProgrammerFn : IFunctionCallback { - public string Name => "util-code-python_interpreter"; - public string Indication => "Executing python code"; + public string Name => "util-code-python_programmer"; + public string Indication => "Programming and executing code"; private readonly IServiceProvider _services; - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly PythonInterpreterSettings _settings; - public PyInterpretationFn( + public PyProgrammerFn( IServiceProvider services, - ILogger logger, + ILogger logger, PythonInterpreterSettings settings) { _services = services; @@ -44,6 +42,7 @@ public async Task Execute(RoleDialogModel message) LlmConfig = GetLlmConfig(), TemplateDict = new Dictionary { + { "python_version", _settings.PythonVersion ?? "3.11" }, { "user_requirement", args?.UserRquirement ?? string.Empty } } }; @@ -54,6 +53,8 @@ public async Task Execute(RoleDialogModel message) dialogs = convService.GetDialogHistory(); } + var messageLimit = _settings.CodeGeneration?.MessageLimit > 0 ? _settings.CodeGeneration.MessageLimit.Value : 50; + dialogs = dialogs.TakeLast(messageLimit).ToList(); dialogs.Add(new RoleDialogModel(AgentRole.User, "Please follow the instruction and chat context to generate valid python code.") { CurrentAgentId = message.CurrentAgentId, @@ -63,25 +64,51 @@ public async Task Execute(RoleDialogModel message) var response = await GetChatCompletion(innerAgent, dialogs); var ret = response.JsonContent(); - using (Py.GIL()) + try { - // Import necessary Python modules - dynamic sys = Py.Import("sys"); - dynamic io = Py.Import("io"); - - // Redirect standard output to capture it - dynamic stringIO = io.StringIO(); - sys.stdout = stringIO; - - // Execute a simple Python script - using var locals = new PyDict(); - PythonEngine.Exec(ret.PythonCode, null, locals); - - // Console.WriteLine($"Result from Python: {result}"); - message.Content = stringIO.getvalue(); - - // Restore the original stdout - sys.stdout = sys.__stdout__; + using (Py.GIL()) + { + // Import necessary Python modules + dynamic sys = Py.Import("sys"); + dynamic io = Py.Import("io"); + + // Redirect standard output/error to capture it + dynamic stringIO = io.StringIO(); + sys.stdout = stringIO; + sys.stderr = stringIO; + + // Set global items + using var globals = new PyDict(); + if (ret.PythonCode?.Contains("__main__") == true) + { + globals.SetItem("__name__", new PyString("__main__")); + } + + // Execute Python script + PythonEngine.Exec(ret.PythonCode, globals); + + // Get result + var result = stringIO.getvalue().ToString(); + message.Content = result; + message.RichContent = new RichContent + { + Recipient = new Recipient { Id = convService.ConversationId }, + Message = new ProgramCodeTemplateMessage + { + Text = ret.PythonCode ?? string.Empty, + Language = "python" + } + }; + message.StopCompletion = true; + + // Restore the original stdout/stderr + sys.stdout = sys.__stdout__; + sys.stderr = sys.__stderr__; + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"Error when executing python code."); } return true; @@ -132,10 +159,10 @@ private string GetPyCodeInterpreterInstruction(string agentId) var state = _services.GetRequiredService(); provider = state.GetState("py_intepreter_llm_provider") - //.IfNullOrEmptyAs(_settings.ChartPlot?.LlmProvider) + .IfNullOrEmptyAs(_settings.CodeGeneration?.LlmProvider) .IfNullOrEmptyAs(provider); model = state.GetState("py_intepreter_llm_model") - //.IfNullOrEmptyAs(_settings.ChartPlot?.LlmModel) + .IfNullOrEmptyAs(_settings.CodeGeneration?.LlmModel) .IfNullOrEmptyAs(model); return (provider, model); @@ -143,8 +170,8 @@ private string GetPyCodeInterpreterInstruction(string agentId) private AgentLlmConfig GetLlmConfig() { - var maxOutputTokens = 8192; - var reasoningEffortLevel = "minimal"; + var maxOutputTokens = _settings?.CodeGeneration?.MaxOutputTokens ?? 8192; + var reasoningEffortLevel = _settings?.CodeGeneration?.ReasoningEffortLevel ?? "minimal"; var state = _services.GetRequiredService(); maxOutputTokens = int.TryParse(state.GetState("py_intepreter_max_output_tokens"), out var tokens) ? tokens : maxOutputTokens; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PyProgrammerUtilityHook.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PyProgrammerUtilityHook.cs new file mode 100644 index 000000000..0866478ae --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PyProgrammerUtilityHook.cs @@ -0,0 +1,24 @@ +namespace BotSharp.Plugin.PythonInterpreter.Hooks; + +public class PyProgrammerUtilityHook : IAgentUtilityHook +{ + private const string PY_PROGRAMMER_FN = "util-code-python_programmer"; + + public void AddUtilities(List utilities) + { + var utility = new AgentUtility() + { + Category = "code", + Name = UtilityName.PythonProgrammer, + Items = [ + new UtilityItem + { + FunctionName = PY_PROGRAMMER_FN, + TemplateName = $"{PY_PROGRAMMER_FN}.fn" + } + ] + }; + + utilities.Add(utility); + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PythonInterpreterUtilityHook.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PythonInterpreterUtilityHook.cs deleted file mode 100644 index 2d243e35b..000000000 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Hooks/PythonInterpreterUtilityHook.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace BotSharp.Plugin.PythonInterpreter.Hooks; - -public class PythonInterpreterUtilityHook : IAgentUtilityHook -{ - private const string PY_INTERPRETER_FN = "util-code-python_interpreter"; - - public void AddUtilities(List utilities) - { - var utility = new AgentUtility() - { - Category = "coding", - Name = UtilityName.PythonInterpreter, - Items = [ - new UtilityItem - { - FunctionName = PY_INTERPRETER_FN, - TemplateName = $"{PY_INTERPRETER_FN}.fn" - } - ] - }; - - utilities.Add(utility); - } -} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs index e1c1bd636..ed55ae4c5 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs @@ -1,6 +1,6 @@ -using BotSharp.Abstraction.Settings; using BotSharp.Plugin.PythonInterpreter.Hooks; using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Hosting; using Python.Runtime; using System.IO; @@ -13,15 +13,15 @@ public class PythonInterpreterPlugin : IBotSharpAppPlugin public string Description => "Python Interpreter enables AI to write and execute Python code within a secure, sandboxed environment."; public string? IconUrl => "https://static.vecteezy.com/system/resources/previews/012/697/295/non_2x/3d-python-programming-language-logo-free-png.png"; + private nint _pyState; + public void RegisterDI(IServiceCollection services, IConfiguration config) { - services.AddSingleton(provider => - { - var settingService = provider.GetRequiredService(); - return settingService.Bind("PythonInterpreter"); - }); + var settings = new PythonInterpreterSettings(); + config.Bind("PythonInterpreter", settings); + services.AddSingleton(x => settings); - services.AddScoped(); + services.AddScoped(); } public void Configure(IApplicationBuilder app) @@ -33,14 +33,17 @@ public void Configure(IApplicationBuilder app) { Runtime.PythonDLL = settings.DllLocation; PythonEngine.Initialize(); - PythonEngine.BeginAllowThreads(); + _pyState = PythonEngine.BeginAllowThreads(); + + var lifetime = app.ApplicationServices.GetRequiredService(); + lifetime.ApplicationStopping.Register(() => { + PythonEngine.EndAllowThreads(_pyState); + PythonEngine.Shutdown(); + }); } else { - Serilog.Log.Error("Python DLL found at {PythonDLL}", settings.DllLocation); + Serilog.Log.Error($"Python DLL found at {settings.DllLocation}"); } - - // Shut down the Python engine - // PythonEngine.Shutdown(); } } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs index 01f5a9d13..7e380ba1b 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs @@ -1,6 +1,15 @@ +using BotSharp.Abstraction.Models; + namespace BotSharp.Plugin.PythonInterpreter.Settings; public class PythonInterpreterSettings { public string DllLocation { get; set; } + public string PythonVersion { get; set; } + public CodeGenerationSetting? CodeGeneration { get; set; } } + +public class CodeGenerationSetting : LlmConfigBase +{ + public int? MessageLimit { get; set; } +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs index 88ed52e6a..84bac0341 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs @@ -16,6 +16,10 @@ global using BotSharp.Abstraction.Repositories; global using BotSharp.Abstraction.Conversations.Models; global using BotSharp.Abstraction.Functions; +global using BotSharp.Abstraction.Messaging; +global using BotSharp.Abstraction.Messaging.Models.RichContent; +global using BotSharp.Abstraction.Messaging.Models.RichContent.Template; +global using BotSharp.Abstraction.Routing; global using BotSharp.Core.Infrastructures; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_interpreter.json b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_programmer.json similarity index 66% rename from src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_interpreter.json rename to src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_programmer.json index ee5a708ea..c9aabc208 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_interpreter.json +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_programmer.json @@ -1,6 +1,6 @@ { "name": "util-code-python_interpreter", - "description": "If the user requests you generating python code to complete tasks, you can call this function to generate python code to execute.", + "description": "If user's requirement can be fulfilled by python code, you can call this function to generate python code to execute.", "parameters": { "type": "object", "properties": { diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid index e69de29bb..3ac455a19 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid @@ -0,0 +1,39 @@ +You are a Python code generator that can produce python code to fulfill user's requirement. +Please read "User Requirement" and the chat context, and then generate valid python code that can fulfill user's requirement. + +You must strictly follow the "Hard Requirements", "Code Requirements", and "Response Format" below. + +=== User Requirement === +{{ user_requirement }} + + +***** Hard Requirements ***** +1. Your output python code must be well constructed inside one or multiple functions. +2. You must not include any code, explanations, or comments outside these functions. +3. Do not include explanations, comments outside code. +4. You must use print() once to output the final result only. Do not print intermediate values, logs, or debug info. +5. If randomness is required, set a fixed seed inside a function. + + +***** Code Requirements ***** +1. Structure + a). You need to create small and focused functions. + b). You need to include a main() function to orchestrates these steps. + c). You must only call main() in "if __name__ == '__main__'" block. +2. Data type & Validation + a). When unsure about a variable’s type, check it at runtime (e.g., isinstance) before applying methods. + b). Validate inputs and handle edge cases, such as empty lists, division by zero, out-of-range values. + c). Except the main() function, it is preferable to generate functions that accept parameters and return values. +3. Error handling + a). If necessary, use try/except block inside main() to catch any errors and produce a single final printed message. +4. Output + a). You must print the final result once in main(). +5. Compatibility + a). You must keep the code compatible with "Python {{ python_version }}" + + +***** Response Format ***** +You must output the response in the following JSON format: +{ + "python_code": "The python code that can fulfill user's request." +} \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_interpreter.fn.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_interpreter.fn.liquid deleted file mode 100644 index 9037aaffc..000000000 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_interpreter.fn.liquid +++ /dev/null @@ -1 +0,0 @@ -Please call function util-code-python_interpreter if user wants to generate python code to complete tasks. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_programmer.fn.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_programmer.fn.liquid new file mode 100644 index 000000000..604e986f6 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_programmer.fn.liquid @@ -0,0 +1 @@ +Please call function util-code-python_interpreter if you think it is necessary to fulfill user's request through python code. \ No newline at end of file diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index f00c4899b..65b781110 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -488,7 +488,8 @@ "LlmProvider": "openai", "LlmModel": "gpt-5", "MaxOutputTokens": 8192, - "ReasoningEffortLevel": "minimal" + "ReasoningEffortLevel": "minimal", + "MessageLimit": 50 } }, @@ -565,7 +566,15 @@ }, "PythonInterpreter": { - "DllLocation": "C:/Users/xxx/AppData/Local/Programs/Python/Python313/python313.dll" + "DllLocation": "C:/Users/xxx/AppData/Local/Programs/Python/Python313/python313.dll", + "PythonVersion": "3.13", + "CodeGeneration": { + "LlmProvider": "openai", + "LlmModel": "gpt-5", + "MaxOutputTokens": 8192, + "ReasoningEffortLevel": "minimal", + "MessageLimit": 50 + } }, "RealtimeModel": { From a5bf44a44ad84ca30ed3ad00ca42d2612c031e3f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 29 Sep 2025 15:34:52 -0500 Subject: [PATCH 07/26] rename --- .../PythonInterpreterPlugin.cs | 39 ++++++++++++------- .../Settings/PythonInterpreterSettings.cs | 5 ++- .../Using.cs | 2 + .../util-code-python_programmer.json | 2 +- src/WebStarter/appsettings.json | 2 +- 5 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs index ed55ae4c5..14ae5dc2f 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs @@ -1,6 +1,6 @@ -using BotSharp.Plugin.PythonInterpreter.Hooks; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Python.Runtime; using System.IO; @@ -26,24 +26,33 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) public void Configure(IApplicationBuilder app) { - var settings = app.ApplicationServices.GetRequiredService(); + var sp = app.ApplicationServices; + var settings = sp.GetRequiredService(); + var logger = sp.GetRequiredService>(); + var pyLoc = settings.InstallLocation; - // For Python interpreter plugin - if (File.Exists(settings.DllLocation)) + try { - Runtime.PythonDLL = settings.DllLocation; - PythonEngine.Initialize(); - _pyState = PythonEngine.BeginAllowThreads(); - - var lifetime = app.ApplicationServices.GetRequiredService(); - lifetime.ApplicationStopping.Register(() => { - PythonEngine.EndAllowThreads(_pyState); - PythonEngine.Shutdown(); - }); + if (File.Exists(pyLoc)) + { + Runtime.PythonDLL = pyLoc; + PythonEngine.Initialize(); + _pyState = PythonEngine.BeginAllowThreads(); + + var lifetime = app.ApplicationServices.GetRequiredService(); + lifetime.ApplicationStopping.Register(() => { + PythonEngine.EndAllowThreads(_pyState); + PythonEngine.Shutdown(); + }); + } + else + { + logger.LogError($"Python dll not found at {pyLoc}"); + } } - else + catch (Exception ex) { - Serilog.Log.Error($"Python DLL found at {settings.DllLocation}"); + logger.LogError(ex, $"Error when loading python dll {pyLoc}"); } } } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs index 7e380ba1b..8fa79dc8a 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Settings/PythonInterpreterSettings.cs @@ -4,7 +4,10 @@ namespace BotSharp.Plugin.PythonInterpreter.Settings; public class PythonInterpreterSettings { - public string DllLocation { get; set; } + /// + /// Python installation path to .dll or .so + /// + public string InstallLocation { get; set; } public string PythonVersion { get; set; } public CodeGenerationSetting? CodeGeneration { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs index 84bac0341..bf94142ef 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs @@ -26,3 +26,5 @@ global using BotSharp.Plugin.PythonInterpreter.Enums; global using BotSharp.Plugin.PythonInterpreter.LlmContext; global using BotSharp.Plugin.PythonInterpreter.Settings; +global using BotSharp.Plugin.PythonInterpreter.Functions; +global using BotSharp.Plugin.PythonInterpreter.Hooks; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_programmer.json b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_programmer.json index c9aabc208..80064bc81 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_programmer.json +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-code-python_programmer.json @@ -1,5 +1,5 @@ { - "name": "util-code-python_interpreter", + "name": "util-code-python_programmer", "description": "If user's requirement can be fulfilled by python code, you can call this function to generate python code to execute.", "parameters": { "type": "object", diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 65b781110..415662d03 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -566,7 +566,7 @@ }, "PythonInterpreter": { - "DllLocation": "C:/Users/xxx/AppData/Local/Programs/Python/Python313/python313.dll", + "InstallLocation": "C:/Users/xxx/AppData/Local/Programs/Python/Python313/python313.dll", "PythonVersion": "3.13", "CodeGeneration": { "LlmProvider": "openai", From ed902b0f5753dd593ba9c0f080afe73a35ea0a31 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 29 Sep 2025 17:58:00 -0500 Subject: [PATCH 08/26] add package handling --- .../Functions/PyProgrammerFn.cs | 4 +- .../Helpers/PyPackageHelper.cs | 178 ++++++++++++++++++ .../LlmContext/LlmContextIn.cs | 5 +- .../LlmContext/LlmContextOut.cs | 3 + .../Models/PackageInstallResult.cs | 7 + .../Using.cs | 2 + ...il-code-python_generate_instruction.liquid | 20 +- src/WebStarter/appsettings.json | 5 +- 8 files changed, 215 insertions(+), 9 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Helpers/PyPackageHelper.cs create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Models/PackageInstallResult.cs diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs index c44bf5cf5..477778a00 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs @@ -108,7 +108,9 @@ public async Task Execute(RoleDialogModel message) } catch (Exception ex) { - _logger.LogError(ex, $"Error when executing python code."); + var errorMsg = $"Error when executing python code."; + message.Content = $"{errorMsg} {ex.Message}"; + _logger.LogError(ex, errorMsg); } return true; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Helpers/PyPackageHelper.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Helpers/PyPackageHelper.cs new file mode 100644 index 000000000..2c8cfd3c5 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Helpers/PyPackageHelper.cs @@ -0,0 +1,178 @@ +using System.Diagnostics; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.PythonInterpreter.Helpers; + +internal static class PyPackageHelper +{ + /// + /// Install python packages + /// + /// + /// + internal static async Task InstallPackages(List? packages) + { + if (packages.IsNullOrEmpty()) + { + return new PackageInstallResult { Success = true }; + } + + try + { + var packageList = string.Join(" ", packages); + var startInfo = new ProcessStartInfo + { + FileName = "pip", + Arguments = $"install {packageList}", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + using var process = Process.Start(startInfo); + if (process == null) + { + return new PackageInstallResult + { + Success = false, + ErrorMsg = "Failed to start pip process" + }; + } + + await process.WaitForExitAsync(); + var output = await process.StandardOutput.ReadToEndAsync(); + var error = await process.StandardError.ReadToEndAsync(); + + if (process.ExitCode == 0) + { + return new PackageInstallResult { Success = true }; + } + else + { + var errorMsg = $"Failed to install packages. Exit code: {process.ExitCode}. Error: {error}"; + return new PackageInstallResult + { + Success = false, + ErrorMsg = errorMsg + }; + } + } + catch (Exception ex) + { + var errorMsg = $"Exception occurred while installing packages: {ex.Message}"; + return new PackageInstallResult + { + Success = false, + ErrorMsg = errorMsg + }; + } + } + + /// + /// Get packages that are not installed + /// + /// + /// + public static async Task> GetUninstalledPackages(List? packages) + { + var missingPackages = new List(); + + if (packages.IsNullOrEmpty()) + { + return missingPackages; + } + + try + { + var installedPackages = await GetInstalledPackages(); + foreach (var package in packages) + { + // Check for common package name mappings + var mappedPackageName = MapToPackageName(package); + var isInstalled = installedPackages.Any(x => x.IsEqualTo(mappedPackageName)); + if (!isInstalled) + { + missingPackages.Add(mappedPackageName); + } + } + return missingPackages; + } + catch (Exception ex) + { + throw; + } + } + + + private static async Task> GetInstalledPackages() + { + try + { + var startInfo = new ProcessStartInfo + { + FileName = "pip", + Arguments = "list --format=freeze", + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + + using var process = Process.Start(startInfo); + if (process == null) + { + throw new InvalidOperationException("Failed to start pip process"); + } + + await process.WaitForExitAsync(); + var output = await process.StandardOutput.ReadToEndAsync(); + + if (process.ExitCode != 0) + { + var error = await process.StandardError.ReadToEndAsync(); + throw new InvalidOperationException($"pip list failed with exit code {process.ExitCode}: {error}"); + } + + // Parse pip list output (format: package==version) + var packages = output.Split("\n", StringSplitOptions.RemoveEmptyEntries) + .Select(line => line.Split("==", StringSplitOptions.None)[0].Trim().ToLowerInvariant()) + .Where(pkg => !string.IsNullOrEmpty(pkg)) + .ToList(); + return packages; + } + catch (Exception ex) + { + throw; + } + } + + /// + /// Map common import name to actual package name + /// + /// + /// + private static string MapToPackageName(string importName) + { + var packageMappings = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "cv2", "opencv-python" }, + { "PIL", "Pillow" }, + { "sklearn", "scikit-learn" }, + { "yaml", "PyYAML" }, + { "bs4", "beautifulsoup4" }, + { "dateutil", "python-dateutil" }, + { "serial", "pyserial" }, + { "psutil", "psutil" }, + { "requests", "requests" }, + { "numpy", "numpy" }, + { "pandas", "pandas" }, + { "matplotlib", "matplotlib" }, + { "scipy", "scipy" }, + { "seaborn", "seaborn" }, + { "plotly", "plotly" } + }; + + return packageMappings.TryGetValue(importName, out var actualName) ? actualName : importName; + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs index bcac0e8be..0f7619128 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextIn.cs @@ -5,5 +5,8 @@ namespace BotSharp.Plugin.PythonInterpreter.LlmContext; public class LlmContextIn { [JsonPropertyName("user_requirement")] - public string UserRquirement { get; set; } + public string UserRquirement { get; set; } = string.Empty; + + [JsonPropertyName("imported_packages")] + public List ImportedPackages { get; set; } = []; } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs index b074a0048..2af106ee0 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/LlmContext/LlmContextOut.cs @@ -6,4 +6,7 @@ public class LlmContextOut { [JsonPropertyName("python_code")] public string PythonCode { get; set; } + + [JsonPropertyName("imported_packages")] + public List? ImportedPackages { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Models/PackageInstallResult.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Models/PackageInstallResult.cs new file mode 100644 index 000000000..b6fdb9941 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Models/PackageInstallResult.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Plugin.PythonInterpreter.Models; + +internal class PackageInstallResult +{ + internal bool Success { get; set; } + internal string ErrorMsg { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs index bf94142ef..73d0bc868 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs @@ -28,3 +28,5 @@ global using BotSharp.Plugin.PythonInterpreter.Settings; global using BotSharp.Plugin.PythonInterpreter.Functions; global using BotSharp.Plugin.PythonInterpreter.Hooks; +global using BotSharp.Plugin.PythonInterpreter.Models; +global using BotSharp.Plugin.PythonInterpreter.Helpers; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid index 3ac455a19..91da2bb5c 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid @@ -8,11 +8,12 @@ You must strictly follow the "Hard Requirements", "Code Requirements", and "Resp ***** Hard Requirements ***** -1. Your output python code must be well constructed inside one or multiple functions. -2. You must not include any code, explanations, or comments outside these functions. -3. Do not include explanations, comments outside code. -4. You must use print() once to output the final result only. Do not print intermediate values, logs, or debug info. -5. If randomness is required, set a fixed seed inside a function. +1. You must import packages at the beginning of the code. DO NOT include any import statement in the middle of the functions. +2. Your output python code must be well constructed inside one or multiple functions. +3. You must not include any code, explanations, or comments outside these functions. +4. Do not include explanations, comments outside code. +5. You must use print() once to output the final result only. Do not print intermediate values, logs, or debug info. +6. If randomness is required, set a fixed seed inside a function. ***** Code Requirements ***** @@ -26,14 +27,21 @@ You must strictly follow the "Hard Requirements", "Code Requirements", and "Resp c). Except the main() function, it is preferable to generate functions that accept parameters and return values. 3. Error handling a). If necessary, use try/except block inside main() to catch any errors and produce a single final printed message. + b). Do not apply try/except block for package import. 4. Output a). You must print the final result once in main(). 5. Compatibility a). You must keep the code compatible with "Python {{ python_version }}" +6. Package Usage + a). Prefer using Python standard library modules when possible (os, sys, json, csv, math, statistics, datetime, etc.) + b). Only use external packages (like pandas, numpy, matplotlib) when absolutely necessary for the task + c). If using external packages, provide fallback implementations using standard library when feasible + d). Always include external packages in the "imported_packages" list in your response ***** Response Format ***** You must output the response in the following JSON format: { - "python_code": "The python code that can fulfill user's request." + "python_code": "The python code that can fulfill user's request.", + "imported_packages": a list of strings that contains the packages that are imported in the code you generated. } \ No newline at end of file diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 415662d03..c2a1974a1 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -567,7 +567,10 @@ "PythonInterpreter": { "InstallLocation": "C:/Users/xxx/AppData/Local/Programs/Python/Python313/python313.dll", - "PythonVersion": "3.13", + "PythonVersion": "3.13.3", + "AutoInstallPackages": false, + "AllowedPackages": [ "pandas", "numpy", "matplotlib", "requests", "beautifulsoup4", "scipy", "seaborn", "plotly" ], + "EnableFallbackGeneration": true, "CodeGeneration": { "LlmProvider": "openai", "LlmModel": "gpt-5", From 7144704c5a6e9e4d650cc0007439516d9d45341f Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 10:25:36 -0500 Subject: [PATCH 09/26] clean code --- .../templates/util-chart-plot_instruction.liquid | 7 +++++-- .../templates/util-code-python_generate_instruction.liquid | 4 +++- src/WebStarter/appsettings.json | 3 --- 3 files changed, 8 insertions(+), 6 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 dfb4da4f2..303ab4089 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 "Plotting Requirement" and generate a javascript code that can be used to render the charts on an html element. +You are a Chart plotter that use javascript code to plot chart. +Please read {% if user_requirement != empty %}"User Requirement" and{% endif %} the chat context, and then generate a javascript code that can be used to render the charts on an html element. + You must strictly follow the "Hard Requirements", "Render Requirements", "Code Requirements" and "Response Format" below. +{% if user_requirement != empty %} === Plotting Requirement === {{ plotting_requirement }} - +{% endif %} ***** Hard Requirements ***** ** Your output javascript code must be wrapped in one or multiple blocks with everything needed inside. diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid index 91da2bb5c..e98757cdc 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-code-python_generate_instruction.liquid @@ -1,10 +1,12 @@ You are a Python code generator that can produce python code to fulfill user's requirement. -Please read "User Requirement" and the chat context, and then generate valid python code that can fulfill user's requirement. +Please read {% if user_requirement != empty %}"User Requirement" and{% endif %} the chat context, and then generate valid python code that can fulfill user's requirement. You must strictly follow the "Hard Requirements", "Code Requirements", and "Response Format" below. +{% if user_requirement != empty %} === User Requirement === {{ user_requirement }} +{% endif %} ***** Hard Requirements ***** diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index c2a1974a1..1304313c9 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -568,9 +568,6 @@ "PythonInterpreter": { "InstallLocation": "C:/Users/xxx/AppData/Local/Programs/Python/Python313/python313.dll", "PythonVersion": "3.13.3", - "AutoInstallPackages": false, - "AllowedPackages": [ "pandas", "numpy", "matplotlib", "requests", "beautifulsoup4", "scipy", "seaborn", "plotly" ], - "EnableFallbackGeneration": true, "CodeGeneration": { "LlmProvider": "openai", "LlmModel": "gpt-5", From b99d0b4ece423eda7c3451bc770c59480c3164a3 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 11:16:53 -0500 Subject: [PATCH 10/26] use role content --- .../Conversations/Models/RoleDialogModel.cs | 19 ++++++++++ .../Providers/ChatCompletionProvider.cs | 22 +++++------ .../Providers/Chat/ChatCompletionProvider.cs | 6 +-- .../Providers/Chat/ChatCompletionProvider.cs | 6 +-- .../Chat/GeminiChatCompletionProvider.cs | 10 ++--- .../Chat/PalmChatCompletionProvider.cs | 6 +-- .../Realtime/RealTimeCompletionProvider.cs | 13 ++++--- .../Providers/ChatCompletionProvider.cs | 4 +- .../Providers/ChatCompletionProvider.cs | 2 +- .../Providers/ChatCompletionProvider.cs | 8 ++-- ...osoftExtensionsAIChatCompletionProvider.cs | 6 +-- .../Providers/Chat/ChatCompletionProvider.cs | 6 +-- .../Realtime/RealTimeCompletionProvider.cs | 37 ++----------------- .../Functions/PyProgrammerFn.cs | 2 +- .../Providers/ChatCompletionProvider.cs | 4 +- 15 files changed, 70 insertions(+), 81 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs index 33678eccb..4ca9c0d1e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs @@ -140,6 +140,25 @@ public class RoleDialogModel : ITrackableMessage [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public bool IsFromAssistant => Role == AgentRole.Assistant || Role == AgentRole.Model; + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public string RoleContent + { + get + { + var text = Content; + if (Role == AgentRole.User) + { + text = !string.IsNullOrWhiteSpace(Payload) ? Payload : Content; + } + else + { + text = !string.IsNullOrWhiteSpace(RichContent?.Message?.Text) ? RichContent.Message.Text : Content; + } + + return text; + } + } + public RoleDialogModel() { } diff --git a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs index 676c182f1..0d24bc41f 100644 --- a/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AnthropicAI/Providers/ChatCompletionProvider.cs @@ -136,17 +136,17 @@ public Task GetChatCompletionsStreamingAsync(Agent agent, List< filteredMessages = filteredMessages.Where((_, idx) => idx >= firstUserMsgIdx).ToList(); } - foreach (var conv in filteredMessages) + foreach (var message in filteredMessages) { - if (conv.Role == AgentRole.User) + if (message.Role == AgentRole.User) { - messages.Add(new Message(RoleType.User, conv.Payload ?? conv.Content)); + messages.Add(new Message(RoleType.User, message.RoleContent)); } - else if (conv.Role == AgentRole.Assistant) + else if (message.Role == AgentRole.Assistant) { - messages.Add(new Message(RoleType.Assistant, conv.Content)); + messages.Add(new Message(RoleType.Assistant, message.RoleContent)); } - else if (conv.Role == AgentRole.Function) + else if (message.Role == AgentRole.Function) { messages.Add(new Message { @@ -155,9 +155,9 @@ public Task GetChatCompletionsStreamingAsync(Agent agent, List< { new ToolUseContent() { - Id = conv.ToolCallId, - Name = conv.FunctionName, - Input = JsonNode.Parse(conv.FunctionArgs ?? "{}") + Id = message.ToolCallId, + Name = message.FunctionName, + Input = JsonNode.Parse(message.FunctionArgs ?? "{}") } } }); @@ -169,8 +169,8 @@ public Task GetChatCompletionsStreamingAsync(Agent agent, List< { new ToolResultContent() { - ToolUseId = conv.ToolCallId, - Content = [new TextContent() { Text = conv.Content }] + ToolUseId = message.ToolCallId, + Content = [new TextContent() { Text = message.RoleContent }] } } }); diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs index 8aaf043a4..b7efb11ff 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -432,11 +432,11 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}")) })); - messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content)); + messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.RoleContent)); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; @@ -448,7 +448,7 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, } else if (message.Role == AgentRole.Assistant) { - var text = message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; diff --git a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs index cb661aaae..eb143ebb3 100644 --- a/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs @@ -399,11 +399,11 @@ public void SetModelName(string model) ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}")) })); - messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content)); + messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.RoleContent)); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; @@ -415,7 +415,7 @@ public void SetModelName(string model) } else if (message.Role == AgentRole.Assistant) { - var text = message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs index 85674a4b8..1d2b4ef48 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs @@ -263,17 +263,17 @@ public void SetModelName(string model) Name = message.FunctionName, Response = new JsonObject() { - ["result"] = message.Content ?? string.Empty + ["result"] = message.RoleContent ?? string.Empty } } } ], AgentRole.Function)); - convPrompts.Add($"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs}) => {message.Content}"); + convPrompts.Add($"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs}) => {message.RoleContent}"); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var text = message.RoleContent; var contentParts = new List { new() { Text = text } }; if (allowMultiModal && !message.Files.IsNullOrEmpty()) @@ -285,7 +285,7 @@ public void SetModelName(string model) } else if (message.Role == AgentRole.Assistant) { - var text = message.Content; + var text = message.RoleContent; var contentParts = new List { new() { Text = text } }; if (allowMultiModal && !message.Files.IsNullOrEmpty()) @@ -294,7 +294,7 @@ public void SetModelName(string model) } contents.Add(new Content(contentParts, AgentRole.Model)); - convPrompts.Add($"{AgentRole.Assistant}: {message.Content}"); + convPrompts.Add($"{AgentRole.Assistant}: {text}"); } } diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs index c37bb751c..8dfaa695b 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/PalmChatCompletionProvider.cs @@ -108,7 +108,7 @@ public async Task GetChatCompletions(Agent agent, List new PalmChatMessage(c.Content, c.Role == AgentRole.User ? "user" : "AI")) + var messages = conversations.Select(c => new PalmChatMessage(c.RoleContent, c.Role == AgentRole.User ? "user" : "AI")) .ToList(); if (!functions.IsNullOrEmpty()) @@ -124,8 +124,8 @@ public async Task GetChatCompletions(Agent agent, List {dialog.Content}\r\n" : - $"{dialog.Role}: {dialog.Content}\r\n"; + $"{dialog.Role}: {dialog.FunctionName} => {dialog.RoleContent}\r\n" : + $"{dialog.Role}: {dialog.RoleContent}\r\n"; } prompt += "\r\n\r\n" + router.Templates.FirstOrDefault(x => x.Name == "response_with_function").Content; diff --git a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs index 90395d49b..802088bdf 100644 --- a/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -482,7 +482,8 @@ await hook.AfterGenerated(new RoleDialogModel(AgentRole.Assistant, text) } - private (string, GenerateContentRequest) PrepareOptions(Agent agent, + private (string, GenerateContentRequest) PrepareOptions( + Agent agent, List conversations) { var agentService = _services.GetRequiredService(); @@ -564,25 +565,25 @@ await hook.AfterGenerated(new RoleDialogModel(AgentRole.Assistant, text) Name = message.FunctionName ?? string.Empty, Response = new JsonObject() { - ["result"] = message.Content ?? string.Empty + ["result"] = message.RoleContent ?? string.Empty } } } ], AgentRole.Function)); convPrompts.Add( - $"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs}) => {message.Content}"); + $"{AgentRole.Assistant}: Call function {message.FunctionName}({message.FunctionArgs}) => {message.RoleContent}"); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var text = message.RoleContent; contents.Add(new Content(text, AgentRole.User)); convPrompts.Add($"{AgentRole.User}: {text}"); } else if (message.Role == AgentRole.Assistant) { - contents.Add(new Content(message.Content, AgentRole.Model)); - convPrompts.Add($"{AgentRole.Assistant}: {message.Content}"); + contents.Add(new Content(message.RoleContent, AgentRole.Model)); + convPrompts.Add($"{AgentRole.Assistant}: {message.RoleContent}"); } } diff --git a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs index 1380881ba..ea6f75fdf 100644 --- a/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.LLamaSharp/Providers/ChatCompletionProvider.cs @@ -42,7 +42,7 @@ public async Task GetChatCompletions(Agent agent, List $"{x.Role}: {x.Content}")).Trim(); + var content = string.Join("\r\n", conversations.Select(x => $"{x.Role}: {x.RoleContent}")).Trim(); content += $"\r\n{AgentRole.Assistant}: "; var llama = _services.GetRequiredService(); @@ -118,7 +118,7 @@ public async Task GetChatCompletionsAsync(Agent agent, Func onMessageReceived, Func onFunctionExecuting) { - var content = string.Join("\r\n", conversations.Select(x => $"{x.Role}: {x.Content}")).Trim(); + var content = string.Join("\r\n", conversations.Select(x => $"{x.Role}: {x.RoleContent}")).Trim(); content += $"\r\n{AgentRole.Assistant}: "; var state = _services.GetRequiredService(); diff --git a/src/Plugins/BotSharp.Plugin.LangChain/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.LangChain/Providers/ChatCompletionProvider.cs index 9e2aa1d46..a7a4adddf 100644 --- a/src/Plugins/BotSharp.Plugin.LangChain/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.LangChain/Providers/ChatCompletionProvider.cs @@ -41,7 +41,7 @@ public async Task GetChatCompletions(Agent agent, List new Message(c.Content, c.Role == AgentRole.User ? MessageRole.Human : MessageRole.Ai)).ToList(); + .Select(c => new Message(c.RoleContent, c.Role == AgentRole.User ? MessageRole.Human : MessageRole.Ai)).ToList(); var response = await model.GenerateAsync(new ChatRequest { Messages = messages }, _settings); diff --git a/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs index f7831e1a9..2505eed1e 100644 --- a/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.MetaGLM/Providers/ChatCompletionProvider.cs @@ -111,8 +111,8 @@ private string PrepareOptions(Agent agent, List conversations, foreach (var message in samples) { messages.Add(message.Role == AgentRole.User ? - new MessageItem("user", message.Content) : - new MessageItem("assistant", message.Content)); + new MessageItem("user", message.RoleContent) : + new MessageItem("assistant", message.RoleContent)); } foreach (var function in functions) @@ -129,13 +129,13 @@ private string PrepareOptions(Agent agent, List conversations, } else if (message.Role == "user") { - var userMessage = new MessageItem("user",message.Content); + var userMessage = new MessageItem("user",message.RoleContent); messages.Add(userMessage); } else if (message.Role == "assistant") { - messages.Add(new MessageItem("assistant", message.Content)); + messages.Add(new MessageItem("assistant", message.RoleContent)); } } diff --git a/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs index 8440dc455..43f5134a0 100644 --- a/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.MicrosoftExtensionsAI/MicrosoftExtensionsAIChatCompletionProvider.cs @@ -109,16 +109,16 @@ public async Task GetChatCompletions(Agent agent, List>(x.FunctionArgs ?? "{}")), - new FunctionResultContent(x.FunctionName, x.Content) + new FunctionResultContent(x.FunctionName, x.RoleContent) ])); } else if (x.Role == AgentRole.System || x.Role == AgentRole.Assistant) { - messages.Add(new(x.Role == AgentRole.System ? ChatRole.System : ChatRole.Assistant, x.Content)); + messages.Add(new(x.Role == AgentRole.System ? ChatRole.System : ChatRole.Assistant, x.RoleContent)); } else if (x.Role == AgentRole.User) { - List contents = [new TextContent(!string.IsNullOrWhiteSpace(x.Payload) ? x.Payload : x.Content)]; + List contents = [new TextContent(x.RoleContent)]; if (allowMultiModal) { foreach (var file in x.Files) diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs index a36e32b66..3812a5916 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Chat/ChatCompletionProvider.cs @@ -402,11 +402,11 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}")) })); - messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content)); + messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.RoleContent)); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; @@ -418,7 +418,7 @@ public async Task GetChatCompletionsStreamingAsync(Agent agent, } else if (message.Role == AgentRole.Assistant) { - var text = message.Content; + var text = message.RoleContent; var textPart = ChatMessageContentPart.CreateTextPart(text); var contentParts = new List { textPart }; diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs index 1e246c630..e6b614e67 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -555,10 +555,8 @@ private async Task OnUserAudioTranscriptionCompleted(RealtimeHu { var agentService = _services.GetRequiredService(); var state = _services.GetRequiredService(); - var fileStorage = _services.GetRequiredService(); var settingsService = _services.GetRequiredService(); var settings = settingsService.GetSetting(Provider, _model); - var allowMultiModal = settings != null && settings.MultiModal; var messages = new List(); @@ -623,44 +621,15 @@ private async Task OnUserAudioTranscriptionCompleted(RealtimeHu ChatToolCall.CreateFunctionToolCall(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.FunctionName, BinaryData.FromString(message.FunctionArgs ?? "{}")) })); - messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.Content)); + messages.Add(new ToolChatMessage(message.ToolCallId.IfNullOrEmptyAs(message.FunctionName), message.RoleContent)); } else if (message.Role == AgentRole.User) { - var text = !string.IsNullOrWhiteSpace(message.Payload) ? message.Payload : message.Content; - var textPart = ChatMessageContentPart.CreateTextPart(text); - var contentParts = new List { textPart }; - - if (allowMultiModal && !message.Files.IsNullOrEmpty()) - { - foreach (var file in message.Files) - { - if (!string.IsNullOrEmpty(file.FileData)) - { - var (contentType, binary) = FileUtility.GetFileInfoFromData(file.FileData); - var contentPart = ChatMessageContentPart.CreateImagePart(binary, contentType, ChatImageDetailLevel.Auto); - contentParts.Add(contentPart); - } - else if (!string.IsNullOrEmpty(file.FileStorageUrl)) - { - var contentType = FileUtility.GetFileContentType(file.FileStorageUrl); - var binary = fileStorage.GetFileBytes(file.FileStorageUrl); - var contentPart = ChatMessageContentPart.CreateImagePart(binary, contentType, ChatImageDetailLevel.Auto); - contentParts.Add(contentPart); - } - else if (!string.IsNullOrEmpty(file.FileUrl)) - { - var uri = new Uri(file.FileUrl); - var contentPart = ChatMessageContentPart.CreateImagePart(uri, ChatImageDetailLevel.Auto); - contentParts.Add(contentPart); - } - } - } - messages.Add(new UserChatMessage(contentParts) { ParticipantName = message.FunctionName }); + messages.Add(new UserChatMessage(message.RoleContent)); } else if (message.Role == AgentRole.Assistant) { - messages.Add(new AssistantChatMessage(message.Content)); + messages.Add(new AssistantChatMessage(message.RoleContent)); } } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs index 477778a00..43820cc37 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs @@ -99,7 +99,7 @@ public async Task Execute(RoleDialogModel message) Language = "python" } }; - message.StopCompletion = true; + //message.StopCompletion = true; // Restore the original stdout/stderr sys.stdout = sys.__stdout__; diff --git a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs index 59c43dc5f..93b90d13f 100644 --- a/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.SparkDesk/Providers/ChatCompletionProvider.cs @@ -269,13 +269,13 @@ public void SetModelName(string model) } else if (message.Role == "user") { - var userMessage = ChatMessage.FromUser(message.Content); + var userMessage = ChatMessage.FromUser(message.RoleContent); messages.Add(userMessage); } else if (message.Role == "assistant") { - messages.Add(ChatMessage.FromAssistant(message.Content)); + messages.Add(ChatMessage.FromAssistant(message.RoleContent)); } } From 2770c4fe22865853339fb95f51ada01ea236eef9 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 12:10:02 -0500 Subject: [PATCH 11/26] init code interpret service --- .../CodeInterpreter/ICodeInterpretService.cs | 10 +++ .../Models/CodeInterpretOptions.cs | 5 ++ .../Models/CodeInterpretResult.cs | 8 ++ .../Functions/PyProgrammerFn.cs | 2 +- .../PythonInterpreterPlugin.cs | 1 + .../Services/PyInterpretService.cs | 87 +++++++++++++++++++ .../Using.cs | 3 + 7 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretResult.cs create mode 100644 src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs new file mode 100644 index 000000000..05afe3c1b --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs @@ -0,0 +1,10 @@ +using BotSharp.Abstraction.CodeInterpreter.Models; + +namespace BotSharp.Abstraction.CodeInterpreter; + +public interface ICodeInterpretService +{ + string Provider { get; } + + Task RunCode(string code, List? arguments = null, CodeInterpretOptions? options = null); +} diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs new file mode 100644 index 000000000..ded31cbe4 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs @@ -0,0 +1,5 @@ +namespace BotSharp.Abstraction.CodeInterpreter.Models; + +public class CodeInterpretOptions +{ +} diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretResult.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretResult.cs new file mode 100644 index 000000000..30b44a97c --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretResult.cs @@ -0,0 +1,8 @@ +namespace BotSharp.Abstraction.CodeInterpreter.Models; + +public class CodeInterpretResult +{ + public object Result { get; set; } + public bool Success { get; set; } + public string? ErrorMsg { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs index 43820cc37..477778a00 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs @@ -99,7 +99,7 @@ public async Task Execute(RoleDialogModel message) Language = "python" } }; - //message.StopCompletion = true; + message.StopCompletion = true; // Restore the original stdout/stderr sys.stdout = sys.__stdout__; diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs index 14ae5dc2f..168d83f82 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/PythonInterpreterPlugin.cs @@ -22,6 +22,7 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) services.AddSingleton(x => settings); services.AddScoped(); + services.AddScoped(); } public void Configure(IApplicationBuilder app) diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs new file mode 100644 index 000000000..5fe00abf5 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs @@ -0,0 +1,87 @@ +using BotSharp.Abstraction.Models; +using Microsoft.Extensions.Logging; +using Python.Runtime; +using System.Threading.Tasks; + +namespace BotSharp.Plugin.PythonInterpreter.Services; + +public class PyInterpretService : ICodeInterpretService +{ + private readonly IServiceProvider _services; + private readonly ILogger _logger; + + public PyInterpretService( + IServiceProvider services, + ILogger logger) + { + _services = services; + _logger = logger; + } + + public string Provider => "python-interpreter"; + + public async Task RunCode(string code, List? arguments = null, CodeInterpretOptions? options = null) + { + try + { + using (Py.GIL()) + { + // Import necessary Python modules + dynamic sys = Py.Import("sys"); + dynamic io = Py.Import("io"); + + // Redirect standard output/error to capture it + dynamic stringIO = io.StringIO(); + sys.stdout = stringIO; + sys.stderr = stringIO; + + // Set global items + using var globals = new PyDict(); + if (code.Contains("__main__") == true) + { + globals.SetItem("__name__", new PyString("__main__")); + } + + // Set arguments + if (!arguments.IsNullOrEmpty()) + { + sys.argv = new PyList(); + sys.argv.Append("code.py"); + + foreach (var arg in arguments) + { + sys.argv.Append($"--{arg.Key}"); + sys.argv.Append($"{arg.Value}"); + } + } + + // Execute Python script + PythonEngine.Exec(code, globals); + + // Get result + var result = stringIO.getvalue().ToString(); + + // Restore the original stdout/stderr + sys.stdout = sys.__stdout__; + sys.stderr = sys.__stderr__; + + return new CodeInterpretResult + { + Result = result, + Success = true + }; + } + } + catch (Exception ex) + { + var errorMsg = $"Error when executing python code in {nameof(PyInterpretService)}-{Provider}. {ex.Message}"; + _logger.LogError(ex, errorMsg); + + return new CodeInterpretResult + { + Success = false, + ErrorMsg = errorMsg + }; + } + } +} diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs index 73d0bc868..11a023fe7 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Using.cs @@ -20,6 +20,8 @@ global using BotSharp.Abstraction.Messaging.Models.RichContent; global using BotSharp.Abstraction.Messaging.Models.RichContent.Template; global using BotSharp.Abstraction.Routing; +global using BotSharp.Abstraction.CodeInterpreter.Models; +global using BotSharp.Abstraction.CodeInterpreter; global using BotSharp.Core.Infrastructures; @@ -30,3 +32,4 @@ global using BotSharp.Plugin.PythonInterpreter.Hooks; global using BotSharp.Plugin.PythonInterpreter.Models; global using BotSharp.Plugin.PythonInterpreter.Helpers; +global using BotSharp.Plugin.PythonInterpreter.Services; \ No newline at end of file From 2e0069d4e50eb209608dc6698a1bf8f48ed1fed9 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 15:03:06 -0500 Subject: [PATCH 12/26] init code template in instruct service --- .../CodeInterpreter/ICodeInterpretService.cs | 2 +- .../Instructs/IInstructService.cs | 13 ++-- .../Instructs/Models/CodeInstructOptions.cs | 7 ++ .../Instructs/Models/InstructResult.cs | 3 +- .../BotSharp.Abstraction/Models/KeyValue.cs | 11 ++++ .../Services/InstructService.Execute.cs | 65 +++++++++++++------ .../Controllers/InstructModeController.cs | 5 +- .../Instructs/Request/InstructMessageModel.cs | 3 + .../Services/PyInterpretService.cs | 15 +++-- 9 files changed, 90 insertions(+), 34 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs index 05afe3c1b..306ab2424 100644 --- a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs @@ -6,5 +6,5 @@ public interface ICodeInterpretService { string Provider { get; } - Task RunCode(string code, List? arguments = null, CodeInterpretOptions? options = null); + Task RunCode(string code, IEnumerable? arguments = null, CodeInterpretOptions? options = null); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs index 7dce24237..00d7534a3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs @@ -7,13 +7,16 @@ public interface IInstructService /// /// Execute completion by using specified instruction or template /// - /// Agent (static agent) - /// Additional message provided by user - /// Template name - /// System prompt + /// + /// + /// + /// + /// + /// /// Task Execute(string agentId, RoleDialogModel message, - string? templateName = null, string? instruction = null, IEnumerable? files = null); + string? instruction = null, string? llmTemplateName = null, + IEnumerable? files = null, CodeInstructOptions? codeOptions = null); /// /// A generic way to execute completion by using specified instruction or template diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs new file mode 100644 index 000000000..9b9265f45 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Instructs.Models; + +public class CodeInstructOptions +{ + public string? CodeTemplateName { get; set; } + public string? CodeInterpretProvider { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs index fe236863b..efaf4ffd0 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/InstructResult.cs @@ -6,7 +6,8 @@ public class InstructResult : ITrackableMessage public string MessageId { get; set; } public string Text { get; set; } = string.Empty; public object? Data { get; set; } + [JsonPropertyName("template")] - public string? Template{ get; set; } + public string? Template { get; set; } public Dictionary? States { get; set; } = new(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs b/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs index 3cb9c73f4..8bf0e1898 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Models/KeyValue.cs @@ -8,6 +8,17 @@ public class KeyValue [JsonPropertyName("value")] public string? Value { get; set; } + public KeyValue() + { + + } + + public KeyValue(string key, string? value) + { + Key = key; + Value = value; + } + public override string ToString() { return $"Key: {Key}, Value: {Value}"; diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 773392f1c..e09caf016 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -1,35 +1,66 @@ +using BotSharp.Abstraction.CodeInterpreter; using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Instructs.Models; using BotSharp.Abstraction.MLTasks; +using BotSharp.Abstraction.Models; namespace BotSharp.Core.Instructs; public partial class InstructService { - public async Task Execute(string agentId, RoleDialogModel message, - string? templateName = null, string? instruction = null, IEnumerable? files = null) + public async Task Execute( + string agentId, + RoleDialogModel message, + string? instruction = null, + string? llmTemplateName = null, + IEnumerable? files = null, + CodeInstructOptions? codeOptions = null) { var agentService = _services.GetRequiredService(); Agent agent = await agentService.LoadAgent(agentId); + var response = new InstructResult + { + MessageId = message.MessageId, + Template = codeOptions?.CodeTemplateName ?? llmTemplateName + }; + + if (agent == null) { - return new InstructResult - { - MessageId = message.MessageId, - Text = $"Agent (id: {agentId}) does not exist!" - }; + response.Text = $"Agent (id: {agentId}) does not exist!"; + return response; } if (agent.Disabled) { var content = $"This agent ({agent.Name}) is disabled, please install the corresponding plugin ({agent.Plugin.Name}) to activate this agent."; - return new InstructResult + response.Text = content; + return response; + } + + // Run code template + if (!string.IsNullOrWhiteSpace(codeOptions?.CodeTemplateName)) + { + var codeInterpreter = _services.GetServices() + .FirstOrDefault(x => x.Provider.IsEqualTo(codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs("python-interpreter"))); + + if (codeInterpreter == null) + { + var error = "No code interpreter found."; + _logger.LogError(error); + response.Text = error; + } + else { - MessageId = message.MessageId, - Text = content - }; + var state = _services.GetRequiredService(); + var arguments = state.GetStates().Select(x => new KeyValue(x.Key, x.Value)); + var result = await codeInterpreter.RunCode("", arguments); + response.Text = result?.Result?.ToString(); + } + return response; } + // Trigger before completion hooks var hooks = _services.GetHooks(agentId); @@ -52,19 +83,13 @@ public async Task Execute(string agentId, RoleDialogModel messag var model = string.Empty; // Render prompt - var prompt = string.IsNullOrEmpty(templateName) ? + var prompt = string.IsNullOrEmpty(llmTemplateName) ? agentService.RenderInstruction(agent) : - agentService.RenderTemplate(agent, templateName); + agentService.RenderTemplate(agent, llmTemplateName); var completer = CompletionProvider.GetCompletion(_services, agentConfig: agent.LlmConfig); - var response = new InstructResult - { - MessageId = message.MessageId, - Template = templateName, - }; - if (completer is ITextCompletion textCompleter) { instruction = null; @@ -111,7 +136,7 @@ await hook.OnResponseGenerated(new InstructResponseModel AgentId = agentId, Provider = provider, Model = model, - TemplateName = templateName, + TemplateName = llmTemplateName, UserMessage = prompt, SystemInstruction = instruction, CompletionText = response.Text diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index 852d780a6..ad4904c03 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -36,9 +36,10 @@ public async Task InstructCompletion([FromRoute] string agentId, var instructor = _services.GetRequiredService(); var result = await instructor.Execute(agentId, new RoleDialogModel(AgentRole.User, input.Text), - templateName: input.Template, instruction: input.Instruction, - files: input.Files); + llmTemplateName: input.Template, + files: input.Files, + codeOptions: input.CodeOptions); result.States = state.GetStates(); diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs index aec81ffbe..05c151be1 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Instructs.Models; + namespace BotSharp.OpenAPI.ViewModels.Instructs; public class InstructMessageModel : IncomingMessageModel @@ -9,6 +11,7 @@ public class InstructMessageModel : IncomingMessageModel public override string Channel { get; set; } = ConversationChannel.OpenAPI; public string? Template { get; set; } public List Files { get; set; } = []; + public CodeInstructOptions? CodeOptions { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs index 5fe00abf5..a88e3a7cd 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs @@ -20,7 +20,8 @@ public PyInterpretService( public string Provider => "python-interpreter"; - public async Task RunCode(string code, List? arguments = null, CodeInterpretOptions? options = null) + public async Task RunCode(string code, + IEnumerable? arguments = null, CodeInterpretOptions? options = null) { try { @@ -45,14 +46,18 @@ public async Task RunCode(string code, List? argu // Set arguments if (!arguments.IsNullOrEmpty()) { - sys.argv = new PyList(); - sys.argv.Append("code.py"); + var list = new PyList(); + list.Append(new PyString("code.py")); foreach (var arg in arguments) { - sys.argv.Append($"--{arg.Key}"); - sys.argv.Append($"{arg.Value}"); + if (!string.IsNullOrWhiteSpace(arg.Key) && !string.IsNullOrWhiteSpace(arg.Value)) + { + list.Append(new PyString($"--{arg.Key}")); + list.Append(new PyString($"{arg.Value}")); + } } + sys.argv = list; } // Execute Python script From 1be9ffebaaab54934c8bfd0007991dec847a53f1 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 15:26:32 -0500 Subject: [PATCH 13/26] init agent code --- .../Agents/Models/AgentCodeScript.cs | 22 +++++++ .../CodeInterpreter/ICodeInterpretService.cs | 2 +- .../Instructs/IInstructService.cs | 4 +- .../Instructs/Models/CodeInstructOptions.cs | 2 +- .../Repositories/IBotSharpRepository.cs | 9 ++- .../Services/InstructService.Execute.cs | 31 +++++++--- .../FileRepository.AgentCode.cs | 61 +++++++++++++++++++ .../FileRepository/FileRepository.cs | 1 + .../Controllers/InstructModeController.cs | 1 - .../Services/PyInterpretService.cs | 6 +- 10 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs create mode 100644 src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs new file mode 100644 index 000000000..ca328d130 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs @@ -0,0 +1,22 @@ +namespace BotSharp.Abstraction.Agents.Models; + +public class AgentCodeScript +{ + public string Name { get; set; } + public string Content { get; set; } + + public AgentCodeScript() + { + } + + public AgentCodeScript(string name, string content) + { + Name = name; + Content = content; + } + + public override string ToString() + { + return Name; + } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs index 306ab2424..49b6b91da 100644 --- a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs @@ -6,5 +6,5 @@ public interface ICodeInterpretService { string Provider { get; } - Task RunCode(string code, IEnumerable? arguments = null, CodeInterpretOptions? options = null); + Task RunCode(string codeScript, IEnumerable? arguments = null, CodeInterpretOptions? options = null); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs index 00d7534a3..3fe1c03f5 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructService.cs @@ -10,12 +10,12 @@ public interface IInstructService /// /// /// - /// + /// /// /// /// Task Execute(string agentId, RoleDialogModel message, - string? instruction = null, string? llmTemplateName = null, + string? instruction = null, string? templateName = null, IEnumerable? files = null, CodeInstructOptions? codeOptions = null); /// diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs index 9b9265f45..673e3825c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs @@ -2,6 +2,6 @@ namespace BotSharp.Abstraction.Instructs.Models; public class CodeInstructOptions { - public string? CodeTemplateName { get; set; } + public string? CodeScriptName { get; set; } public string? CodeInterpretProvider { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 8e0e2d55c..f13af9a64 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -85,7 +85,6 @@ string GetAgentTemplate(string agentId, string templateName) => throw new NotImplementedException(); bool PatchAgentTemplate(string agentId, AgentTemplate template) => throw new NotImplementedException(); - bool UpdateAgentLabels(string agentId, List labels) => throw new NotImplementedException(); bool AppendAgentLabels(string agentId, List labels) @@ -109,6 +108,14 @@ bool DeleteAgentTasks() => throw new NotImplementedException(); #endregion + #region Agent Code + string? GetAgentCodeScript(string agentId, string scriptName) + => throw new NotImplementedException(); + + bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) + => throw new NotImplementedException(); + #endregion + #region Conversation void CreateNewConversation(Conversation conversation) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index e09caf016..85960878d 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -12,7 +12,7 @@ public async Task Execute( string agentId, RoleDialogModel message, string? instruction = null, - string? llmTemplateName = null, + string? templateName = null, IEnumerable? files = null, CodeInstructOptions? codeOptions = null) { @@ -22,7 +22,7 @@ public async Task Execute( var response = new InstructResult { MessageId = message.MessageId, - Template = codeOptions?.CodeTemplateName ?? llmTemplateName + Template = codeOptions?.CodeScriptName ?? templateName }; @@ -40,23 +40,36 @@ public async Task Execute( } // Run code template - if (!string.IsNullOrWhiteSpace(codeOptions?.CodeTemplateName)) + if (!string.IsNullOrWhiteSpace(codeOptions?.CodeScriptName)) { var codeInterpreter = _services.GetServices() .FirstOrDefault(x => x.Provider.IsEqualTo(codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs("python-interpreter"))); if (codeInterpreter == null) { - var error = "No code interpreter found."; + var error = $"No code interpreter found. (Agent: {agentId}, Code interpreter: {codeOptions.CodeInterpretProvider})"; _logger.LogError(error); response.Text = error; } else { + var db = _services.GetRequiredService(); var state = _services.GetRequiredService(); + var arguments = state.GetStates().Select(x => new KeyValue(x.Key, x.Value)); - var result = await codeInterpreter.RunCode("", arguments); - response.Text = result?.Result?.ToString(); + var codeScript = db.GetAgentCodeScript(agentId, codeOptions.CodeScriptName); + + if (string.IsNullOrWhiteSpace(codeScript)) + { + var error = $"Empty code script. (Agent: {agentId}, Code script: {codeOptions.CodeScriptName})"; + _logger.LogError(error); + response.Text = error; + } + else + { + var result = await codeInterpreter.RunCode(codeScript, arguments); + response.Text = result?.Result?.ToString(); + } } return response; } @@ -83,9 +96,9 @@ public async Task Execute( var model = string.Empty; // Render prompt - var prompt = string.IsNullOrEmpty(llmTemplateName) ? + var prompt = string.IsNullOrEmpty(templateName) ? agentService.RenderInstruction(agent) : - agentService.RenderTemplate(agent, llmTemplateName); + agentService.RenderTemplate(agent, templateName); var completer = CompletionProvider.GetCompletion(_services, agentConfig: agent.LlmConfig); @@ -136,7 +149,7 @@ await hook.OnResponseGenerated(new InstructResponseModel AgentId = agentId, Provider = provider, Model = model, - TemplateName = llmTemplateName, + TemplateName = templateName, UserMessage = prompt, SystemInstruction = instruction, CompletionText = response.Text diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs new file mode 100644 index 000000000..b8c18ddde --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs @@ -0,0 +1,61 @@ +using System.IO; + +namespace BotSharp.Core.Repository; + +public partial class FileRepository +{ + #region Code + public string? GetAgentCodeScript(string agentId, string scriptName) + { + if (string.IsNullOrWhiteSpace(agentId) + || string.IsNullOrWhiteSpace(scriptName)) + { + return null; + } + + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODE_FOLDER); + if (!Directory.Exists(dir)) + { + return null; + } + + foreach (var file in Directory.GetFiles(dir)) + { + var fileName = Path.GetFileName(file); + if (scriptName.IsEqualTo(fileName)) + { + return File.ReadAllText(file); + } + } + return string.Empty; + } + + public bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) + { + if (string.IsNullOrEmpty(agentId) || script == null) + { + return false; + } + + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODE_FOLDER); + if (!Directory.Exists(dir)) + { + return false; + } + + var found = Directory.GetFiles(dir).FirstOrDefault(f => + { + var fileName = Path.GetFileName(f); + return fileName.IsEqualTo(script.Name); + }); + + if (found == null) + { + return false; + } + + File.WriteAllText(found, script.Content); + return true; + } + #endregion +} diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index 8e448c25e..78ff764cb 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -26,6 +26,7 @@ public partial class FileRepository : IBotSharpRepository private const string AGENT_FUNCTIONS_FOLDER = "functions"; private const string AGENT_TEMPLATES_FOLDER = "templates"; private const string AGENT_RESPONSES_FOLDER = "responses"; + private const string AGENT_CODE_FOLDER = "codes"; private const string AGENT_TASKS_FOLDER = "tasks"; private const string AGENT_TASK_PREFIX = "#metadata"; private const string AGENT_TASK_SUFFIX = "/metadata"; diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index ad4904c03..cf0762591 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -42,7 +42,6 @@ public async Task InstructCompletion([FromRoute] string agentId, codeOptions: input.CodeOptions); result.States = state.GetStates(); - return result; } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs index a88e3a7cd..5f7a89614 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs @@ -20,7 +20,7 @@ public PyInterpretService( public string Provider => "python-interpreter"; - public async Task RunCode(string code, + public async Task RunCode(string codeScript, IEnumerable? arguments = null, CodeInterpretOptions? options = null) { try @@ -38,7 +38,7 @@ public async Task RunCode(string code, // Set global items using var globals = new PyDict(); - if (code.Contains("__main__") == true) + if (codeScript.Contains("__main__") == true) { globals.SetItem("__name__", new PyString("__main__")); } @@ -61,7 +61,7 @@ public async Task RunCode(string code, } // Execute Python script - PythonEngine.Exec(code, globals); + PythonEngine.Exec(codeScript, globals); // Get result var result = stringIO.getvalue().ToString(); From 75d0cf1ae1449b18d810c90963055fa7a4ec9ba3 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 16:03:58 -0500 Subject: [PATCH 14/26] add arguments in options --- .../Instructs/Models/CodeInstructOptions.cs | 1 + .../Repositories/IBotSharpRepository.cs | 2 +- .../Instructs/Services/InstructService.Execute.cs | 4 +--- .../Repository/FileRepository/FileRepository.Agent.cs | 6 ++++++ .../Repository/FileRepository/FileRepository.AgentCode.cs | 6 +++--- .../Repository/FileRepository/FileRepository.cs | 2 +- .../BotSharp.OpenAPI/Controllers/InstructModeController.cs | 2 +- .../Services/PyInterpretService.cs | 2 +- 8 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs index 673e3825c..721266531 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructOptions.cs @@ -4,4 +4,5 @@ public class CodeInstructOptions { public string? CodeScriptName { get; set; } public string? CodeInterpretProvider { get; set; } + public List? Arguments { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index f13af9a64..3da95e029 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -112,7 +112,7 @@ bool DeleteAgentTasks() string? GetAgentCodeScript(string agentId, string scriptName) => throw new NotImplementedException(); - bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) + bool PatchAgentCodeScript(string agentId, AgentCodeScript script) => throw new NotImplementedException(); #endregion diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 85960878d..3916beb1f 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -56,9 +56,7 @@ public async Task Execute( var db = _services.GetRequiredService(); var state = _services.GetRequiredService(); - var arguments = state.GetStates().Select(x => new KeyValue(x.Key, x.Value)); var codeScript = db.GetAgentCodeScript(agentId, codeOptions.CodeScriptName); - if (string.IsNullOrWhiteSpace(codeScript)) { var error = $"Empty code script. (Agent: {agentId}, Code script: {codeOptions.CodeScriptName})"; @@ -67,7 +65,7 @@ public async Task Execute( } else { - var result = await codeInterpreter.RunCode(codeScript, arguments); + var result = await codeInterpreter.RunCode(codeScript, codeOptions.Arguments); response.Text = result?.Result?.ToString(); } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index 35351311c..bd2599526 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -547,6 +547,12 @@ join u in Users on ua.UserId equals u.Id public string GetAgentTemplate(string agentId, string templateName) { + if (string.IsNullOrWhiteSpace(agentId) + || string.IsNullOrWhiteSpace(templateName)) + { + return string.Empty; + } + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_TEMPLATES_FOLDER); if (!Directory.Exists(dir)) return string.Empty; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs index b8c18ddde..a60d8146f 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs @@ -13,7 +13,7 @@ public partial class FileRepository return null; } - var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODE_FOLDER); + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER); if (!Directory.Exists(dir)) { return null; @@ -30,14 +30,14 @@ public partial class FileRepository return string.Empty; } - public bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) + public bool PatchAgentCodeScript(string agentId, AgentCodeScript script) { if (string.IsNullOrEmpty(agentId) || script == null) { return false; } - var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODE_FOLDER); + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER); if (!Directory.Exists(dir)) { return false; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index 78ff764cb..69e8bd38f 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -26,7 +26,7 @@ public partial class FileRepository : IBotSharpRepository private const string AGENT_FUNCTIONS_FOLDER = "functions"; private const string AGENT_TEMPLATES_FOLDER = "templates"; private const string AGENT_RESPONSES_FOLDER = "responses"; - private const string AGENT_CODE_FOLDER = "codes"; + private const string AGENT_CODES_FOLDER = "codes"; private const string AGENT_TASKS_FOLDER = "tasks"; private const string AGENT_TASK_PREFIX = "#metadata"; private const string AGENT_TASK_SUFFIX = "/metadata"; diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs index cf0762591..eab8637c1 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs @@ -37,7 +37,7 @@ public async Task InstructCompletion([FromRoute] string agentId, var result = await instructor.Execute(agentId, new RoleDialogModel(AgentRole.User, input.Text), instruction: input.Instruction, - llmTemplateName: input.Template, + templateName: input.Template, files: input.Files, codeOptions: input.CodeOptions); diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs index 5f7a89614..82739f2ae 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs @@ -79,7 +79,7 @@ public async Task RunCode(string codeScript, } catch (Exception ex) { - var errorMsg = $"Error when executing python code in {nameof(PyInterpretService)}-{Provider}. {ex.Message}"; + var errorMsg = $"Error when executing python code in {nameof(PyInterpretService)}: {Provider}. {ex.Message}"; _logger.LogError(ex, errorMsg); return new CodeInterpretResult From 82d2311018db23b29991abcb9b6257e075bc023b Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 17:28:35 -0500 Subject: [PATCH 15/26] complete db on code scripts --- .../Agents/IAgentService.cs | 1 - .../Agents/Models/AgentCodeScript.cs | 8 +- .../Repositories/IBotSharpRepository.cs | 15 ++- .../Services/AgentService.CreateAgent.cs | 24 ++++ .../Services/AgentService.RefreshAgents.cs | 7 +- .../Services/AgentService.UpdateAgent.cs | 107 +--------------- .../FileRepository.AgentCode.cs | 107 +++++++++++++++- .../FileRepository.AgentTask.cs | 25 ++-- .../Tasks/Services/AgentTaskService.cs | 2 +- .../Controllers/AgentController.cs | 6 - .../Collections/AgentCodeDocument.cs | 32 +++++ .../MongoDbContext.cs | 3 + .../Repository/MongoRepository.Agent.cs | 7 +- .../Repository/MongoRepository.AgentCode.cs | 116 ++++++++++++++++++ .../Repository/MongoRepository.AgentTask.cs | 42 +++---- 15 files changed, 337 insertions(+), 165 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeDocument.cs create mode 100644 src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index c195df5e9..a2f2a17f2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -59,7 +59,6 @@ public interface IAgentService /// /// Task PatchAgentTemplate(Agent agent); - Task UpdateAgentFromFile(string id); string GetDataDir(); string GetAgentDataDir(string agentId); diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs index ca328d130..9782f8484 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs @@ -2,6 +2,8 @@ namespace BotSharp.Abstraction.Agents.Models; public class AgentCodeScript { + public string Id { get; set; } + public string AgentId { get; set; } public string Name { get; set; } public string Content { get; set; } @@ -9,12 +11,6 @@ public AgentCodeScript() { } - public AgentCodeScript(string name, string content) - { - Name = name; - Content = content; - } - public override string ToString() { return Name; diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 3da95e029..3d23352a2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -98,21 +98,24 @@ ValueTask> GetAgentTasks(AgentTaskFilter filter) => throw new NotImplementedException(); void InsertAgentTask(AgentTask task) => throw new NotImplementedException(); - void BulkInsertAgentTasks(List tasks) + void BulkInsertAgentTasks(string agentId, List tasks) => throw new NotImplementedException(); void UpdateAgentTask(AgentTask task, AgentTaskField field) => throw new NotImplementedException(); - bool DeleteAgentTask(string agentId, List taskIds) - => throw new NotImplementedException(); - bool DeleteAgentTasks() + bool DeleteAgentTasks(string agentId, List? taskIds = null) => throw new NotImplementedException(); #endregion #region Agent Code + List GetAgentCodeScripts(string agentId, List? scriptNames = null) + => throw new NotImplementedException(); string? GetAgentCodeScript(string agentId, string scriptName) => throw new NotImplementedException(); - - bool PatchAgentCodeScript(string agentId, AgentCodeScript script) + bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) + => throw new NotImplementedException(); + bool BulkInsertAgentCodeScripts(string agentId, List scripts) + => throw new NotImplementedException(); + bool DeleteAgentCodeScripts(string agentId, List? scriptNames) => throw new NotImplementedException(); #endregion diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index fb4d49c4d..95b8e27fa 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -218,4 +218,28 @@ private List GetTasksFromFile(string fileDir) task.Content = content.Substring(suffix.Length).Trim(); return task; } + + private List GetCodeScriptsFromFile(string fileDir) + { + var scripts = new List(); + var codeDir = Path.Combine(fileDir, "codes"); + if (!Directory.Exists(codeDir)) + { + return scripts; + } + + var agentId = fileDir.Split(Path.DirectorySeparatorChar).Last(); + foreach (var file in Directory.GetFiles(codeDir)) + { + var script = new AgentCodeScript + { + AgentId = agentId, + Name = Path.GetFileName(file), + Content = File.ReadAllText(file) + }; + scripts.Add(script); + } + + return scripts; + } } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs index 506e00bb6..cc38a151a 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.RefreshAgents.cs @@ -60,13 +60,16 @@ public async Task RefreshAgents(IEnumerable? agentIds = null) .SetSamples(samples); var tasks = GetTasksFromFile(dir); + var codeScripts = GetCodeScriptsFromFile(dir); var isAgentDeleted = _db.DeleteAgent(agent.Id); if (isAgentDeleted) { await Task.Delay(100); - _db.BulkInsertAgents(new List { agent }); - _db.BulkInsertAgentTasks(tasks); + _db.BulkInsertAgents([agent]); + _db.BulkInsertAgentTasks(agent.Id, tasks); + _db.BulkInsertAgentCodeScripts(agent.Id, codeScripts); + refreshedAgents.Add(agent.Name); _logger.LogInformation($"Agent {agent.Name} has been migrated."); } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index d99b39d96..4a2e1ec51 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -1,8 +1,5 @@ -using BotSharp.Abstraction.Repositories.Enums; -using BotSharp.Abstraction.Repositories.Settings; using BotSharp.Abstraction.Users.Enums; using BotSharp.Abstraction.Users.Models; -using System.IO; namespace BotSharp.Core.Agents.Services; @@ -13,7 +10,7 @@ public async Task UpdateAgent(Agent agent, AgentField updateField) if (agent == null || string.IsNullOrEmpty(agent.Id)) return; var userService = _services.GetRequiredService(); - var auth = await userService.GetUserAuthorizations(new List { agent.Id }); + var auth = await userService.GetUserAuthorizations([agent.Id]); var allowEdit = auth.IsAgentActionAllowed(agent.Id, UserAction.Edit); if (!allowEdit) @@ -57,81 +54,6 @@ public async Task UpdateAgent(Agent agent, AgentField updateField) await Task.CompletedTask; } - public async Task UpdateAgentFromFile(string id) - { - string updateResult; - var dbSettings = _services.GetRequiredService(); - var agentSettings = _services.GetRequiredService(); - - if (dbSettings.Default == RepositoryEnum.FileRepository) - { - updateResult = $"Invalid database repository setting: {dbSettings.Default}"; - _logger.LogWarning(updateResult); - return updateResult; - } - - var agent = _db.GetAgent(id); - if (agent == null) - { - updateResult = $"Cannot find agent ${id}"; - _logger.LogError(updateResult); - return updateResult; - } - - var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, - dbSettings.FileRepository, - agentSettings.DataDir); - - var clonedAgent = Agent.Clone(agent); - var foundAgent = GetAgentFileById(agent.Id, filePath); - if (foundAgent == null) - { - updateResult = $"Cannot find agent {agent.Name} in file directory: {filePath}"; - _logger.LogError(updateResult); - return updateResult; - } - - try - { - clonedAgent.SetId(foundAgent.Id) - .SetName(foundAgent.Name) - .SetType(foundAgent.Type) - .SetRoutingMode(foundAgent.Mode) - .SetFuncVisMode(foundAgent.FuncVisMode) - .SetIsPublic(foundAgent.IsPublic) - .SetDisabled(foundAgent.Disabled) - .SetDescription(foundAgent.Description) - .SetMergeUtility(foundAgent.MergeUtility) - .SetProfiles(foundAgent.Profiles) - .SetLabels(foundAgent.Labels) - .SetRoutingRules(foundAgent.RoutingRules) - .SetInstruction(foundAgent.Instruction) - .SetChannelInstructions(foundAgent.ChannelInstructions) - .SetTemplates(foundAgent.Templates) - .SetFunctions(foundAgent.Functions) - .SetResponses(foundAgent.Responses) - .SetSamples(foundAgent.Samples) - .SetUtilities(foundAgent.Utilities) - .SetMcpTools(foundAgent.McpTools) - .SetKnowledgeBases(foundAgent.KnowledgeBases) - .SetRules(foundAgent.Rules) - .SetLlmConfig(foundAgent.LlmConfig); - - _db.UpdateAgent(clonedAgent, AgentField.All); - Utilities.ClearCache(); - - updateResult = $"Agent {agent.Name} has been migrated!"; - _logger.LogInformation(updateResult); - return updateResult; - } - catch (Exception ex) - { - updateResult = $"Failed to migrate agent {agent.Name} in file directory {filePath}.\r\nError: {ex.Message}"; - _logger.LogError(updateResult); - return updateResult; - } - } - public async Task PatchAgentTemplate(Agent agent) { @@ -184,31 +106,4 @@ public async Task PatchAgentTemplate(Agent agent) return patchResult; } - - private Agent? GetAgentFileById(string agentId, string filePath) - { - if (!Directory.Exists(filePath)) return null; - - foreach (var dir in Directory.GetDirectories(filePath)) - { - var agentJson = File.ReadAllText(Path.Combine(dir, "agent.json")); - var agent = JsonSerializer.Deserialize(agentJson, _options); - if (agent != null && agent.Id == agentId) - { - var (defaultInstruction, channelInstructions) = GetInstructionsFromFile(dir); - var functions = GetFunctionsFromFile(dir); - var responses = GetResponsesFromFile(dir); - var templates = GetTemplatesFromFile(dir); - var samples = GetSamplesFromFile(dir); - return agent.SetInstruction(defaultInstruction) - .SetChannelInstructions(channelInstructions) - .SetTemplates(templates) - .SetFunctions(functions) - .SetResponses(responses) - .SetSamples(samples); - } - } - - return null; - } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs index a60d8146f..699341744 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs @@ -5,6 +5,39 @@ namespace BotSharp.Core.Repository; public partial class FileRepository { #region Code + public List GetAgentCodeScripts(string agentId, List? scriptNames = null) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return []; + } + + var dir = BuildAgentCodeDir(agentId); + if (!Directory.Exists(dir)) + { + return []; + } + + var results = new List(); + foreach (var file in Directory.GetFiles(dir)) + { + var fileName = Path.GetFileName(file); + if (scriptNames != null || !scriptNames.Contains(fileName)) + { + continue; + } + + var script = new AgentCodeScript + { + AgentId = agentId, + Name = fileName, + Content = File.ReadAllText(file) + }; + results.Add(script); + } + return results; + } + public string? GetAgentCodeScript(string agentId, string scriptName) { if (string.IsNullOrWhiteSpace(agentId) @@ -13,7 +46,7 @@ public partial class FileRepository return null; } - var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER); + var dir = BuildAgentCodeDir(agentId); if (!Directory.Exists(dir)) { return null; @@ -30,14 +63,14 @@ public partial class FileRepository return string.Empty; } - public bool PatchAgentCodeScript(string agentId, AgentCodeScript script) + public bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) { - if (string.IsNullOrEmpty(agentId) || script == null) + if (string.IsNullOrWhiteSpace(agentId) || script == null) { return false; } - var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER); + var dir = BuildAgentCodeDir(agentId); if (!Directory.Exists(dir)) { return false; @@ -57,5 +90,71 @@ public bool PatchAgentCodeScript(string agentId, AgentCodeScript script) File.WriteAllText(found, script.Content); return true; } + + public bool BulkInsertAgentCodeScripts(string agentId, List scripts) + { + if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) + { + return false; + } + + var dir = BuildAgentCodeDir(agentId); + if (!Directory.Exists(dir)) + { + return false; + } + + foreach (var script in scripts) + { + if (string.IsNullOrWhiteSpace(script.Name)) + { + continue; + } + + var path = Path.Combine(dir, script.Name); + File.WriteAllText(path, script.Content); + } + + return true; + } + + public bool DeleteAgentCodeScripts(string agentId, List? scriptNames) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return false; + } + + var dir = BuildAgentCodeDir(agentId); + if (!Directory.Exists(dir)) + { + return false; + } + + if (scriptNames == null) + { + Directory.Delete(dir, true); + return true; + } + else if (!scriptNames.Any()) + { + return false; + } + + foreach (var file in Directory.GetFiles(dir)) + { + var fileName = Path.GetFileName(file); + if (scriptNames.Contains(fileName)) + { + File.Delete(file); + } + } + return true; + } #endregion + + private string BuildAgentCodeDir(string agentId) + { + return Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER); + } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs index 2b84f212f..b3926e66f 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs @@ -137,7 +137,7 @@ public void InsertAgentTask(AgentTask task) File.WriteAllText(taskFile, fileContent); } - public void BulkInsertAgentTasks(List tasks) + public void BulkInsertAgentTasks(string agentId, List tasks) { } @@ -194,13 +194,25 @@ public void UpdateAgentTask(AgentTask task, AgentTaskField field) File.WriteAllText(taskFile, fileContent); } - public bool DeleteAgentTask(string agentId, List taskIds) + public bool DeleteAgentTasks(string agentId, List? taskIds = null) { var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId); - if (!Directory.Exists(agentDir) || taskIds.IsNullOrEmpty()) return false; + if (!Directory.Exists(agentDir)) + { + return false; + } var taskDir = Path.Combine(agentDir, AGENT_TASKS_FOLDER); - if (!Directory.Exists(taskDir)) return false; + if (!Directory.Exists(taskDir)) + { + return false; + } + + if (taskIds == null) + { + Directory.Delete(taskDir, true); + return true; + } var deletedTasks = new List(); foreach (var taskId in taskIds) @@ -215,11 +227,6 @@ public bool DeleteAgentTask(string agentId, List taskIds) return deletedTasks.Any(); } - public bool DeleteAgentTasks() - { - return false; - } - private string? FindTaskFileById(string taskDir, string taskId) { if (!Directory.Exists(taskDir) || string.IsNullOrEmpty(taskId)) return null; diff --git a/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs b/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs index 5ae52a73a..c6ac878db 100644 --- a/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs +++ b/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs @@ -91,7 +91,7 @@ public async Task UpdateTask(AgentTask task, AgentTaskField field) public async Task DeleteTask(string agentId, string taskId) { var db = _services.GetRequiredService(); - var isDeleted = db.DeleteAgentTask(agentId, new List { taskId }); + var isDeleted = db.DeleteAgentTasks(agentId, new List { taskId }); return await Task.FromResult(isDeleted); } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index b6950e04b..6aff56aee 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -115,12 +115,6 @@ public async Task RefreshAgents([FromBody] AgentMigrationModel request) return await _agentService.RefreshAgents(request?.AgentIds); } - [HttpPut("/agent/file/{agentId}")] - public async Task UpdateAgentFromFile([FromRoute] string agentId) - { - return await _agentService.UpdateAgentFromFile(agentId); - } - [HttpPut("/agent/{agentId}")] public async Task UpdateAgent([FromRoute] string agentId, [FromBody] AgentUpdateModel agent) { diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeDocument.cs new file mode 100644 index 000000000..f37db77d5 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeDocument.cs @@ -0,0 +1,32 @@ +using BotSharp.Abstraction.Agents.Models; + +namespace BotSharp.Plugin.MongoStorage.Collections; + +public class AgentCodeDocument : MongoBase +{ + public string AgentId { get; set; } = default!; + public string Name { get; set; } = default!; + public string Content { get; set; } = default!; + + public static AgentCodeDocument ToMongoModel(AgentCodeScript script) + { + return new AgentCodeDocument + { + Id = script.Id, + AgentId = script.AgentId, + Name = script.Name, + Content = script.Content + }; + } + + public static AgentCodeScript ToDomainModel(AgentCodeDocument script) + { + return new AgentCodeScript + { + Id = script.Id, + AgentId = script.AgentId, + Name = script.Name, + Content = script.Content + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs index 992dc6f82..6f171cb30 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs @@ -160,6 +160,9 @@ public IMongoCollection Agents public IMongoCollection AgentTasks => CreateAgentTaskIndex(); + public IMongoCollection AgentCodes + => GetCollectionOrCreate("AgentCodes"); + public IMongoCollection Conversations => CreateConversationIndex(); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 40558e131..407832d99 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Routing.Models; +using MongoDB.Driver; namespace BotSharp.Plugin.MongoStorage.Repository; @@ -593,6 +594,8 @@ public bool DeleteAgents() { _dc.UserAgents.DeleteMany(Builders.Filter.Empty); _dc.RoleAgents.DeleteMany(Builders.Filter.Empty); + _dc.AgentTasks.DeleteMany(Builders.Filter.Empty); + _dc.AgentCodes.DeleteMany(Builders.Filter.Empty); _dc.Agents.DeleteMany(Builders.Filter.Empty); return true; } @@ -612,11 +615,13 @@ public bool DeleteAgent(string agentId) var userAgentFilter = Builders.Filter.Eq(x => x.AgentId, agentId); var roleAgentFilter = Builders.Filter.Eq(x => x.AgentId, agentId); var agentTaskFilter = Builders.Filter.Eq(x => x.AgentId, agentId); + var agentCodeFilter = Builders.Filter.Eq(x => x.AgentId, agentId); - _dc.Agents.DeleteOne(agentFilter); _dc.UserAgents.DeleteMany(userAgentFilter); _dc.RoleAgents.DeleteMany(roleAgentFilter); _dc.AgentTasks.DeleteMany(agentTaskFilter); + _dc.AgentCodes.DeleteMany(agentCodeFilter); + _dc.Agents.DeleteOne(agentFilter); return true; } catch diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs new file mode 100644 index 000000000..c0fd21b75 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs @@ -0,0 +1,116 @@ +using BotSharp.Abstraction.Agents.Models; + +namespace BotSharp.Plugin.MongoStorage.Repository; + +public partial class MongoRepository +{ + #region Code + public List GetAgentCodeScripts(string agentId, List? scriptNames = null) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return []; + } + + var builder = Builders.Filter; + var filters = new List>() + { + builder.Eq(x => x.AgentId, agentId) + }; + + if (!scriptNames.IsNullOrEmpty()) + { + filters.Add(builder.In(x => x.Name, scriptNames)); + } + + var found = _dc.AgentCodes.Find(builder.And(filters)).ToList(); + return found.Select(x => AgentCodeDocument.ToDomainModel(x)).ToList(); + } + + public string? GetAgentCodeScript(string agentId, string scriptName) + { + if (string.IsNullOrWhiteSpace(agentId) + || string.IsNullOrWhiteSpace(scriptName)) + { + return null; + } + + var builder = Builders.Filter; + var filters = new List>() + { + builder.Eq(x => x.AgentId, agentId), + builder.Eq(x => x.Name, scriptName) + }; + + var found = _dc.AgentCodes.Find(builder.And(filters)).FirstOrDefault(); + return found?.Content; + } + + public bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) + { + if (string.IsNullOrWhiteSpace(agentId) || script == null) + { + return false; + } + + var builder = Builders.Filter; + var filters = new List>() + { + builder.Eq(x => x.AgentId, agentId), + builder.Eq(x => x.Name, script.Name) + }; + var filterDef = builder.And(filters); + + var found = _dc.AgentCodes.Find(filterDef).FirstOrDefault(); + if (found == null) + { + return false; + } + + var update = Builders.Update.Set(x => x.Content, script.Content); + _dc.AgentCodes.UpdateOne(filterDef, update); + return true; + } + + public bool InsertAgentCodeScripts(string agentId, List scripts) + { + if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) + { + return false; + } + + var docs = scripts.Select(x => + { + var script = AgentCodeDocument.ToMongoModel(x); + script.AgentId = agentId; + script.Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(); + return script; + }).ToList(); + + _dc.AgentCodes.InsertMany(docs); + return true; + } + + public bool BulkInsertAgentCodeScripts(string agentId, List? scriptNames) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return false; + } + + var filterDef = Builders.Filter.Empty; + if (scriptNames != null) + { + var builder = Builders.Filter; + var filters = new List> + { + builder.In(x => x.Name, scriptNames) + }; + filterDef = builder.And(filters); + } + + var deleted = _dc.AgentCodes.DeleteMany(filterDef); + return deleted.DeletedCount > 0; + } + #endregion +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs index 85f142157..060994d44 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentTask.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Tasks.Models; +using MongoDB.Driver; namespace BotSharp.Plugin.MongoStorage.Repository; @@ -86,13 +87,17 @@ public void InsertAgentTask(AgentTask task) _dc.AgentTasks.InsertOne(taskDoc); } - public void BulkInsertAgentTasks(List tasks) + public void BulkInsertAgentTasks(string agentId, List tasks) { - if (tasks.IsNullOrEmpty()) return; + if (string.IsNullOrWhiteSpace(agentId) || tasks.IsNullOrEmpty()) + { + return; + } var taskDocs = tasks.Select(x => { var task = AgentTaskDocument.ToMongoModel(x); + task.AgentId = agentId; task.Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(); return task; }).ToList(); @@ -138,30 +143,21 @@ public void UpdateAgentTask(AgentTask task, AgentTaskField field) _dc.AgentTasks.ReplaceOne(filter, taskDoc); } - public bool DeleteAgentTask(string agentId, List taskIds) - { - if (taskIds.IsNullOrEmpty()) return false; - - var builder = Builders.Filter; - var filters = new List> - { - builder.In(x => x.Id, taskIds) - }; - var taskDeleted = _dc.AgentTasks.DeleteMany(builder.And(filters)); - return taskDeleted.DeletedCount > 0; - } - - public bool DeleteAgentTasks() + public bool DeleteAgentTasks(string agentId, List? taskIds = null) { - try + var filterDef = Builders.Filter.Empty; + if (taskIds != null) { - _dc.AgentTasks.DeleteMany(Builders.Filter.Empty); - return true; - } - catch - { - return false; + var builder = Builders.Filter; + var filters = new List> + { + builder.In(x => x.Id, taskIds) + }; + filterDef = builder.And(filters); } + + var taskDeleted = _dc.AgentTasks.DeleteMany(filterDef); + return taskDeleted.DeletedCount > 0; } #endregion } From ed8a1d8890dc27eafce8c582d03c640ab22768f6 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 17:32:45 -0500 Subject: [PATCH 16/26] minor change --- .../Agents/Services/AgentService.CreateAgent.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index 95b8e27fa..cc0a92bc7 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -229,17 +229,12 @@ private List GetCodeScriptsFromFile(string fileDir) } var agentId = fileDir.Split(Path.DirectorySeparatorChar).Last(); - foreach (var file in Directory.GetFiles(codeDir)) + scripts = Directory.GetFiles(codeDir).Select(file => new AgentCodeScript { - var script = new AgentCodeScript - { - AgentId = agentId, - Name = Path.GetFileName(file), - Content = File.ReadAllText(file) - }; - scripts.Add(script); - } - + AgentId = agentId, + Name = Path.GetFileName(file), + Content = File.ReadAllText(file) + }).ToList(); return scripts; } } From bc500a4dfa2be97742d6af62f7215c850345cce6 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 17:46:58 -0500 Subject: [PATCH 17/26] minor change --- .../BotSharp.Core/Instructs/Functions/ExecuteTemplateFn.cs | 2 -- .../Repository/MongoRepository.AgentCode.cs | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Functions/ExecuteTemplateFn.cs b/src/Infrastructure/BotSharp.Core/Instructs/Functions/ExecuteTemplateFn.cs index b81c56910..2684ae02f 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Functions/ExecuteTemplateFn.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Functions/ExecuteTemplateFn.cs @@ -1,6 +1,4 @@ using BotSharp.Abstraction.Functions; -using BotSharp.Abstraction.Infrastructures; -using BotSharp.Abstraction.Instructs; using BotSharp.Abstraction.Instructs.Models; namespace BotSharp.Core.Instructs.Functions; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs index c0fd21b75..e07f3c8c8 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs @@ -72,7 +72,7 @@ public bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) return true; } - public bool InsertAgentCodeScripts(string agentId, List scripts) + public bool BulkInsertAgentCodeScripts(string agentId, List scripts) { if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) { @@ -91,7 +91,7 @@ public bool InsertAgentCodeScripts(string agentId, List scripts return true; } - public bool BulkInsertAgentCodeScripts(string agentId, List? scriptNames) + public bool DeleteAgentCodeScripts(string agentId, List? scriptNames) { if (string.IsNullOrWhiteSpace(agentId)) { From 0a7dbfe44f28c3f79b5e00dd521a11027ef36bf8 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 20:45:32 -0500 Subject: [PATCH 18/26] refine --- .../CodeInterpreter/ICodeInterpretService.cs | 3 +- .../Models/CodeInterpretOptions.cs | 1 + .../Repositories/IBotSharpRepository.cs | 2 +- .../Services/InstructService.Execute.cs | 5 ++- .../FileRepository.AgentCode.cs | 19 +++++------ .../Repository/MongoRepository.Agent.cs | 1 - .../Repository/MongoRepository.AgentCode.cs | 33 +++++++++---------- .../Functions/PyProgrammerFn.cs | 4 +-- .../Services/PyInterpretService.cs | 11 +++---- 9 files changed, 39 insertions(+), 40 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs index 49b6b91da..bdc10f5c9 100644 --- a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs @@ -6,5 +6,6 @@ public interface ICodeInterpretService { string Provider { get; } - Task RunCode(string codeScript, IEnumerable? arguments = null, CodeInterpretOptions? options = null); + Task RunCode(string codeScript, CodeInterpretOptions? options = null) + => throw new NotImplementedException(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs index ded31cbe4..5255c18ff 100644 --- a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs @@ -2,4 +2,5 @@ namespace BotSharp.Abstraction.CodeInterpreter.Models; public class CodeInterpretOptions { + public List? Arguments { get; set; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 3d23352a2..85f75e52a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -111,7 +111,7 @@ List GetAgentCodeScripts(string agentId, List? scriptNa => throw new NotImplementedException(); string? GetAgentCodeScript(string agentId, string scriptName) => throw new NotImplementedException(); - bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) + bool UpdateAgentCodeScripts(string agentId, List scripts) => throw new NotImplementedException(); bool BulkInsertAgentCodeScripts(string agentId, List scripts) => throw new NotImplementedException(); diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 3916beb1f..44ab141e0 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -65,7 +65,10 @@ public async Task Execute( } else { - var result = await codeInterpreter.RunCode(codeScript, codeOptions.Arguments); + var result = await codeInterpreter.RunCode(codeScript, options: new() + { + Arguments = codeOptions?.Arguments + }); response.Text = result?.Result?.ToString(); } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs index 699341744..b596b4ed1 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs @@ -63,9 +63,9 @@ public List GetAgentCodeScripts(string agentId, List? s return string.Empty; } - public bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) + public bool UpdateAgentCodeScripts(string agentId, List scripts) { - if (string.IsNullOrWhiteSpace(agentId) || script == null) + if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) { return false; } @@ -76,18 +76,17 @@ public bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) return false; } - var found = Directory.GetFiles(dir).FirstOrDefault(f => - { - var fileName = Path.GetFileName(f); - return fileName.IsEqualTo(script.Name); - }); + var dict = scripts.DistinctBy(x => x.Name).ToDictionary(x => x.Name, x => x); + var files = Directory.GetFiles(dir).Where(x => dict.Keys.Contains(Path.GetFileName(x))).ToList(); - if (found == null) + foreach (var file in files) { - return false; + if (dict.TryGetValue(Path.GetFileName(file), out var script)) + { + File.WriteAllText(file, script.Content); + } } - File.WriteAllText(found, script.Content); return true; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 407832d99..3d085c70c 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -2,7 +2,6 @@ using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Routing.Models; -using MongoDB.Driver; namespace BotSharp.Plugin.MongoStorage.Repository; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs index e07f3c8c8..580497f7f 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs @@ -46,30 +46,27 @@ public List GetAgentCodeScripts(string agentId, List? s return found?.Content; } - public bool UpdateAgentCodeScript(string agentId, AgentCodeScript script) + public bool UpdateAgentCodeScripts(string agentId, List scripts) { - if (string.IsNullOrWhiteSpace(agentId) || script == null) + if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) { return false; } var builder = Builders.Filter; - var filters = new List>() - { - builder.Eq(x => x.AgentId, agentId), - builder.Eq(x => x.Name, script.Name) - }; - var filterDef = builder.And(filters); - - var found = _dc.AgentCodes.Find(filterDef).FirstOrDefault(); - if (found == null) - { - return false; - } - - var update = Builders.Update.Set(x => x.Content, script.Content); - _dc.AgentCodes.UpdateOne(filterDef, update); - return true; + var ops = scripts.Where(x => !string.IsNullOrWhiteSpace(x.Name)) + .Select(x => new UpdateOneModel( + builder.And(new List> + { + builder.Eq(y => y.AgentId, agentId), + builder.Eq(y => y.Name, x.Name) + }), + Builders.Update.Set(y => y.Content, x.Content) + )) + .ToList(); + + var result = _dc.AgentCodes.BulkWrite(ops, new BulkWriteOptions { IsOrdered = false }); + return result.ModifiedCount > 0; } public bool BulkInsertAgentCodeScripts(string agentId, List scripts) diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs index 477778a00..6bfc90432 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs @@ -88,8 +88,8 @@ public async Task Execute(RoleDialogModel message) PythonEngine.Exec(ret.PythonCode, globals); // Get result - var result = stringIO.getvalue().ToString(); - message.Content = result; + var result = stringIO.getvalue()?.ToString() as string; + message.Content = result?.TrimEnd('\r', '\n') ?? string.Empty; message.RichContent = new RichContent { Recipient = new Recipient { Id = convService.ConversationId }, diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs index 82739f2ae..a03638eb7 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs @@ -20,8 +20,7 @@ public PyInterpretService( public string Provider => "python-interpreter"; - public async Task RunCode(string codeScript, - IEnumerable? arguments = null, CodeInterpretOptions? options = null) + public async Task RunCode(string codeScript, CodeInterpretOptions? options = null) { try { @@ -44,12 +43,12 @@ public async Task RunCode(string codeScript, } // Set arguments - if (!arguments.IsNullOrEmpty()) + if (options?.Arguments?.Any() == true) { var list = new PyList(); list.Append(new PyString("code.py")); - foreach (var arg in arguments) + foreach (var arg in options.Arguments) { if (!string.IsNullOrWhiteSpace(arg.Key) && !string.IsNullOrWhiteSpace(arg.Value)) { @@ -64,7 +63,7 @@ public async Task RunCode(string codeScript, PythonEngine.Exec(codeScript, globals); // Get result - var result = stringIO.getvalue().ToString(); + var result = stringIO.getvalue()?.ToString() as string; // Restore the original stdout/stderr sys.stdout = sys.__stdout__; @@ -72,7 +71,7 @@ public async Task RunCode(string codeScript, return new CodeInterpretResult { - Result = result, + Result = result?.TrimEnd('\r', '\n'), Success = true }; } From 24295bfe423c010e60b017a09df734b6cfd3041a Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Tue, 30 Sep 2025 21:12:34 -0500 Subject: [PATCH 19/26] minor change --- .../Repository/MongoRepository.AgentCode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs index 580497f7f..35d67355b 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs @@ -66,7 +66,7 @@ public bool UpdateAgentCodeScripts(string agentId, List scripts .ToList(); var result = _dc.AgentCodes.BulkWrite(ops, new BulkWriteOptions { IsOrdered = false }); - return result.ModifiedCount > 0; + return result.ModifiedCount > 0 || result.MatchedCount > 0; } public bool BulkInsertAgentCodeScripts(string agentId, List scripts) From 94be564f6e5f061c9bc49fc1e601f5ef8a26661f Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Tue, 30 Sep 2025 21:25:45 -0500 Subject: [PATCH 20/26] fix condition --- .../Repository/FileRepository/FileRepository.AgentCode.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs index b596b4ed1..bf11059a6 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs @@ -22,7 +22,7 @@ public List GetAgentCodeScripts(string agentId, List? s foreach (var file in Directory.GetFiles(dir)) { var fileName = Path.GetFileName(file); - if (scriptNames != null || !scriptNames.Contains(fileName)) + if (scriptNames != null && !scriptNames.Contains(fileName)) { continue; } @@ -152,8 +152,10 @@ public bool DeleteAgentCodeScripts(string agentId, List? scriptNames) } #endregion + #region Private methods private string BuildAgentCodeDir(string agentId) { return Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER); } + #endregion } From 56a8e046f1e0c2ea361e4a36e9026f46c6e961d5 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 1 Oct 2025 10:21:58 -0500 Subject: [PATCH 21/26] reset argv --- .../Functions/PyProgrammerFn.cs | 1 + .../Services/PyInterpretService.cs | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs index 6bfc90432..30d306587 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs @@ -104,6 +104,7 @@ public async Task Execute(RoleDialogModel message) // Restore the original stdout/stderr sys.stdout = sys.__stdout__; sys.stderr = sys.__stderr__; + sys.argv = new PyList(); } } catch (Exception ex) diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs index a03638eb7..b47cfba0e 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Models; using Microsoft.Extensions.Logging; using Python.Runtime; using System.Threading.Tasks; @@ -35,6 +34,8 @@ public async Task RunCode(string codeScript, CodeInterpretO sys.stdout = stringIO; sys.stderr = stringIO; + var org = sys.argv; + // Set global items using var globals = new PyDict(); if (codeScript.Contains("__main__") == true) @@ -43,9 +44,9 @@ public async Task RunCode(string codeScript, CodeInterpretO } // Set arguments + var list = new PyList(); if (options?.Arguments?.Any() == true) { - var list = new PyList(); list.Append(new PyString("code.py")); foreach (var arg in options.Arguments) @@ -56,8 +57,8 @@ public async Task RunCode(string codeScript, CodeInterpretO list.Append(new PyString($"{arg.Value}")); } } - sys.argv = list; } + sys.argv = list; // Execute Python script PythonEngine.Exec(codeScript, globals); @@ -68,6 +69,7 @@ public async Task RunCode(string codeScript, CodeInterpretO // Restore the original stdout/stderr sys.stdout = sys.__stdout__; sys.stderr = sys.__stderr__; + sys.argv = new PyList(); return new CodeInterpretResult { From 83fe09bd7f4ac83571a71e3fce62fa77f2ea1759 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 1 Oct 2025 15:52:43 -0500 Subject: [PATCH 22/26] refine --- .../Models/CodeInterpretOptions.cs | 2 +- .../Services/InstructService.Execute.cs | 102 ++++++++++++------ 2 files changed, 70 insertions(+), 34 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs index 5255c18ff..23e78a972 100644 --- a/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs +++ b/src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/Models/CodeInterpretOptions.cs @@ -2,5 +2,5 @@ namespace BotSharp.Abstraction.CodeInterpreter.Models; public class CodeInterpretOptions { - public List? Arguments { get; set; } + public IEnumerable? Arguments { get; set; } } diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 44ab141e0..aae327b93 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -8,6 +8,8 @@ namespace BotSharp.Core.Instructs; public partial class InstructService { + private const string DEFAULT_CODE_INTERPRETER = "python-interpreter"; + public async Task Execute( string agentId, RoleDialogModel message, @@ -22,7 +24,7 @@ public async Task Execute( var response = new InstructResult { MessageId = message.MessageId, - Template = codeOptions?.CodeScriptName ?? templateName + Template = templateName }; @@ -40,41 +42,13 @@ public async Task Execute( } // Run code template - if (!string.IsNullOrWhiteSpace(codeOptions?.CodeScriptName)) + var (text, isCodeComplete) = await GetCodeResponse(agentId, templateName, codeOptions); + if (isCodeComplete) { - var codeInterpreter = _services.GetServices() - .FirstOrDefault(x => x.Provider.IsEqualTo(codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs("python-interpreter"))); - - if (codeInterpreter == null) - { - var error = $"No code interpreter found. (Agent: {agentId}, Code interpreter: {codeOptions.CodeInterpretProvider})"; - _logger.LogError(error); - response.Text = error; - } - else - { - var db = _services.GetRequiredService(); - var state = _services.GetRequiredService(); - - var codeScript = db.GetAgentCodeScript(agentId, codeOptions.CodeScriptName); - if (string.IsNullOrWhiteSpace(codeScript)) - { - var error = $"Empty code script. (Agent: {agentId}, Code script: {codeOptions.CodeScriptName})"; - _logger.LogError(error); - response.Text = error; - } - else - { - var result = await codeInterpreter.RunCode(codeScript, options: new() - { - Arguments = codeOptions?.Arguments - }); - response.Text = result?.Result?.ToString(); - } - } + response.Text = text; return response; } - + // Trigger before completion hooks var hooks = _services.GetHooks(agentId); @@ -93,6 +67,7 @@ public async Task Execute( } } + var provider = string.Empty; var model = string.Empty; @@ -159,4 +134,65 @@ await hook.OnResponseGenerated(new InstructResponseModel return response; } + + private async Task<(string?, bool)> GetCodeResponse(string agentId, string templateName, CodeInstructOptions? codeOptions) + { + var state = _services.GetRequiredService(); + var db = _services.GetRequiredService(); + + var isComplete = false; + var response = string.Empty; + + var codeProvider = codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs(DEFAULT_CODE_INTERPRETER); + var codeInterpreter = _services.GetServices() + .FirstOrDefault(x => x.Provider.IsEqualTo(codeProvider)); + + if (codeInterpreter == null) + { + _logger.LogWarning($"No code interpreter found. (Agent: {agentId}, Code interpreter: {codeProvider})"); + return (response, isComplete); + } + + // Get code script name + var scriptName = string.Empty; + if (!string.IsNullOrEmpty(codeOptions?.CodeScriptName)) + { + scriptName = codeOptions.CodeScriptName; + } + else if (!string.IsNullOrEmpty(templateName)) + { + scriptName = $"{templateName}.py"; + } + + if (string.IsNullOrEmpty(scriptName)) + { + _logger.LogWarning($"Empty code script name. (Agent: {agentId}, {scriptName})"); + return (response, isComplete); + } + + // Get code script + var codeScript = db.GetAgentCodeScript(agentId, scriptName); + if (string.IsNullOrWhiteSpace(codeScript)) + { + _logger.LogWarning($"Empty code script. (Agent: {agentId}, {scriptName})"); + return (response, isComplete); + } + + // Get code arguments + var arguments = codeOptions?.Arguments ?? []; + if (arguments.IsNullOrEmpty()) + { + arguments = state.GetStates().Select(x => new KeyValue(x.Key, x.Value)).ToList(); + } + + // Run code script + var result = await codeInterpreter.RunCode(codeScript, options: new() + { + Arguments = arguments + }); + + response = result?.Result?.ToString(); + isComplete = true; + return (response, isComplete); + } } From 74d0c96e2fcc624966b2e4b3e76cd98460a94d66 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Wed, 1 Oct 2025 19:58:28 -0500 Subject: [PATCH 23/26] refine --- .../Services/InstructService.Execute.cs | 121 ++++++++++-------- .../Services/PyInterpretService.cs | 4 +- 2 files changed, 70 insertions(+), 55 deletions(-) diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index aae327b93..7eee1b556 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -8,8 +8,6 @@ namespace BotSharp.Core.Instructs; public partial class InstructService { - private const string DEFAULT_CODE_INTERPRETER = "python-interpreter"; - public async Task Execute( string agentId, RoleDialogModel message, @@ -41,16 +39,13 @@ public async Task Execute( return response; } - // Run code template - var (text, isCodeComplete) = await GetCodeResponse(agentId, templateName, codeOptions); - if (isCodeComplete) - { - response.Text = text; - return response; - } + + var provider = string.Empty; + var model = string.Empty; + var prompt = string.Empty; - // Trigger before completion hooks + // Before completion hooks var hooks = _services.GetHooks(agentId); foreach (var hook in hooks) { @@ -68,43 +63,48 @@ public async Task Execute( } - var provider = string.Empty; - var model = string.Empty; - - // Render prompt - var prompt = string.IsNullOrEmpty(templateName) ? - agentService.RenderInstruction(agent) : - agentService.RenderTemplate(agent, templateName); - - var completer = CompletionProvider.GetCompletion(_services, - agentConfig: agent.LlmConfig); - - if (completer is ITextCompletion textCompleter) + // Run code template + var (text, isCodeComplete) = await GetCodeResponse(agentId, templateName, codeOptions); + if (isCodeComplete) { - instruction = null; - provider = textCompleter.Provider; - model = textCompleter.Model; - - var result = await textCompleter.GetCompletion(prompt, agentId, message.MessageId); - response.Text = result; + response.Text = text; } - else if (completer is IChatCompletion chatCompleter) + else { - provider = chatCompleter.Provider; - model = chatCompleter.Model; + // Render prompt + prompt = string.IsNullOrEmpty(templateName) ? + agentService.RenderInstruction(agent) : + agentService.RenderTemplate(agent, templateName); + + var completer = CompletionProvider.GetCompletion(_services, + agentConfig: agent.LlmConfig); - if (instruction == "#TEMPLATE#") + if (completer is ITextCompletion textCompleter) { - instruction = prompt; - prompt = message.Content; - } + instruction = null; + provider = textCompleter.Provider; + model = textCompleter.Model; - var result = await chatCompleter.GetChatCompletions(new Agent + var result = await textCompleter.GetCompletion(prompt, agentId, message.MessageId); + response.Text = result; + } + else if (completer is IChatCompletion chatCompleter) { - Id = agentId, - Name = agent.Name, - Instruction = instruction - }, new List + provider = chatCompleter.Provider; + model = chatCompleter.Model; + + if (instruction == "#TEMPLATE#") + { + instruction = prompt; + prompt = message.Content; + } + + var result = await chatCompleter.GetChatCompletions(new Agent + { + Id = agentId, + Name = agent.Name, + Instruction = instruction + }, new List { new RoleDialogModel(AgentRole.User, prompt) { @@ -113,28 +113,39 @@ public async Task Execute( Files = files?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData, ContentType = x.ContentType }).ToList() ?? [] } }); - response.Text = result.Content; + response.Text = result.Content; + } } - + // After completion hooks foreach (var hook in hooks) { await hook.AfterCompletion(agent, response); - await hook.OnResponseGenerated(new InstructResponseModel + if (!isCodeComplete) { - AgentId = agentId, - Provider = provider, - Model = model, - TemplateName = templateName, - UserMessage = prompt, - SystemInstruction = instruction, - CompletionText = response.Text - }); + await hook.OnResponseGenerated(new InstructResponseModel + { + AgentId = agentId, + Provider = provider, + Model = model, + TemplateName = templateName, + UserMessage = prompt, + SystemInstruction = instruction, + CompletionText = response.Text + }); + } } return response; } + /// + /// Get code response => return: (response text, whether code execution is completed) + /// + /// + /// + /// + /// private async Task<(string?, bool)> GetCodeResponse(string agentId, string templateName, CodeInstructOptions? codeOptions) { var state = _services.GetRequiredService(); @@ -143,13 +154,15 @@ await hook.OnResponseGenerated(new InstructResponseModel var isComplete = false; var response = string.Empty; - var codeProvider = codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs(DEFAULT_CODE_INTERPRETER); + var codeProvider = codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs("botsharp-py-interpreter"); var codeInterpreter = _services.GetServices() .FirstOrDefault(x => x.Provider.IsEqualTo(codeProvider)); if (codeInterpreter == null) { +#if DEBUG _logger.LogWarning($"No code interpreter found. (Agent: {agentId}, Code interpreter: {codeProvider})"); +#endif return (response, isComplete); } @@ -166,7 +179,9 @@ await hook.OnResponseGenerated(new InstructResponseModel if (string.IsNullOrEmpty(scriptName)) { +#if DEBUG _logger.LogWarning($"Empty code script name. (Agent: {agentId}, {scriptName})"); +#endif return (response, isComplete); } @@ -174,7 +189,9 @@ await hook.OnResponseGenerated(new InstructResponseModel var codeScript = db.GetAgentCodeScript(agentId, scriptName); if (string.IsNullOrWhiteSpace(codeScript)) { +#if DEBUG _logger.LogWarning($"Empty code script. (Agent: {agentId}, {scriptName})"); +#endif return (response, isComplete); } diff --git a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs index b47cfba0e..740499d5c 100644 --- a/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs +++ b/src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs @@ -17,7 +17,7 @@ public PyInterpretService( _logger = logger; } - public string Provider => "python-interpreter"; + public string Provider => "botsharp-py-interpreter"; public async Task RunCode(string codeScript, CodeInterpretOptions? options = null) { @@ -34,8 +34,6 @@ public async Task RunCode(string codeScript, CodeInterpretO sys.stdout = stringIO; sys.stderr = stringIO; - var org = sys.argv; - // Set global items using var globals = new PyDict(); if (codeScript.Contains("__main__") == true) From 5be85d3f4ba0fbb1aabaf07fead6aed4aa5070f5 Mon Sep 17 00:00:00 2001 From: Jicheng Lu Date: Mon, 6 Oct 2025 01:27:18 -0500 Subject: [PATCH 24/26] refine repository for agent code script --- .../Agents/Enums/AgentCodeScriptType.cs | 7 + .../Agents/Enums/AgentFuncVisMode.cs | 2 +- .../Agents/Enums/AgentRole.cs | 2 +- .../Agents/Enums/AgentType.cs | 2 +- .../Agents/Enums/BuiltInAgentId.cs | 2 +- .../Agents/Models/AgentCodeScript.cs | 23 ++- .../Enums/ConversationChannel.cs | 2 +- .../Conversations/Enums/ConversationStatus.cs | 2 +- .../Conversations/Enums/StateDataType.cs | 2 +- .../Conversations/Enums/StateSource.cs | 2 +- .../Infrastructures/Enums/LanguageType.cs | 2 +- .../Infrastructures/Enums/StateConst.cs | 2 +- .../Instructs/IInstructHook.cs | 9 +- .../Instructs/Models/CodeInstructContext.cs | 6 + .../Filters/AgentCodeScriptFilter.cs | 12 ++ .../Repositories/IBotSharpRepository.cs | 6 +- .../Routing/Enums/RoutingMode.cs | 2 +- .../Routing/Enums/RuleType.cs | 2 +- .../Tasks/Enums/TaskStatus.cs | 2 +- .../Services/AgentService.CreateAgent.cs | 20 +- .../Services/InstructService.Execute.cs | 180 +++++++++++------- .../FileRepository.AgentCode.cs | 161 ---------------- .../FileRepository.AgentCodeScript.cs | 157 +++++++++++++++ .../Hooks/InstructionLogHook.cs | 5 +- ...Document.cs => AgentCodeScriptDocument.cs} | 15 +- .../MongoDbContext.cs | 4 +- .../Repository/MongoRepository.Agent.cs | 6 +- .../Repository/MongoRepository.AgentCode.cs | 113 ----------- .../MongoRepository.AgentCodeScript.cs | 129 +++++++++++++ 29 files changed, 494 insertions(+), 385 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentCodeScriptType.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentCodeScriptFilter.cs delete mode 100644 src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs create mode 100644 src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs rename src/Plugins/BotSharp.Plugin.MongoStorage/Collections/{AgentCodeDocument.cs => AgentCodeScriptDocument.cs} (54%) delete mode 100644 src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs create mode 100644 src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentCodeScriptType.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentCodeScriptType.cs new file mode 100644 index 000000000..4bf5f262c --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentCodeScriptType.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Agents.Enums; + +public static class AgentCodeScriptType +{ + public const string Src = "src"; + public const string Test = "test"; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentFuncVisMode.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentFuncVisMode.cs index 212248153..713237837 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentFuncVisMode.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentFuncVisMode.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Agents.Enums; -public class AgentFuncVisMode +public static class AgentFuncVisMode { public const string Manual = "manual"; public const string Auto = "auto"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs index 226313e09..2975e44f1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentRole.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Agents.Enums; -public class AgentRole +public static class AgentRole { public const string System = "system"; public const string Assistant = "assistant"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs index 455e87ff5..3b9767845 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/AgentType.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Agents.Enums; -public class AgentType +public static class AgentType { /// /// Routing agent diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs index 96aa47540..82b0efabe 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Enums/BuiltInAgentId.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Agents.Enums; -public class BuiltInAgentId +public static class BuiltInAgentId { /// /// A routing agent can be used as a base router. diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs index 9782f8484..840d67210 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs @@ -1,18 +1,29 @@ namespace BotSharp.Abstraction.Agents.Models; -public class AgentCodeScript +public class AgentCodeScript : AgentCodeScriptBase { public string Id { get; set; } - public string AgentId { get; set; } - public string Name { get; set; } - public string Content { get; set; } + public string AgentId { get; set; } = null!; - public AgentCodeScript() + public AgentCodeScript() : base() { } public override string ToString() { - return Name; + return $"{CodePath}"; } } + +public class AgentCodeScriptBase +{ + public string Name { get; set; } = null!; + public string Content { get; set; } = null!; + + /// + /// Code script type: src, test + /// + public string ScriptType { get; set; } = null!; + + public string CodePath => $"{ScriptType}/{Name}"; +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationChannel.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationChannel.cs index 1843a1bcc..d569da596 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationChannel.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationChannel.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Conversations.Enums; -public class ConversationChannel +public static class ConversationChannel { public const string WebChat = "webchat"; public const string OpenAPI = "openapi"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationStatus.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationStatus.cs index dc8a53b4b..b82e094b2 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationStatus.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/ConversationStatus.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Conversations.Enums; -public class ConversationStatus +public static class ConversationStatus { public const string Open = "open"; public const string Closed = "closed"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs index 20d635f47..9f80a9e8c 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateDataType.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Conversations.Enums; -public class StateDataType +public static class StateDataType { public const string String = "string"; public const string Boolean = "boolean"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs index 31f32e162..cff798025 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/Enums/StateSource.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Conversations.Enums; -public class StateSource +public static class StateSource { public const string External = "external"; public const string Application = "application"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs index 64351e459..9c5dd827a 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/LanguageType.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Infrastructures.Enums; -public class LanguageType +public static class LanguageType { public const string UNKNOWN = "Unknown"; public const string ENGLISH = "English"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs index 038699ac5..4a048b02e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Infrastructures/Enums/StateConst.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Infrastructures.Enums; -public class StateConst +public static class StateConst { public const string EXPECTED_ACTION_AGENT = "expected_next_action_agent"; public const string EXPECTED_GOAL_AGENT = "expected_user_goal_agent"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs index 23b8bf60b..a7e3c1dfd 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/IInstructHook.cs @@ -5,7 +5,10 @@ namespace BotSharp.Abstraction.Instructs; public interface IInstructHook : IHookBase { - Task BeforeCompletion(Agent agent, RoleDialogModel message); - Task AfterCompletion(Agent agent, InstructResult result); - Task OnResponseGenerated(InstructResponseModel response); + Task BeforeCompletion(Agent agent, RoleDialogModel message) => Task.CompletedTask; + Task AfterCompletion(Agent agent, InstructResult result) => Task.CompletedTask; + Task OnResponseGenerated(InstructResponseModel response) => Task.CompletedTask; + + Task BeforeCodeExecution(Agent agent, RoleDialogModel message, CodeInstructContext context) => Task.CompletedTask; + Task AfterCodeExecution(Agent agent, InstructResult result) => Task.CompletedTask; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs new file mode 100644 index 000000000..0cb3910be --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.Instructs.Models; + +public class CodeInstructContext +{ + public List Arguments { get; set; } = []; +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentCodeScriptFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentCodeScriptFilter.cs new file mode 100644 index 000000000..530bb4aa5 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/AgentCodeScriptFilter.cs @@ -0,0 +1,12 @@ +namespace BotSharp.Abstraction.Repositories.Filters; + +public class AgentCodeScriptFilter +{ + public List? ScriptNames { get; set; } + public List? ScriptTypes { get; set; } + + public static AgentCodeScriptFilter Empty() + { + return new AgentCodeScriptFilter(); + } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 85f75e52a..f2ebe6a8f 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -107,15 +107,15 @@ bool DeleteAgentTasks(string agentId, List? taskIds = null) #endregion #region Agent Code - List GetAgentCodeScripts(string agentId, List? scriptNames = null) + List GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null) => throw new NotImplementedException(); - string? GetAgentCodeScript(string agentId, string scriptName) + string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) => throw new NotImplementedException(); bool UpdateAgentCodeScripts(string agentId, List scripts) => throw new NotImplementedException(); bool BulkInsertAgentCodeScripts(string agentId, List scripts) => throw new NotImplementedException(); - bool DeleteAgentCodeScripts(string agentId, List? scriptNames) + bool DeleteAgentCodeScripts(string agentId, List? scripts = null) => throw new NotImplementedException(); #endregion diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs index 8f946d01e..bc63066f5 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RoutingMode.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Routing.Enums; -public class RoutingMode +public static class RoutingMode { public const string Eager = "eager"; public const string Lazy = "lazy"; diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs index c595d59c6..dc74b6ca8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Enums/RuleType.cs @@ -1,6 +1,6 @@ namespace BotSharp.Abstraction.Routing.Enums; -public class RuleType +public static class RuleType { /// /// Fallback to redirect agent diff --git a/src/Infrastructure/BotSharp.Abstraction/Tasks/Enums/TaskStatus.cs b/src/Infrastructure/BotSharp.Abstraction/Tasks/Enums/TaskStatus.cs index f36d00a0e..48d31c6e6 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Tasks/Enums/TaskStatus.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Tasks/Enums/TaskStatus.cs @@ -1,7 +1,7 @@ /// /// Agent task status /// -public class TaskStatus +public static class TaskStatus { public const string Scheduled = "scheduled"; public const string New = "new"; diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index cc0a92bc7..d61f0f3dd 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -229,12 +229,22 @@ private List GetCodeScriptsFromFile(string fileDir) } var agentId = fileDir.Split(Path.DirectorySeparatorChar).Last(); - scripts = Directory.GetFiles(codeDir).Select(file => new AgentCodeScript + + foreach (var folder in Directory.EnumerateDirectories(codeDir)) { - AgentId = agentId, - Name = Path.GetFileName(file), - Content = File.ReadAllText(file) - }).ToList(); + var scriptType = folder.Split(Path.DirectorySeparatorChar).Last(); + foreach (var file in Directory.EnumerateFiles(folder)) + { + scripts.Add(new AgentCodeScript + { + AgentId = agentId, + Name = Path.GetFileName(file), + ScriptType = scriptType, + Content = File.ReadAllText(file) + }); + } + } + return scripts; } } diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 7eee1b556..70a2257b5 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -40,9 +40,12 @@ public async Task Execute( } - var provider = string.Empty; - var model = string.Empty; - var prompt = string.Empty; + // Run code template + var codeResponse = await GetCodeResponse(agent, message, templateName, codeOptions); + if (codeResponse != null) + { + return codeResponse; + } // Before completion hooks @@ -63,48 +66,43 @@ public async Task Execute( } - // Run code template - var (text, isCodeComplete) = await GetCodeResponse(agentId, templateName, codeOptions); - if (isCodeComplete) + var provider = string.Empty; + var model = string.Empty; + + // Render prompt + var prompt = string.IsNullOrEmpty(templateName) ? + agentService.RenderInstruction(agent) : + agentService.RenderTemplate(agent, templateName); + + var completer = CompletionProvider.GetCompletion(_services, + agentConfig: agent.LlmConfig); + + if (completer is ITextCompletion textCompleter) { - response.Text = text; + instruction = null; + provider = textCompleter.Provider; + model = textCompleter.Model; + + var result = await textCompleter.GetCompletion(prompt, agentId, message.MessageId); + response.Text = result; } - else + else if (completer is IChatCompletion chatCompleter) { - // Render prompt - prompt = string.IsNullOrEmpty(templateName) ? - agentService.RenderInstruction(agent) : - agentService.RenderTemplate(agent, templateName); - - var completer = CompletionProvider.GetCompletion(_services, - agentConfig: agent.LlmConfig); + provider = chatCompleter.Provider; + model = chatCompleter.Model; - if (completer is ITextCompletion textCompleter) + if (instruction == "#TEMPLATE#") { - instruction = null; - provider = textCompleter.Provider; - model = textCompleter.Model; - - var result = await textCompleter.GetCompletion(prompt, agentId, message.MessageId); - response.Text = result; + instruction = prompt; + prompt = message.Content; } - else if (completer is IChatCompletion chatCompleter) - { - provider = chatCompleter.Provider; - model = chatCompleter.Model; - if (instruction == "#TEMPLATE#") - { - instruction = prompt; - prompt = message.Content; - } - - var result = await chatCompleter.GetChatCompletions(new Agent - { - Id = agentId, - Name = agent.Name, - Instruction = instruction - }, new List + var result = await chatCompleter.GetChatCompletions(new Agent + { + Id = agentId, + Name = agent.Name, + Instruction = instruction + }, new List { new RoleDialogModel(AgentRole.User, prompt) { @@ -113,46 +111,49 @@ public async Task Execute( Files = files?.Select(x => new BotSharpFile { FileUrl = x.FileUrl, FileData = x.FileData, ContentType = x.ContentType }).ToList() ?? [] } }); - response.Text = result.Content; - } + response.Text = result.Content; } // After completion hooks foreach (var hook in hooks) { await hook.AfterCompletion(agent, response); - if (!isCodeComplete) + await hook.OnResponseGenerated(new InstructResponseModel { - await hook.OnResponseGenerated(new InstructResponseModel - { - AgentId = agentId, - Provider = provider, - Model = model, - TemplateName = templateName, - UserMessage = prompt, - SystemInstruction = instruction, - CompletionText = response.Text - }); - } + AgentId = agentId, + Provider = provider, + Model = model, + TemplateName = templateName, + UserMessage = prompt, + SystemInstruction = instruction, + CompletionText = response.Text + }); } return response; } /// - /// Get code response => return: (response text, whether code execution is completed) + /// Get code response /// - /// + /// + /// /// /// /// - private async Task<(string?, bool)> GetCodeResponse(string agentId, string templateName, CodeInstructOptions? codeOptions) + private async Task GetCodeResponse(Agent agent, RoleDialogModel message, string templateName, CodeInstructOptions? codeOptions) { + InstructResult? response = null; + + if (agent == null) + { + return response; + } + + var state = _services.GetRequiredService(); var db = _services.GetRequiredService(); - - var isComplete = false; - var response = string.Empty; + var hooks = _services.GetHooks(agent.Id); var codeProvider = codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs("botsharp-py-interpreter"); var codeInterpreter = _services.GetServices() @@ -161,9 +162,9 @@ await hook.OnResponseGenerated(new InstructResponseModel if (codeInterpreter == null) { #if DEBUG - _logger.LogWarning($"No code interpreter found. (Agent: {agentId}, Code interpreter: {codeProvider})"); + _logger.LogWarning($"No code interpreter found. (Agent: {agent.Id}, Code interpreter: {codeProvider})"); #endif - return (response, isComplete); + return response; } // Get code script name @@ -180,19 +181,19 @@ await hook.OnResponseGenerated(new InstructResponseModel if (string.IsNullOrEmpty(scriptName)) { #if DEBUG - _logger.LogWarning($"Empty code script name. (Agent: {agentId}, {scriptName})"); + _logger.LogWarning($"Empty code script name. (Agent: {agent.Id}, {scriptName})"); #endif - return (response, isComplete); + return response; } // Get code script - var codeScript = db.GetAgentCodeScript(agentId, scriptName); + var codeScript = db.GetAgentCodeScript(agent.Id, scriptName, scriptType: AgentCodeScriptType.Src); if (string.IsNullOrWhiteSpace(codeScript)) { #if DEBUG - _logger.LogWarning($"Empty code script. (Agent: {agentId}, {scriptName})"); + _logger.LogWarning($"Empty code script. (Agent: {agent.Id}, {scriptName})"); #endif - return (response, isComplete); + return response; } // Get code arguments @@ -202,14 +203,55 @@ await hook.OnResponseGenerated(new InstructResponseModel arguments = state.GetStates().Select(x => new KeyValue(x.Key, x.Value)).ToList(); } + var context = new CodeInstructContext + { + Arguments = arguments + }; + + // Before code execution + foreach (var hook in hooks) + { + await hook.BeforeCodeExecution(agent, message, context); + + // Interrupted by hook + if (message.StopCompletion) + { + return new InstructResult + { + MessageId = message.MessageId, + Text = message.Content + }; + } + } + // Run code script var result = await codeInterpreter.RunCode(codeScript, options: new() { - Arguments = arguments + Arguments = context.Arguments }); - response = result?.Result?.ToString(); - isComplete = true; - return (response, isComplete); + response = new InstructResult + { + MessageId = message.MessageId, + Text = result?.Result?.ToString() + }; + + // After code execution + foreach (var hook in hooks) + { + await hook.AfterCodeExecution(agent, response); + await hook.OnResponseGenerated(new InstructResponseModel + { + AgentId = agent.Id, + Provider = codeInterpreter.Provider, + Model = string.Empty, + TemplateName = scriptName, + UserMessage = string.Empty, + SystemInstruction = string.Empty, + CompletionText = response.Text + }); + } + + return response; } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs deleted file mode 100644 index bf11059a6..000000000 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System.IO; - -namespace BotSharp.Core.Repository; - -public partial class FileRepository -{ - #region Code - public List GetAgentCodeScripts(string agentId, List? scriptNames = null) - { - if (string.IsNullOrWhiteSpace(agentId)) - { - return []; - } - - var dir = BuildAgentCodeDir(agentId); - if (!Directory.Exists(dir)) - { - return []; - } - - var results = new List(); - foreach (var file in Directory.GetFiles(dir)) - { - var fileName = Path.GetFileName(file); - if (scriptNames != null && !scriptNames.Contains(fileName)) - { - continue; - } - - var script = new AgentCodeScript - { - AgentId = agentId, - Name = fileName, - Content = File.ReadAllText(file) - }; - results.Add(script); - } - return results; - } - - public string? GetAgentCodeScript(string agentId, string scriptName) - { - if (string.IsNullOrWhiteSpace(agentId) - || string.IsNullOrWhiteSpace(scriptName)) - { - return null; - } - - var dir = BuildAgentCodeDir(agentId); - if (!Directory.Exists(dir)) - { - return null; - } - - foreach (var file in Directory.GetFiles(dir)) - { - var fileName = Path.GetFileName(file); - if (scriptName.IsEqualTo(fileName)) - { - return File.ReadAllText(file); - } - } - return string.Empty; - } - - public bool UpdateAgentCodeScripts(string agentId, List scripts) - { - if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) - { - return false; - } - - var dir = BuildAgentCodeDir(agentId); - if (!Directory.Exists(dir)) - { - return false; - } - - var dict = scripts.DistinctBy(x => x.Name).ToDictionary(x => x.Name, x => x); - var files = Directory.GetFiles(dir).Where(x => dict.Keys.Contains(Path.GetFileName(x))).ToList(); - - foreach (var file in files) - { - if (dict.TryGetValue(Path.GetFileName(file), out var script)) - { - File.WriteAllText(file, script.Content); - } - } - - return true; - } - - public bool BulkInsertAgentCodeScripts(string agentId, List scripts) - { - if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) - { - return false; - } - - var dir = BuildAgentCodeDir(agentId); - if (!Directory.Exists(dir)) - { - return false; - } - - foreach (var script in scripts) - { - if (string.IsNullOrWhiteSpace(script.Name)) - { - continue; - } - - var path = Path.Combine(dir, script.Name); - File.WriteAllText(path, script.Content); - } - - return true; - } - - public bool DeleteAgentCodeScripts(string agentId, List? scriptNames) - { - if (string.IsNullOrWhiteSpace(agentId)) - { - return false; - } - - var dir = BuildAgentCodeDir(agentId); - if (!Directory.Exists(dir)) - { - return false; - } - - if (scriptNames == null) - { - Directory.Delete(dir, true); - return true; - } - else if (!scriptNames.Any()) - { - return false; - } - - foreach (var file in Directory.GetFiles(dir)) - { - var fileName = Path.GetFileName(file); - if (scriptNames.Contains(fileName)) - { - File.Delete(file); - } - } - return true; - } - #endregion - - #region Private methods - private string BuildAgentCodeDir(string agentId) - { - return Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER); - } - #endregion -} diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs new file mode 100644 index 000000000..7482f3585 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs @@ -0,0 +1,157 @@ +using System.IO; + +namespace BotSharp.Core.Repository; + +public partial class FileRepository +{ + #region Code + public List GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return []; + } + + var dir = BuildAgentCodeScriptDir(agentId); + if (!Directory.Exists(dir)) + { + return []; + } + + filter ??= AgentCodeScriptFilter.Empty(); + var results = new List(); + + foreach (var folder in Directory.EnumerateDirectories(dir)) + { + var scriptType = folder.Split(Path.DirectorySeparatorChar).Last(); + if (filter.ScriptTypes != null && !filter.ScriptTypes.Contains(scriptType)) + { + continue; + } + + foreach (var file in Directory.EnumerateFiles(folder)) + { + var fileName = Path.GetFileName(file); + if (filter.ScriptNames != null && !filter.ScriptNames.Contains(fileName)) + { + continue; + } + + results.Add(new AgentCodeScript + { + AgentId = agentId, + Name = fileName, + ScriptType = scriptType, + Content = File.ReadAllText(file) + }); + } + } + + return results; + } + + public string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) + { + if (string.IsNullOrWhiteSpace(agentId) + || string.IsNullOrWhiteSpace(scriptName) + || string.IsNullOrWhiteSpace(scriptType)) + { + return null; + } + + var dir = BuildAgentCodeScriptDir(agentId, scriptType); + if (!Directory.Exists(dir)) + { + return null; + } + + var foundFile = Directory.GetFiles(dir).FirstOrDefault(file => scriptName.IsEqualTo(Path.GetFileName(file))); + if (!string.IsNullOrEmpty(foundFile)) + { + return File.ReadAllText(foundFile); + } + return string.Empty; + } + + public bool UpdateAgentCodeScripts(string agentId, List scripts) + { + if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) + { + return false; + } + + foreach (var script in scripts) + { + if (string.IsNullOrWhiteSpace(script.Name) + || string.IsNullOrWhiteSpace(script.ScriptType)) + { + continue; + } + + var dir = BuildAgentCodeScriptDir(agentId, script.ScriptType); + if (!Directory.Exists(dir)) + { + continue; + } + + var file = Path.Combine(dir, script.Name); + File.WriteAllText(file, script.Content); + } + + return true; + } + + public bool BulkInsertAgentCodeScripts(string agentId, List scripts) + { + return UpdateAgentCodeScripts(agentId, scripts); + } + + public bool DeleteAgentCodeScripts(string agentId, List? scripts = null) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return false; + } + + var dir = BuildAgentCodeScriptDir(agentId); + if (!Directory.Exists(dir)) + { + return false; + } + + if (scripts == null) + { + Directory.Delete(dir, true); + return true; + } + else if (!scripts.Any()) + { + return false; + } + + var dict = scripts.DistinctBy(x => x.CodePath).ToDictionary(x => x.CodePath, x => x); + foreach (var pair in dict) + { + var file = Path.Combine(dir, pair.Value.ScriptType, pair.Value.Name); + if (File.Exists(file)) + { + File.Delete(file); + } + } + + return true; + } + #endregion + + #region Private methods + private string BuildAgentCodeScriptDir(string agentId, string? scirptType = null) + { + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId, AGENT_CODES_FOLDER); + if (!string.IsNullOrWhiteSpace(scirptType)) + { + dir = Path.Combine(dir, scirptType); + } + return dir; + } + #endregion +} diff --git a/src/Infrastructure/BotSharp.Logger/Hooks/InstructionLogHook.cs b/src/Infrastructure/BotSharp.Logger/Hooks/InstructionLogHook.cs index 8de3db703..c95af0c1d 100644 --- a/src/Infrastructure/BotSharp.Logger/Hooks/InstructionLogHook.cs +++ b/src/Infrastructure/BotSharp.Logger/Hooks/InstructionLogHook.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Instructs.Settings; using BotSharp.Abstraction.Loggers.Models; using BotSharp.Abstraction.Users; +using BotSharp.Abstraction.Utilities; namespace BotSharp.Logger.Hooks; @@ -39,7 +40,9 @@ public override async Task OnResponseGenerated(InstructResponseModel response) var state = _services.GetRequiredService(); var user = db.GetUserById(_user.Id); - var templateName = response.TemplateName ?? state.GetState("instruct_template_name") ?? null; + var templateName = response.TemplateName + .IfNullOrEmptyAs(state.GetState("instruct_template_name")) + .IfNullOrEmptyAs(null); db.SaveInstructionLogs(new List { diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs similarity index 54% rename from src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeDocument.cs rename to src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs index f37db77d5..7b877fda9 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs @@ -2,31 +2,34 @@ namespace BotSharp.Plugin.MongoStorage.Collections; -public class AgentCodeDocument : MongoBase +public class AgentCodeScriptDocument : MongoBase { public string AgentId { get; set; } = default!; public string Name { get; set; } = default!; public string Content { get; set; } = default!; + public string ScriptType { get; set; } = default!; - public static AgentCodeDocument ToMongoModel(AgentCodeScript script) + public static AgentCodeScriptDocument ToMongoModel(AgentCodeScript script) { - return new AgentCodeDocument + return new AgentCodeScriptDocument { Id = script.Id, AgentId = script.AgentId, Name = script.Name, - Content = script.Content + Content = script.Content, + ScriptType = script.ScriptType }; } - public static AgentCodeScript ToDomainModel(AgentCodeDocument script) + public static AgentCodeScript ToDomainModel(AgentCodeScriptDocument script) { return new AgentCodeScript { Id = script.Id, AgentId = script.AgentId, Name = script.Name, - Content = script.Content + Content = script.Content, + ScriptType = script.ScriptType }; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs index 6f171cb30..a0d0ccff6 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs @@ -160,8 +160,8 @@ public IMongoCollection Agents public IMongoCollection AgentTasks => CreateAgentTaskIndex(); - public IMongoCollection AgentCodes - => GetCollectionOrCreate("AgentCodes"); + public IMongoCollection AgentCodeScripts + => GetCollectionOrCreate("AgentCodeScripts"); public IMongoCollection Conversations => CreateConversationIndex(); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 3d085c70c..76cadd324 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -594,7 +594,7 @@ public bool DeleteAgents() _dc.UserAgents.DeleteMany(Builders.Filter.Empty); _dc.RoleAgents.DeleteMany(Builders.Filter.Empty); _dc.AgentTasks.DeleteMany(Builders.Filter.Empty); - _dc.AgentCodes.DeleteMany(Builders.Filter.Empty); + _dc.AgentCodeScripts.DeleteMany(Builders.Filter.Empty); _dc.Agents.DeleteMany(Builders.Filter.Empty); return true; } @@ -614,12 +614,12 @@ public bool DeleteAgent(string agentId) var userAgentFilter = Builders.Filter.Eq(x => x.AgentId, agentId); var roleAgentFilter = Builders.Filter.Eq(x => x.AgentId, agentId); var agentTaskFilter = Builders.Filter.Eq(x => x.AgentId, agentId); - var agentCodeFilter = Builders.Filter.Eq(x => x.AgentId, agentId); + var agentCodeFilter = Builders.Filter.Eq(x => x.AgentId, agentId); _dc.UserAgents.DeleteMany(userAgentFilter); _dc.RoleAgents.DeleteMany(roleAgentFilter); _dc.AgentTasks.DeleteMany(agentTaskFilter); - _dc.AgentCodes.DeleteMany(agentCodeFilter); + _dc.AgentCodeScripts.DeleteMany(agentCodeFilter); _dc.Agents.DeleteOne(agentFilter); return true; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs deleted file mode 100644 index 35d67355b..000000000 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs +++ /dev/null @@ -1,113 +0,0 @@ -using BotSharp.Abstraction.Agents.Models; - -namespace BotSharp.Plugin.MongoStorage.Repository; - -public partial class MongoRepository -{ - #region Code - public List GetAgentCodeScripts(string agentId, List? scriptNames = null) - { - if (string.IsNullOrWhiteSpace(agentId)) - { - return []; - } - - var builder = Builders.Filter; - var filters = new List>() - { - builder.Eq(x => x.AgentId, agentId) - }; - - if (!scriptNames.IsNullOrEmpty()) - { - filters.Add(builder.In(x => x.Name, scriptNames)); - } - - var found = _dc.AgentCodes.Find(builder.And(filters)).ToList(); - return found.Select(x => AgentCodeDocument.ToDomainModel(x)).ToList(); - } - - public string? GetAgentCodeScript(string agentId, string scriptName) - { - if (string.IsNullOrWhiteSpace(agentId) - || string.IsNullOrWhiteSpace(scriptName)) - { - return null; - } - - var builder = Builders.Filter; - var filters = new List>() - { - builder.Eq(x => x.AgentId, agentId), - builder.Eq(x => x.Name, scriptName) - }; - - var found = _dc.AgentCodes.Find(builder.And(filters)).FirstOrDefault(); - return found?.Content; - } - - public bool UpdateAgentCodeScripts(string agentId, List scripts) - { - if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) - { - return false; - } - - var builder = Builders.Filter; - var ops = scripts.Where(x => !string.IsNullOrWhiteSpace(x.Name)) - .Select(x => new UpdateOneModel( - builder.And(new List> - { - builder.Eq(y => y.AgentId, agentId), - builder.Eq(y => y.Name, x.Name) - }), - Builders.Update.Set(y => y.Content, x.Content) - )) - .ToList(); - - var result = _dc.AgentCodes.BulkWrite(ops, new BulkWriteOptions { IsOrdered = false }); - return result.ModifiedCount > 0 || result.MatchedCount > 0; - } - - public bool BulkInsertAgentCodeScripts(string agentId, List scripts) - { - if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) - { - return false; - } - - var docs = scripts.Select(x => - { - var script = AgentCodeDocument.ToMongoModel(x); - script.AgentId = agentId; - script.Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(); - return script; - }).ToList(); - - _dc.AgentCodes.InsertMany(docs); - return true; - } - - public bool DeleteAgentCodeScripts(string agentId, List? scriptNames) - { - if (string.IsNullOrWhiteSpace(agentId)) - { - return false; - } - - var filterDef = Builders.Filter.Empty; - if (scriptNames != null) - { - var builder = Builders.Filter; - var filters = new List> - { - builder.In(x => x.Name, scriptNames) - }; - filterDef = builder.And(filters); - } - - var deleted = _dc.AgentCodes.DeleteMany(filterDef); - return deleted.DeletedCount > 0; - } - #endregion -} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs new file mode 100644 index 000000000..8e81b2934 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs @@ -0,0 +1,129 @@ +using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Repositories.Filters; + +namespace BotSharp.Plugin.MongoStorage.Repository; + +public partial class MongoRepository +{ + #region Code + public List GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return []; + } + + filter ??= AgentCodeScriptFilter.Empty(); + + var builder = Builders.Filter; + var filters = new List>() + { + builder.Eq(x => x.AgentId, agentId) + }; + + if (!filter.ScriptNames.IsNullOrEmpty()) + { + filters.Add(builder.In(x => x.Name, filter.ScriptNames)); + } + if (!filter.ScriptTypes.IsNullOrEmpty()) + { + filters.Add(builder.In(x => x.ScriptType, filter.ScriptTypes)); + } + + var found = _dc.AgentCodeScripts.Find(builder.And(filters)).ToList(); + return found.Select(x => AgentCodeScriptDocument.ToDomainModel(x)).ToList(); + } + + public string? GetAgentCodeScript(string agentId, string scriptName, string scriptType = AgentCodeScriptType.Src) + { + if (string.IsNullOrWhiteSpace(agentId) + || string.IsNullOrWhiteSpace(scriptName) + || string.IsNullOrWhiteSpace(scriptType)) + { + return null; + } + + var builder = Builders.Filter; + var filters = new List>() + { + builder.Eq(x => x.AgentId, agentId), + builder.Eq(x => x.Name, scriptName), + builder.Eq(x => x.ScriptType, scriptType) + }; + + var found = _dc.AgentCodeScripts.Find(builder.And(filters)).FirstOrDefault(); + return found?.Content; + } + + public bool UpdateAgentCodeScripts(string agentId, List scripts) + { + if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) + { + return false; + } + + var builder = Builders.Filter; + var ops = scripts.Where(x => !string.IsNullOrWhiteSpace(x.Name)) + .Select(x => new UpdateOneModel( + builder.And(new List> + { + builder.Eq(y => y.AgentId, agentId), + builder.Eq(y => y.Name, x.Name), + builder.Eq(y => y.ScriptType, x.ScriptType) + }), + Builders.Update.Set(y => y.Content, x.Content) + )) + .ToList(); + + var result = _dc.AgentCodeScripts.BulkWrite(ops, new BulkWriteOptions { IsOrdered = false }); + return result.ModifiedCount > 0 || result.MatchedCount > 0; + } + + public bool BulkInsertAgentCodeScripts(string agentId, List scripts) + { + if (string.IsNullOrWhiteSpace(agentId) || scripts.IsNullOrEmpty()) + { + return false; + } + + var docs = scripts.Select(x => + { + var script = AgentCodeScriptDocument.ToMongoModel(x); + script.AgentId = agentId; + script.Id = x.Id.IfNullOrEmptyAs(Guid.NewGuid().ToString()); + return script; + }).ToList(); + + _dc.AgentCodeScripts.InsertMany(docs); + return true; + } + + public bool DeleteAgentCodeScripts(string agentId, List? scripts = null) + { + if (string.IsNullOrWhiteSpace(agentId)) + { + return false; + } + + DeleteResult deleted; + if (scripts != null) + { + var scriptPaths = scripts.Select(x => x.CodePath); + var exprFilter = new BsonDocument("$expr", new BsonDocument("$in", new BsonArray + { + new BsonDocument("$concat", new BsonArray { "$ScriptType", "/", "$Name" }), + new BsonArray(scriptPaths) + })); + + var filterDef = new BsonDocumentFilterDefinition(exprFilter); + deleted = _dc.AgentCodeScripts.DeleteMany(filterDef); + } + else + { + deleted = _dc.AgentCodeScripts.DeleteMany(Builders.Filter.Empty); + } + + return deleted.DeletedCount > 0; + } + #endregion +} From babb8795b33fc1054d084af9f737d415f603e790 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 6 Oct 2025 10:50:20 -0500 Subject: [PATCH 25/26] add demo script --- .../BotSharp.Core/BotSharp.Core.csproj | 3 ++- .../Instructs/Services/InstructService.Execute.cs | 1 + .../FileRepository/FileRepository.AgentCodeScript.cs | 2 +- .../codes/src/demo.py | 12 ++++++++++++ .../Collections/AgentCodeScriptDocument.cs | 2 ++ .../Repository/MongoRepository.AgentCodeScript.cs | 5 ++++- 6 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/codes/src/demo.py diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 7b0e55ac0..d960ca184 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -80,6 +80,7 @@ + @@ -126,7 +127,7 @@ PreserveNewest - + PreserveNewest diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 70a2257b5..7be841f52 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -233,6 +233,7 @@ await hook.OnResponseGenerated(new InstructResponseModel response = new InstructResult { MessageId = message.MessageId, + Template = scriptName, Text = result?.Result?.ToString() }; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs index 7482f3585..764287ddf 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs @@ -4,7 +4,7 @@ namespace BotSharp.Core.Repository; public partial class FileRepository { - #region Code + #region Code script public List GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null) { if (string.IsNullOrWhiteSpace(agentId)) diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/codes/src/demo.py b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/codes/src/demo.py new file mode 100644 index 000000000..438a6a87f --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/codes/src/demo.py @@ -0,0 +1,12 @@ +import argparse + +def main(): + parser = argparse.ArgumentParser(description="Receive named arguments") + parser.add_argument("--first_name", required=True, help="The first name") + parser.add_argument("--last_name", required=True, help="The last name") + + args = parser.parse_args() + print(f"Hello, {args.first_name} {args.last_name}!") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs index 7b877fda9..9a39fb58c 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCodeScriptDocument.cs @@ -8,6 +8,8 @@ public class AgentCodeScriptDocument : MongoBase public string Name { get; set; } = default!; public string Content { get; set; } = default!; public string ScriptType { get; set; } = default!; + public DateTime CreatedTime { get; set; } + public DateTime UpdatedTime { get; set; } public static AgentCodeScriptDocument ToMongoModel(AgentCodeScript script) { diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs index 8e81b2934..3d551b7d0 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs @@ -5,7 +5,7 @@ namespace BotSharp.Plugin.MongoStorage.Repository; public partial class MongoRepository { - #region Code + #region Code script public List GetAgentCodeScripts(string agentId, AgentCodeScriptFilter? filter = null) { if (string.IsNullOrWhiteSpace(agentId)) @@ -72,6 +72,7 @@ public bool UpdateAgentCodeScripts(string agentId, List scripts builder.Eq(y => y.ScriptType, x.ScriptType) }), Builders.Update.Set(y => y.Content, x.Content) + .Set(x => x.UpdatedTime, DateTime.UtcNow) )) .ToList(); @@ -91,6 +92,8 @@ public bool BulkInsertAgentCodeScripts(string agentId, List scr var script = AgentCodeScriptDocument.ToMongoModel(x); script.AgentId = agentId; script.Id = x.Id.IfNullOrEmptyAs(Guid.NewGuid().ToString()); + script.CreatedTime = DateTime.UtcNow; + script.UpdatedTime = DateTime.UtcNow; return script; }).ToList(); From 6a0153db28b12a17abaf8ebd06f694b7fb81ba86 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 6 Oct 2025 10:57:18 -0500 Subject: [PATCH 26/26] add code script to context --- .../Instructs/Models/CodeInstructContext.cs | 1 + .../Instructs/Services/InstructService.Execute.cs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs index 0cb3910be..2be0368f1 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Instructs/Models/CodeInstructContext.cs @@ -2,5 +2,6 @@ namespace BotSharp.Abstraction.Instructs.Models; public class CodeInstructContext { + public string CodeScript { get; set; } public List Arguments { get; set; } = []; } diff --git a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs index 7be841f52..11ff7518c 100644 --- a/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs +++ b/src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs @@ -205,6 +205,7 @@ await hook.OnResponseGenerated(new InstructResponseModel var context = new CodeInstructContext { + CodeScript = codeScript, Arguments = arguments }; @@ -225,7 +226,7 @@ await hook.OnResponseGenerated(new InstructResponseModel } // Run code script - var result = await codeInterpreter.RunCode(codeScript, options: new() + var result = await codeInterpreter.RunCode(context.CodeScript, options: new() { Arguments = context.Arguments });