Skip to content

Conversation

iceljc
Copy link
Collaborator

@iceljc iceljc commented Oct 1, 2025

PR Type

Enhancement


Description

  • Add Python interpreter plugin with code execution capabilities

  • Implement agent code script management system

  • Enhance instruct service with code template execution

  • Add realtime options support for audio format configuration


Diagram Walkthrough

flowchart LR
  A["User Request"] --> B["InstructService"]
  B --> C["Code Template Check"]
  C --> D["PyInterpretService"]
  D --> E["Python Code Execution"]
  E --> F["Result Output"]
  B --> G["LLM Completion"]
  G --> F
  H["Agent Code Scripts"] --> C
  I["Repository Layer"] --> H
Loading

File Walkthrough

Relevant files
Enhancement
18 files
PythonInterpreterPlugin.cs
Main plugin registration and Python engine initialization
+59/-0   
PyInterpretService.cs
Core Python code execution service implementation               
+91/-0   
PyProgrammerFn.cs
Function callback for Python code generation and execution
+189/-0 
InstructService.Execute.cs
Enhanced instruct service with code template execution     
+156/-20
IBotSharpRepository.cs
Add agent code script repository interface methods             
+14/-4   
MongoRepository.AgentCodeScript.cs
MongoDB implementation for agent code script storage         
+132/-0 
FileRepository.AgentCodeScript.cs
File system implementation for agent code script storage 
+157/-0 
AgentCodeScript.cs
Domain model for agent code scripts                                           
+29/-0   
ICodeInterpretService.cs
Interface definition for code interpretation services       
+11/-0   
CodeInstructOptions.cs
Options model for code instruction execution                         
+8/-0     
RealtimeOptions.cs
Audio format configuration options for realtime                   
+12/-0   
RoleDialogModel.cs
Add `RoleContent` property for unified content access       
+19/-0   
PyPackageHelper.cs
Python package installation and validation utilities         
+178/-0 
AgentService.CreateAgent.cs
Add code script loading from file system                                 
+29/-0   
AgentService.RefreshAgents.cs
Include code scripts in agent refresh process                       
+5/-2     
IInstructHook.cs
Add code execution hooks to instruct interface                     
+6/-3     
IRealTimeCompletion.cs
Add options setter for realtime completion providers         
+1/-0     
RealTimeCompletionProvider.cs
Implement realtime options and use `RoleContent` property
+11/-36 
Configuration changes
1 files
PythonInterpreterSettings.cs
Configuration settings for Python interpreter                       
+18/-0   
Additional files
75 files
Directory.Packages.props +1/-1     
AgentCodeScriptType.cs +7/-0     
AgentFuncVisMode.cs +1/-1     
AgentRole.cs +1/-1     
AgentType.cs +1/-1     
BuiltInAgentId.cs +1/-1     
IAgentService.cs +0/-1     
CodeInterpretOptions.cs +6/-0     
CodeInterpretResult.cs +8/-0     
ConversationChannel.cs +1/-1     
ConversationStatus.cs +1/-1     
StateDataType.cs +1/-1     
StateSource.cs +1/-1     
LanguageType.cs +1/-1     
StateConst.cs +1/-1     
IInstructService.cs +8/-5     
CodeInstructContext.cs +7/-0     
InstructResult.cs +2/-1     
InterpretationRequest.cs +0/-10   
InterpreterSettings.cs +0/-11   
KeyValue.cs +11/-0   
IRealtimeHub.cs +2/-1     
AgentCodeScriptFilter.cs +12/-0   
RoutingMode.cs +1/-1     
RuleType.cs +1/-1     
TaskStatus.cs +1/-1     
RealtimeHub.cs +6/-1     
AgentService.UpdateAgent.cs +1/-106 
BotSharp.Core.csproj +2/-1     
BotSharpCoreExtensions.cs +0/-5     
ExecuteTemplateFn.cs +0/-2     
FileRepository.Agent.cs +6/-0     
FileRepository.AgentTask.cs +16/-9   
FileRepository.cs +1/-0     
AgentTaskService.cs +1/-1     
demo.py +12/-0   
InstructionLogHook.cs +4/-1     
AgentController.cs +0/-6     
InstructModeController.cs +3/-3     
InstructMessageModel.cs +3/-0     
ChatCompletionProvider.cs +11/-11 
ChatCompletionProvider.cs +3/-3     
util-chart-plot_instruction.liquid +5/-2     
ChatStreamMiddleware.cs +3/-4     
ChatStreamRequest.cs +4/-0     
ChatCompletionProvider.cs +3/-3     
GeminiChatCompletionProvider.cs +5/-5     
PalmChatCompletionProvider.cs +3/-3     
RealTimeCompletionProvider.cs +17/-11 
ChatCompletionProvider.cs +2/-2     
ChatCompletionProvider.cs +1/-1     
ChatCompletionProvider.cs +4/-4     
MicrosoftExtensionsAIChatCompletionProvider.cs +3/-3     
AgentCodeScriptDocument.cs +37/-0   
MongoDbContext.cs +3/-0     
MongoRepository.Agent.cs +5/-1     
MongoRepository.AgentTask.cs +19/-23 
ChatCompletionProvider.cs +3/-3     
BotSharp.Plugin.PythonInterpreter.csproj +8/-6     
UtilityName.cs +1/-1     
InterpretationFn.cs +0/-46   
PyProgrammerUtilityHook.cs +6/-6     
InterpreterPlugin.cs +0/-40   
LlmContextIn.cs +12/-0   
LlmContextOut.cs +12/-0   
PackageInstallResult.cs +7/-0     
Using.cs +17/-0   
python_interpreter.json +0/-19   
util-code-python_programmer.json +14/-0   
python_interpreter.fn.liquid +0/-1     
util-code-python_generate_instruction.liquid +49/-0   
util-code-python_programmer.fn.liquid +1/-0     
ChatCompletionProvider.cs +2/-2     
WebStarter.csproj +1/-0     
appsettings.json +27/-5   

