refine running py code script
This commit is contained in:
parent
6a0153db28
commit
edd2ad6002
|
|
@ -1,4 +1,5 @@
|
||||||
using BotSharp.Abstraction.CodeInterpreter.Models;
|
using BotSharp.Abstraction.CodeInterpreter.Models;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace BotSharp.Abstraction.CodeInterpreter;
|
namespace BotSharp.Abstraction.CodeInterpreter;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace BotSharp.Abstraction.CodeInterpreter.Models;
|
namespace BotSharp.Abstraction.CodeInterpreter.Models;
|
||||||
|
|
||||||
public class CodeInterpretOptions
|
public class CodeInterpretOptions
|
||||||
{
|
{
|
||||||
public IEnumerable<KeyValue>? Arguments { get; set; }
|
public IEnumerable<KeyValue>? Arguments { get; set; }
|
||||||
|
public bool LockFree { get; set; }
|
||||||
|
public CancellationToken? CancellationToken { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
using BotSharp.Abstraction.CodeInterpreter.Models;
|
||||||
|
|
||||||
|
namespace BotSharp.Core.CodeInterpreter;
|
||||||
|
|
||||||
|
public class CodeScriptExecutor
|
||||||
|
{
|
||||||
|
private readonly ILogger<CodeScriptExecutor> _logger;
|
||||||
|
private readonly SemaphoreSlim _semLock = new(initialCount: 1, maxCount: 1);
|
||||||
|
|
||||||
|
public CodeScriptExecutor(
|
||||||
|
ILogger<CodeScriptExecutor> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CodeInterpretResult> Execute(Func<Task<CodeInterpretResult>> func, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await _semLock.WaitAsync(cancellationToken);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await func();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, $"Error in {nameof(CodeScriptExecutor)}.");
|
||||||
|
return new CodeInterpretResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
ErrorMsg = ex.Message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semLock.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ using BotSharp.Abstraction.Planning;
|
||||||
using BotSharp.Abstraction.Plugins.Models;
|
using BotSharp.Abstraction.Plugins.Models;
|
||||||
using BotSharp.Abstraction.Settings;
|
using BotSharp.Abstraction.Settings;
|
||||||
using BotSharp.Abstraction.Templating;
|
using BotSharp.Abstraction.Templating;
|
||||||
|
using BotSharp.Core.CodeInterpreter;
|
||||||
using BotSharp.Core.Instructs;
|
using BotSharp.Core.Instructs;
|
||||||
using BotSharp.Core.MessageHub;
|
using BotSharp.Core.MessageHub;
|
||||||
using BotSharp.Core.MessageHub.Observers;
|
using BotSharp.Core.MessageHub.Observers;
|
||||||
|
|
@ -70,6 +71,7 @@ public class ConversationPlugin : IBotSharpPlugin
|
||||||
services.AddScoped<ITokenStatistics, TokenStatistics>();
|
services.AddScoped<ITokenStatistics, TokenStatistics>();
|
||||||
|
|
||||||
services.AddScoped<IAgentUtilityHook, WebSearchUtilityHook>();
|
services.AddScoped<IAgentUtilityHook, WebSearchUtilityHook>();
|
||||||
|
services.AddSingleton<CodeScriptExecutor>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AttachMenu(List<PluginMenuDef> menu)
|
public bool AttachMenu(List<PluginMenuDef> menu)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Python.Runtime;
|
using Python.Runtime;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
@ -66,30 +67,10 @@ public class PyProgrammerFn : IFunctionCallback
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Py.GIL())
|
var (isSuccess, result) = InnerRunCode(ret.PythonCode);
|
||||||
|
if (isSuccess)
|
||||||
{
|
{
|
||||||
// Import necessary Python modules
|
message.Content = result;
|
||||||
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>
|
message.RichContent = new RichContent<IRichMessage>
|
||||||
{
|
{
|
||||||
Recipient = new Recipient { Id = convService.ConversationId },
|
Recipient = new Recipient { Id = convService.ConversationId },
|
||||||
|
|
@ -100,21 +81,70 @@ public class PyProgrammerFn : IFunctionCallback
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
message.StopCompletion = true;
|
message.StopCompletion = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message.Content = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var errorMsg = $"Error when executing python code. {ex.Message}";
|
||||||
|
message.Content = errorMsg;
|
||||||
|
_logger.LogError(ex, errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
// Restore the original stdout/stderr
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Run python code script => (isSuccess, result)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codeScript"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private (bool, string) InnerRunCode(string codeScript)
|
||||||
|
{
|
||||||
|
using (Py.GIL())
|
||||||
|
{
|
||||||
|
// Import necessary Python modules
|
||||||
|
dynamic sys = Py.Import("sys");
|
||||||
|
dynamic io = Py.Import("io");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 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__"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute Python script
|
||||||
|
PythonEngine.Exec(codeScript, globals);
|
||||||
|
|
||||||
|
// Get result
|
||||||
|
var result = stringIO.getvalue()?.ToString() as string;
|
||||||
|
return (true, result?.TrimEnd('\r', '\n') ?? string.Empty);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var errorMsg = $"Error when executing inner python code. {ex.Message}";
|
||||||
|
_logger.LogError(ex, errorMsg);
|
||||||
|
return (false, errorMsg);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Restore the original stdout/stderr/argv
|
||||||
sys.stdout = sys.__stdout__;
|
sys.stdout = sys.__stdout__;
|
||||||
sys.stderr = sys.__stderr__;
|
sys.stderr = sys.__stderr__;
|
||||||
sys.argv = new PyList();
|
sys.argv = new PyList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
var errorMsg = $"Error when executing python code.";
|
|
||||||
message.Content = $"{errorMsg} {ex.Message}";
|
|
||||||
_logger.LogError(ex, errorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> GetChatCompletion(Agent agent, List<RoleDialogModel> dialogs)
|
private async Task<string> GetChatCompletion(Agent agent, List<RoleDialogModel> dialogs)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
using BotSharp.Core.CodeInterpreter;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Python.Runtime;
|
using Python.Runtime;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace BotSharp.Plugin.PythonInterpreter.Services;
|
namespace BotSharp.Plugin.PythonInterpreter.Services;
|
||||||
|
|
@ -8,27 +10,63 @@ public class PyInterpretService : ICodeInterpretService
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider _services;
|
private readonly IServiceProvider _services;
|
||||||
private readonly ILogger<PyInterpretService> _logger;
|
private readonly ILogger<PyInterpretService> _logger;
|
||||||
|
private readonly CodeScriptExecutor _executor;
|
||||||
|
|
||||||
public PyInterpretService(
|
public PyInterpretService(
|
||||||
IServiceProvider services,
|
IServiceProvider services,
|
||||||
ILogger<PyInterpretService> logger)
|
ILogger<PyInterpretService> logger,
|
||||||
|
CodeScriptExecutor executor)
|
||||||
{
|
{
|
||||||
_services = services;
|
_services = services;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_executor = executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Provider => "botsharp-py-interpreter";
|
public string Provider => "botsharp-py-interpreter";
|
||||||
|
|
||||||
public async Task<CodeInterpretResult> RunCode(string codeScript, CodeInterpretOptions? options = null)
|
public async Task<CodeInterpretResult> RunCode(string codeScript, CodeInterpretOptions? options = null)
|
||||||
|
{
|
||||||
|
if (options?.LockFree != true)
|
||||||
|
{
|
||||||
|
return await _executor.Execute(async () =>
|
||||||
|
{
|
||||||
|
return InnerRunCode(codeScript, options);
|
||||||
|
}, cancellationToken: options?.CancellationToken ?? CancellationToken.None);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return InnerRunCode(codeScript, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CodeInterpretResult InnerRunCode(string codeScript, CodeInterpretOptions? options = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Py.GIL())
|
return CoreRun(codeScript, options);
|
||||||
{
|
}
|
||||||
// Import necessary Python modules
|
catch (Exception ex)
|
||||||
dynamic sys = Py.Import("sys");
|
{
|
||||||
dynamic io = Py.Import("io");
|
var errorMsg = $"Error when executing inner python code in {nameof(PyInterpretService)}: {Provider}.";
|
||||||
|
_logger.LogError(ex, errorMsg);
|
||||||
|
|
||||||
|
return new CodeInterpretResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
ErrorMsg = errorMsg
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CodeInterpretResult CoreRun(string codeScript, CodeInterpretOptions? options = null)
|
||||||
|
{
|
||||||
|
using (Py.GIL())
|
||||||
|
{
|
||||||
|
// Import necessary Python modules
|
||||||
|
dynamic sys = Py.Import("sys");
|
||||||
|
dynamic io = Py.Import("io");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
// Redirect standard output/error to capture it
|
// Redirect standard output/error to capture it
|
||||||
dynamic stringIO = io.StringIO();
|
dynamic stringIO = io.StringIO();
|
||||||
sys.stdout = stringIO;
|
sys.stdout = stringIO;
|
||||||
|
|
@ -64,28 +102,30 @@ public class PyInterpretService : ICodeInterpretService
|
||||||
// Get result
|
// Get result
|
||||||
var result = stringIO.getvalue()?.ToString() as string;
|
var result = stringIO.getvalue()?.ToString() as string;
|
||||||
|
|
||||||
// Restore the original stdout/stderr
|
|
||||||
sys.stdout = sys.__stdout__;
|
|
||||||
sys.stderr = sys.__stderr__;
|
|
||||||
sys.argv = new PyList();
|
|
||||||
|
|
||||||
return new CodeInterpretResult
|
return new CodeInterpretResult
|
||||||
{
|
{
|
||||||
Result = result?.TrimEnd('\r', '\n'),
|
Result = result?.TrimEnd('\r', '\n'),
|
||||||
Success = true
|
Success = true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
catch (Exception ex)
|
||||||
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,
|
var errorMsg = $"Error when executing core python code in {nameof(PyInterpretService)}: {Provider}. {ex.Message}";
|
||||||
ErrorMsg = errorMsg
|
_logger.LogError(ex, errorMsg);
|
||||||
};
|
|
||||||
|
return new CodeInterpretResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
ErrorMsg = errorMsg
|
||||||
|
};
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Restore the original stdout/stderr/argv
|
||||||
|
sys.stdout = sys.__stdout__;
|
||||||
|
sys.stderr = sys.__stderr__;
|
||||||
|
sys.argv = new PyList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue