Skip to content

Commit 03880b0

Browse files
author
Jicheng Lu
committed
refine py interpreter
1 parent ff08a39 commit 03880b0

File tree

10 files changed

+41
-86
lines changed

10 files changed

+41
-86
lines changed

src/Infrastructure/BotSharp.Abstraction/Coding/ICodeProcessor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public interface ICodeProcessor
1717
/// <param name="cancellationToken">The cancellation token</param>
1818
/// <returns></returns>
1919
/// <exception cref="NotImplementedException"></exception>
20-
Task<CodeInterpretResponse> RunAsync(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
20+
CodeInterpretResponse Run(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
2121
=> throw new NotImplementedException();
2222

2323
/// <summary>

src/Infrastructure/BotSharp.Abstraction/Coding/Settings/CodingSettings.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,4 @@ public class CodeScriptExecutionSettings
1919
public bool UseLock { get; set; }
2020
public bool UseProcess { get; set; }
2121
public int TimeoutSeconds { get; set; } = 3;
22-
public int MaxConcurrency { get; set; } = 1;
2322
}

src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ private async Task<bool> TriggerCodeScript(Agent agent, string triggerName, Rule
140140

141141
var (useLock, useProcess, timeoutSeconds) = GetCodeExecutionConfig();
142142
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
143-
var response = await processor.RunAsync(codeScript.Content, options: new()
143+
var response = processor.Run(codeScript.Content, options: new()
144144
{
145145
ScriptName = scriptName,
146146
Arguments = arguments,

src/Infrastructure/BotSharp.Core/Coding/CodeScriptExecutor.cs

Lines changed: 0 additions & 39 deletions
This file was deleted.

src/Infrastructure/BotSharp.Core/Coding/CodingPlugin.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,5 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
1313
var coding = new CodingSettings();
1414
config.Bind("Coding", coding);
1515
services.AddSingleton(provider => coding);
16-
17-
services.AddSingleton<CodeScriptExecutor>();
1816
}
1917
}

src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ public void RegisterDI(IServiceCollection services, IConfiguration config)
6969
services.AddScoped<ITokenStatistics, TokenStatistics>();
7070

7171
services.AddScoped<IAgentUtilityHook, WebSearchUtilityHook>();
72-
services.AddSingleton<CodeScriptExecutor>();
7372
}
7473

7574
public bool AttachMenu(List<PluginMenuDef> menu)

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ await hook.OnResponseGenerated(new InstructResponseModel
258258
// Run code script
259259
var (useLock, useProcess, timeoutSeconds) = GetCodeExecutionConfig(codingSettings);
260260
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
261-
var codeResponse = await codeProcessor.RunAsync(context.CodeScript?.Content ?? string.Empty, options: new()
261+
var codeResponse = codeProcessor.Run(context.CodeScript?.Content ?? string.Empty, options: new()
262262
{
263263
ScriptName = context.CodeScript?.Name,
264264
Arguments = context.Arguments,

src/Plugins/BotSharp.Plugin.PythonInterpreter/Functions/PyProgrammerFn.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public async Task<bool> Execute(RoleDialogModel message)
7171

7272
try
7373
{
74-
var (isSuccess, result) = await InnerRunCode(ret.PythonCode);
74+
var (isSuccess, result) = InnerRunCode(ret.PythonCode);
7575
if (isSuccess)
7676
{
7777
message.Content = result;
@@ -107,7 +107,7 @@ public async Task<bool> Execute(RoleDialogModel message)
107107
/// </summary>
108108
/// <param name="codeScript"></param>
109109
/// <returns></returns>
110-
private async Task<(bool, string)> InnerRunCode(string codeScript)
110+
private (bool, string) InnerRunCode(string codeScript)
111111
{
112112
var codeProvider = _codingSettings.CodeExecution?.Processor;
113113
codeProvider = !string.IsNullOrEmpty(codeProvider) ? codeProvider : BuiltInCodeProcessor.PyInterpreter;
@@ -122,7 +122,7 @@ public async Task<bool> Execute(RoleDialogModel message)
122122
var (useLock, useProcess, timeoutSeconds) = GetCodeExecutionConfig();
123123
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
124124

125-
var response = await processor.RunAsync(codeScript, options: new()
125+
var response = processor.Run(codeScript, options: new()
126126
{
127127
UseLock = useLock,
128128
UseProcess = useProcess

src/Plugins/BotSharp.Plugin.PythonInterpreter/Services/PyCodeInterpreter.cs

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,42 +12,37 @@ public class PyCodeInterpreter : ICodeProcessor
1212
{
1313
private readonly IServiceProvider _services;
1414
private readonly ILogger<PyCodeInterpreter> _logger;
15-
private readonly CodeScriptExecutor _executor;
1615
private readonly CodingSettings _settings;
16+
private static SemaphoreSlim _semLock = new(initialCount: 1, maxCount: 1);
1717

1818
public PyCodeInterpreter(
1919
IServiceProvider services,
2020
ILogger<PyCodeInterpreter> logger,
21-
CodeScriptExecutor executor,
2221
CodingSettings settings)
2322
{
2423
_services = services;
2524
_logger = logger;
26-
_executor = executor;
2725
_settings = settings;
2826
}
2927

3028
public string Provider => BuiltInCodeProcessor.PyInterpreter;
3129

32-
public async Task<CodeInterpretResponse> RunAsync(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
30+
public CodeInterpretResponse Run(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
3331
{
3432
if (options?.UseLock == true)
3533
{
3634
try
3735
{
38-
return await _executor.ExecuteAsync(async () =>
39-
{
40-
return await InnerRunCode(codeScript, options, cancellationToken);
41-
}, cancellationToken: cancellationToken);
36+
return InnerRunWithLock(codeScript, options, cancellationToken);
4237
}
4338
catch (Exception ex)
4439
{
45-
_logger.LogError(ex, "Error when using code script executor.");
40+
_logger.LogError(ex, $"Error when using code script with {nameof(SemaphoreSlim)}.");
4641
return new() { ErrorMsg = ex.Message };
4742
}
4843
}
4944

50-
return await InnerRunCode(codeScript, options, cancellationToken);
45+
return InnerRunCode(codeScript, options, cancellationToken);
5146
}
5247

5348
public async Task<CodeGenerationResult> GenerateCodeScriptAsync(string text, CodeGenerationOptions? options = null)
@@ -105,7 +100,30 @@ public async Task<CodeGenerationResult> GenerateCodeScriptAsync(string text, Cod
105100

106101

107102
#region Private methods
108-
private async Task<CodeInterpretResponse> InnerRunCode(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
103+
private CodeInterpretResponse InnerRunWithLock(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
104+
{
105+
var lockAcquired = false;
106+
try
107+
{
108+
_semLock.Wait(cancellationToken);
109+
lockAcquired = true;
110+
return InnerRunCode(codeScript, options, cancellationToken);
111+
}
112+
catch (Exception ex)
113+
{
114+
_logger.LogError(ex, $"Error in {nameof(InnerRunWithLock)}");
115+
return new() { ErrorMsg = ex.Message };
116+
}
117+
finally
118+
{
119+
if (lockAcquired)
120+
{
121+
_semLock.Release();
122+
}
123+
}
124+
}
125+
126+
private CodeInterpretResponse InnerRunCode(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
109127
{
110128
var response = new CodeInterpretResponse();
111129
var scriptName = options?.ScriptName ?? codeScript.SubstringMax(30);
@@ -116,11 +134,11 @@ private async Task<CodeInterpretResponse> InnerRunCode(string codeScript, CodeIn
116134

117135
if (options?.UseProcess == true)
118136
{
119-
response = await CoreRunProcess(codeScript, options, cancellationToken);
137+
response = CoreRunProcess(codeScript, options, cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
120138
}
121139
else
122140
{
123-
response = await CoreRunScript(codeScript, options, cancellationToken);
141+
response = CoreRunScript(codeScript, options, cancellationToken);
124142
}
125143

126144
_logger.LogWarning($"End running python code script in {Provider}: {scriptName}");
@@ -141,7 +159,7 @@ private async Task<CodeInterpretResponse> InnerRunCode(string codeScript, CodeIn
141159
}
142160
}
143161

144-
private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
162+
private CodeInterpretResponse CoreRunScript(string codeScript, CodeInterpretOptions? options = null, CancellationToken cancellationToken = default)
145163
{
146164
cancellationToken.ThrowIfCancellationRequested();
147165

@@ -161,21 +179,6 @@ private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeI
161179

162180
try
163181
{
164-
// Capture the Python thread ID for the current thread executing under the GIL
165-
var pythonThreadId = PythonEngine.GetPythonThreadID();
166-
using var reg = cancellationToken.Register(() =>
167-
{
168-
try
169-
{
170-
PythonEngine.Interrupt(pythonThreadId);
171-
_logger.LogWarning($"Cancellation requested: issued PythonEngine.Interrupt for thread {pythonThreadId} (request {requestId})");
172-
}
173-
catch (Exception ex)
174-
{
175-
_logger.LogError(ex, "Failed to interrupt Python execution on cancellation.");
176-
}
177-
});
178-
179182
// Redirect standard output/error to capture it
180183
dynamic outIO = io.StringIO();
181184
dynamic errIO = io.StringIO();
@@ -206,8 +209,6 @@ private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeI
206209
}
207210
sys.argv = list;
208211

209-
cancellationToken.ThrowIfCancellationRequested();
210-
211212
// Execute Python script
212213
PythonEngine.Exec(codeScript, globals);
213214

@@ -217,8 +218,6 @@ private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeI
217218
var stdout = outIO.getvalue()?.ToString() as string;
218219
var stderr = errIO.getvalue()?.ToString() as string;
219220

220-
cancellationToken.ThrowIfCancellationRequested();
221-
222221
return new CodeInterpretResponse
223222
{
224223
Result = stdout?.TrimEnd('\r', '\n') ?? string.Empty,
@@ -237,10 +236,10 @@ private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeI
237236
sys.stderr = sys.__stderr__;
238237
sys.argv = new PyList();
239238
}
240-
};
239+
}
241240
}, cancellationToken);
242241

243-
return await execTask.WaitAsync(cancellationToken);
242+
return execTask.WaitAsync(cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult();
244243
}
245244

246245

src/WebStarter/appsettings.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,8 +527,7 @@
527527
"Processor": null,
528528
"UseLock": false,
529529
"UseProcess": false,
530-
"TimeoutSeconds": 3,
531-
"MaxConcurrency": 1
530+
"TimeoutSeconds": 3
532531
}
533532
},
534533

0 commit comments

Comments
 (0)