Copy link

qodo-merge-pro bot commented Oct 1, 2025

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 No relevant tests
🔒 Security concerns

Code execution sandboxing:
The Python interpreter executes arbitrary LLM-generated code (PyProgrammerFn and PyInterpretService) with full process permissions and access to sys modules. There are no resource limits, filesystem/network restrictions, or timeouts. This could allow remote code execution impacting the host. Consider sandboxing (separate process/container, restricted working directory, timeout, memory/CPU limits), validating/whitelisting imports, and disabling dangerous modules. Also, pip install via PyPackageHelper can fetch arbitrary packages at runtime without verification, which may introduce supply-chain risks.

⚡ Recommended focus areas for review

Possible Issue

Missing consideration for installing/importing required Python packages returned by the model; ret.ImportedPackages is parsed but never used. Generated code that relies on external packages may fail at runtime without pre-install checks or sandboxing.

var response = await GetChatCompletion(innerAgent, dialogs);
var ret = response.JsonContent<LlmContextOut>();

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 (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() as string;
        message.Content = result?.TrimEnd('\r', '\n') ?? string.Empty;
        message.RichContent = new RichContent<IRichMessage>
        {
            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)
{
    var errorMsg = $"Error when executing python code.";
    message.Content = $"{errorMsg} {ex.Message}";
    _logger.LogError(ex, errorMsg);
}
Logic Error

In GetAgentCodeScripts, the condition if (scriptNames != null || !scriptNames.Contains(fileName)) will skip all files whenever scriptNames is not null. Likely intended to be && to filter only when a non-null list is provided.

var results = new List<AgentCodeScript>();
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;
Resource Cleanup

Python runtime is initialized and thread state stored, but shutdown relies on ApplicationStopping only. If initialization partially fails or multiple initializations occur across scopes, ensure idempotency and guard against double shutdown; also validate InstallLocation path to a DLL vs directory on non-Windows.

public void Configure(IApplicationBuilder app)
{
    var sp = app.ApplicationServices;
    var settings = sp.GetRequiredService<PythonInterpreterSettings>();
    var logger = sp.GetRequiredService<ILogger<PyProgrammerFn>>();
    var pyLoc = settings.InstallLocation;

    try
    {
        if (File.Exists(pyLoc))
        {
            Runtime.PythonDLL = pyLoc;
            PythonEngine.Initialize();
            _pyState = PythonEngine.BeginAllowThreads();

            var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
            lifetime.ApplicationStopping.Register(() => {
                PythonEngine.EndAllowThreads(_pyState);
                PythonEngine.Shutdown();
            });
        }
        else
        {
            logger.LogError($"Python dll not found at {pyLoc}");
        }
    }
    catch (Exception ex)
    {
        logger.LogError(ex, $"Error when loading python dll {pyLoc}");
    }
}

Copy link

qodo-merge-pro bot commented Oct 1, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Implement robust sandboxing for code execution

The current direct execution of LLM-generated Python code is a major security
risk. To fix this, implement a sandboxed environment using tools like Docker to
isolate the code execution and restrict its access to system resources.

Examples:

src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs [23-90]
src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs [67-117]

Solution Walkthrough:

Before:

// In PyInterpretService.cs and PyProgrammerFn.cs
public class PyInterpretService
{
    public async Task<CodeInterpretResult> RunCode(string codeScript, ...)
    {
        try
        {
            using (Py.GIL())
            {
                // ... setup environment
                
                // Direct execution of arbitrary code in the same process
                PythonEngine.Exec(codeScript, globals);

                // ... capture and return result
            }
        }
        catch (Exception ex) { /* ... */ }
    }
}

After:

// A new sandboxed execution service
public class SandboxedPythonExecutor
{
    public async Task<ExecutionResult> Execute(string code)
    {
        // 1. Spin up an isolated environment (e.g., Docker container).
        // 2. Set strict resource limits (no network, read-only filesystem).
        // 3. Execute the code inside the isolated environment.
        // 4. Capture stdout/stderr.
        // 5. Tear down the environment.
        // 6. Return the result.
        var result = await RunInContainerAsync(code);
        return result;
    }
}

// In PyInterpretService.cs
public async Task<CodeInterpretResult> RunCode(string codeScript, ...)
{
    var sandboxedExecutor = _services.GetRequiredService<SandboxedPythonExecutor>();
    var result = await sandboxedExecutor.Execute(codeScript);
    // ... return result
}
Suggestion importance[1-10]: 10

__

Why: This suggestion addresses a critical security vulnerability by pointing out that executing LLM-generated code directly is unsafe and proposes a robust, standard solution.

High
Possible issue
Ensure agent-specific data deletion

Fix a critical bug in DeleteAgentCodeScripts by adding a filter for agentId to
prevent deleting code scripts from unintended agents.

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCode.cs [91-111]

     public bool DeleteAgentCodeScripts(string agentId, List<string>? scriptNames)
     {
         if (string.IsNullOrWhiteSpace(agentId))
         {
             return false;
         }
 
-        var filterDef = Builders<AgentCodeDocument>.Filter.Empty;
+        var builder = Builders<AgentCodeDocument>.Filter;
+        var filterDef = builder.Eq(x => x.AgentId, agentId);
+
         if (scriptNames != null)
         {
-            var builder = Builders<AgentCodeDocument>.Filter;
-            var filters = new List<FilterDefinition<AgentCodeDocument>>
-            {
-                builder.In(x => x.Name, scriptNames)
-            };
-            filterDef = builder.And(filters);
+            var scriptFilter = builder.In(x => x.Name, scriptNames);
+            filterDef = builder.And(filterDef, scriptFilter);
         }
 
         var deleted = _dc.AgentCodes.DeleteMany(filterDef);
         return deleted.DeletedCount > 0;
     }
  • Apply / Chat
Suggestion importance[1-10]: 10

__

Why: This suggestion points out a critical data-loss bug where agentId is ignored during deletion, potentially deleting scripts from all agents. The fix is crucial for data integrity.

High
Fix incorrect filtering logic

Fix the incorrect filtering logic in GetAgentCodeScripts by changing the ||
operator to && to correctly filter files when scriptNames is provided.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCode.cs [24-28]

     var fileName = Path.GetFileName(file);
-    if (scriptNames != null || !scriptNames.Contains(fileName))
+    if (scriptNames != null && !scriptNames.Contains(fileName))
     {
         continue;
     }
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical bug where the filtering logic is flawed, causing the method to either return an empty list or throw a NullReferenceException.

High
Ensure Python streams are always restored

Move the restoration logic for Python's sys.stdout and sys.stderr into a finally
block to ensure they are always restored, even if an exception occurs.

src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs [91-107]

     var result = stringIO.getvalue()?.ToString() as string;
     message.Content = result?.TrimEnd('\r', '\n') ?? string.Empty;
     message.RichContent = new RichContent<IRichMessage>
     {
         Recipient = new Recipient { Id = convService.ConversationId },
         Message = new ProgramCodeTemplateMessage
         {
             Text = ret.PythonCode ?? string.Empty,
             Language = "python"
         }
     };
     message.StopCompletion = true;
+  }
+  finally
+  {
+      using (Py.GIL())
+      {
+          // Restore the original stdout/stderr
+          dynamic sys = Py.Import("sys");
+          sys.stdout = sys.__stdout__;
+          sys.stderr = sys.__stderr__;
+      }
+  }
 
