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 5a8f7552..00000000
--- 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 e6ea6cf3..00000000
--- 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 a3fa3965..9c34de59 100644
--- a/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
+++ b/src/Infrastructure/BotSharp.Core/BotSharpCoreExtensions.cs
@@ -6,7 +6,6 @@ using BotSharp.Abstraction.Settings;
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 db85df01..8b0775e8 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 95a5b8a2..813031af 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 f3876e8f..c44bf5cf 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 class PyInterpretationFn : IFunctionCallback
LlmConfig = GetLlmConfig(),
TemplateDict = new Dictionary
{
+ { "python_version", _settings.PythonVersion ?? "3.11" },
{ "user_requirement", args?.UserRquirement ?? string.Empty }
}
};
@@ -54,6 +53,8 @@ public class PyInterpretationFn : IFunctionCallback
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 class PyInterpretationFn : IFunctionCallback
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");
+ using (Py.GIL())
+ {
+ // 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;
+ // Redirect standard output/error to capture it
+ dynamic stringIO = io.StringIO();
+ sys.stdout = stringIO;
+ sys.stderr = stringIO;
- // Execute a simple Python script
- using var locals = new PyDict();
- PythonEngine.Exec(ret.PythonCode, null, locals);
+ // Set global items
+ using var globals = new PyDict();
+ if (ret.PythonCode?.Contains("__main__") == true)
+ {
+ globals.SetItem("__name__", new PyString("__main__"));
+ }
- // Console.WriteLine($"Result from Python: {result}");
- message.Content = stringIO.getvalue();
+ // Execute Python script
+ PythonEngine.Exec(ret.PythonCode, globals);
- // Restore the original stdout
- sys.stdout = sys.__stdout__;
+ // 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 @@ public class PyInterpretationFn : IFunctionCallback
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 @@ public class PyInterpretationFn : IFunctionCallback
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 00000000..0866478a
--- /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 2d243e35..00000000
--- 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 e1c1bd63..ed55ae4c 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 class PythonInterpreterPlugin : IBotSharpAppPlugin
{
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 01f5a9d1..7e380ba1 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 88ed52e6..84bac034 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.Functions.Models;
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 ee5a708e..c9aabc20 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 e69de29b..3ac455a1 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 9037aaff..00000000
--- 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 00000000..604e986f
--- /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 f00c4899..65b78111 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": {