-    // Restore the original stdout/stderr
-    sys.stdout = sys.__stdout__;
-    sys.stderr = sys.__stderr__;
-

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a potential issue where Python's standard streams are not restored on error and provides a robust fix using a finally block, improving application stability.

Medium
Learned
best practice
Remove throwing default implementation

Remove the throwing default implementation and require implementers to provide a
concrete method, or provide a safe base class if needed.

src/Infrastructure/BotSharp.Abstraction/CodeInterpreter/ICodeInterpretService.cs [7-11]

 public interface ICodeInterpretService
 {
     string Provider { get; }
-
-    Task<CodeInterpretResult> RunCode(string codeScript, CodeInterpretOptions? options = null)
-        => throw new NotImplementedException();
+    Task<CodeInterpretResult> RunCode(string codeScript, CodeInterpretOptions? options = null);
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Validate asynchronous usage; avoid default interface implementations that throw NotImplementedException at runtime.

Low
  • More

@iceljc iceljc marked this pull request as ready for review October 6, 2025 15:58
Copy link

qodo-merge-pro bot commented Oct 6, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🔴
Arbitrary code execution

Description: Arbitrary Python code execution via Python.NET (PythonEngine.Exec) without sandboxing,
resource limits, or process isolation allows code injection to access filesystem, network,
or host process memory.
PyInterpretService.cs [22-90]

Referred Code
public async Task<CodeInterpretResult> RunCode(string codeScript, 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 (codeScript.Contains("__main__") == true)
            {
                globals.SetItem("__name__", new PyString("__main__"));
            }



 ... (clipped 48 lines)
LLM code execution risk

Description: Executes LLM-generated Python code directly inside app process (PythonEngine.Exec)
capturing stdout with no isolation or validation, enabling remote code execution if LLM is
manipulated.
PyProgrammerFn.cs [67-116]

Referred Code
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 (ret.PythonCode?.Contains("__main__") == true)
        {
            globals.SetItem("__name__", new PyString("__main__"));
        }

        // Execute Python script



 ... (clipped 29 lines)
Supply-chain risk

Description: Invokes pip install with unvalidated package names from inputs which may allow dependency
confusion or supply-chain compromise and executes external process without strict path or
allowlist.
PyPackageHelper.cs [21-69]

Referred Code
{
    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"
        };
    }



 ... (clipped 28 lines)
Untrusted library load

Description: Loads Python runtime DLL path from configuration without validation; incorrect or tampered
path could lead to loading untrusted native library (DLL preloading risk).
PythonInterpreterPlugin.cs [35-58]

Referred Code
try
{
    if (File.Exists(pyLoc))
    {
        Runtime.PythonDLL = pyLoc;
        PythonEngine.Initialize();
        _pyState = PythonEngine.BeginAllowThreads();

        var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();
        lifetime.ApplicationStopping.Register(() => {
            PythonEngine.EndAllowThreads(_pyState);
            PythonEngine.Shutdown();
        });
    }
    else
    {
        logger.LogError($"Python dll not found at {pyLoc}");
    }
}
catch (Exception ex)
{



 ... (clipped 3 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
No custom compliance provided

Follow the guide to enable custom compliance check.

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Copy link

qodo-merge-pro bot commented Oct 6, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Implement sandboxing for Python execution

The current implementation of Python code execution is insecure because it runs
within the main application process. It should be moved to a sandboxed
environment, like a Docker container, to prevent potential server compromise.

Examples:

src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs [22-90]
src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs [67-115]

Solution Walkthrough:

Before:

// In PyInterpretService.cs
public async Task<CodeInterpretResult> RunCode(string codeScript, ...)
{
    try
    {
        using (Py.GIL())
        {
            // ... setup stdout/stderr redirection ...
            
            // Execute Python script directly in the current process
            PythonEngine.Exec(codeScript, globals);

            // ... get result and restore stdout/stderr ...
            return new CodeInterpretResult { Success = true, ... };
        }
    }
    catch (Exception ex) { ... }
}

After:

// In a new SandboxedPyInterpretService.cs
public async Task<CodeInterpretResult> RunCode(string codeScript, ...)
{
    try
    {
        // 1. Create a new isolated environment (e.g., Docker container)
        var containerId = await _sandboxManager.CreateContainerAsync("python:3.11-slim");

        // 2. Execute the script inside the isolated environment
        var execResult = await _sandboxManager.ExecuteInContainerAsync(containerId, codeScript);

        // 3. Clean up the environment
        await _sandboxManager.RemoveContainerAsync(containerId);

        // 4. Return the result from the sandboxed execution
        return new CodeInterpretResult { Result = execResult.StdOut, Success = execResult.ExitCode == 0 };
    }
    catch (Exception ex) { ... }
}
Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a critical security vulnerability by executing LLM-generated Python code in-process, which could lead to server compromise, and proposes the correct mitigation strategy.

High
Possible issue
Prevent unintended deletion of agent scripts

Fix a critical bug in DeleteAgentCodeScripts where passing null for scripts
would delete all scripts from the database instead of just those for the
specified agent.

src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.AgentCodeScript.cs [104-130]

     public bool DeleteAgentCodeScripts(string agentId, List<AgentCodeScript>? scripts = null)
     {
         if (string.IsNullOrWhiteSpace(agentId))
         {
             return false;
         }
+
+        var builder = Builders<AgentCodeScriptDocument>.Filter;
+        var agentFilter = builder.Eq(x => x.AgentId, agentId);
 
         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<AgentCodeScriptDocument>(exprFilter);
-            deleted = _dc.AgentCodeScripts.DeleteMany(filterDef);
+            deleted = _dc.AgentCodeScripts.DeleteMany(builder.And(agentFilter, filterDef));
         }
         else
         {
-            deleted = _dc.AgentCodeScripts.DeleteMany(Builders<AgentCodeScriptDocument>.Filter.Empty);
+            deleted = _dc.AgentCodeScripts.DeleteMany(agentFilter);
         }
 
         return deleted.DeletedCount > 0;
     }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 10

__

Why: This suggestion identifies a critical bug where deleting all scripts for one agent would delete all scripts for all agents, leading to major data loss.

High
Implement empty method to prevent data loss

Implement the empty BulkInsertAgentTasks method to iterate through tasks and
save them individually, preventing data loss.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs [140-143]

     public void BulkInsertAgentTasks(string agentId, List<AgentTask> tasks)
     {
-        
+        if (tasks.IsNullOrEmpty()) return;
+
+        foreach (var task in tasks)
+        {
+            InsertAgentTask(task);
+        }
     }
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies an empty method body for BulkInsertAgentTasks which was introduced in the PR, and its implementation is crucial to prevent data loss.

Medium
Fix bulk insert logic for code scripts

Fix the BulkInsertAgentCodeScripts method to correctly handle file and directory
creation for new scripts, instead of incorrectly reusing the update logic.

src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentCodeScript.cs [104-107]

     public bool BulkInsertAgentCodeScripts(string agentId, List<AgentCodeScript> scripts)
     {
-        return UpdateAgentCodeScripts(agentId, 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))
+            {
+                Directory.CreateDirectory(dir);
+            }
+
+            var file = Path.Combine(dir, script.Name);
+            File.WriteAllText(file, script.Content);
+        }
+
+        return true;
     }
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a bug where BulkInsertAgentCodeScripts reuses UpdateAgentCodeScripts, which doesn't create new directories, causing insertions to fail silently.

Medium
General
Improve Python exception handling and logging

Enhance exception handling for Python code execution by specifically catching
PythonException to log the full Python traceback for better debugging.

src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs [110-115]

+    catch (PythonException pyEx)
+    {
+        var errorMsg = $"An error occurred while executing the Python code.";
+        using (Py.GIL())
+        {
+            dynamic traceback = Py.Import("traceback");
+            string pyTraceback = traceback.format_exc();
+            message.Content = $"{errorMsg}\n\nTraceback:\n{pyTraceback}";
+            _logger.LogError(pyEx, $"{errorMsg}\n{pyTraceback}");
+        }
+    }
     catch (Exception ex)
     {
         var errorMsg = $"Error when executing python code.";
         message.Content = $"{errorMsg} {ex.Message}";
         _logger.LogError(ex, errorMsg);
     }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: This suggestion improves error handling by catching a more specific PythonException and logging the full traceback, which significantly aids in debugging Python code execution failures.

Low
Ensure logging for missing interpreter

Remove the #if DEBUG condition for logging a missing code interpreter to ensure
warnings are always logged, aiding diagnostics in all build configurations.

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs [158-168]

     var codeProvider = codeOptions?.CodeInterpretProvider.IfNullOrEmptyAs("botsharp-py-interpreter");
     var codeInterpreter = _services.GetServices<ICodeInterpretService>()
                                    .FirstOrDefault(x => x.Provider.IsEqualTo(codeProvider));
     
     if (codeInterpreter == null)
     {
-#if DEBUG
         _logger.LogWarning($"No code interpreter found. (Agent: {agent.Id}, Code interpreter: {codeProvider})");
-#endif
         return response;
     }
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out that logging a missing code interpreter only in DEBUG mode can hide configuration issues in RELEASE builds, and recommends unconditional logging.

Low
Learned
best practice
Validate script input and defaults

Validate inputs and return safe defaults: check codeScript for null/empty before
execution and avoid calling .Contains on a potentially null string.

src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyInterpretService.cs [22-90]

 public async Task<CodeInterpretResult> RunCode(string codeScript, CodeInterpretOptions? options = null)
 {
+    if (string.IsNullOrWhiteSpace(codeScript))
+    {
+        return new CodeInterpretResult
+        {
+            Result = string.Empty,
+            Success = false,
+            ErrorMsg = "Empty code script."
+        };
+    }
+
     try
     {
         using (Py.GIL())
         {
-            ...
-            if (codeScript.Contains("__main__") == true)
+            dynamic sys = Py.Import("sys");
+            dynamic io = Py.Import("io");
+
+            dynamic stringIO = io.StringIO();
+            sys.stdout = stringIO;
+            sys.stderr = stringIO;
+
+            using var globals = new PyDict();
+            if (!string.IsNullOrEmpty(codeScript) && codeScript.Contains("__main__"))
             {
                 globals.SetItem("__name__", new PyString("__main__"));
             }
-            ...
+
+            var list = new PyList();
+            if (options?.Arguments?.Any() == true)
+            {
+                list.Append(new PyString("code.py"));
+                foreach (var arg in options.Arguments)
+                {
+                    if (!string.IsNullOrWhiteSpace(arg.Key) && !string.IsNullOrWhiteSpace(arg.Value))
+                    {
+                        list.Append(new PyString($"--{arg.Key}"));
+                        list.Append(new PyString($"{arg.Value}"));
+                    }
+                }
+            }
+            sys.argv = list;
+
             PythonEngine.Exec(codeScript, globals);
-            ...
+
+            var result = stringIO.getvalue()?.ToString() as string;
+
+            sys.stdout = sys.__stdout__;
+            sys.stderr = sys.__stderr__;
+            sys.argv = new PyList();
+
             return new CodeInterpretResult
             {
-                Result = result?.TrimEnd('\r', '\n'),
+                Result = (result ?? string.Empty).TrimEnd('\r', '\n'),
                 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
         {
+            Result = string.Empty,
             Success = false,
             ErrorMsg = errorMsg
         };
     }
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Validate external/system state and asynchronous usage to avoid deadlocks and invalid blocking calls; also ensure null-safe defaults.

Low
  • More

@iceljc iceljc merged commit 4404e21 into SciSharp:master Oct 7, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant