Skip to content

Commit 022ba17

Browse files
committed
resolve conflicts
2 parents 7ed9211 + 61cad7c commit 022ba17

File tree

10 files changed

+194
-41
lines changed

10 files changed

+194
-41
lines changed

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentCodeScript.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public AgentCodeScript() : base()
1414

1515
public override string ToString()
1616
{
17-
return $"{CodePath}";
17+
return base.ToString();
1818
}
1919
}
2020

@@ -29,4 +29,9 @@ public class AgentCodeScriptBase
2929
public string ScriptType { get; set; } = null!;
3030

3131
public string CodePath => $"{ScriptType}/{Name}";
32+
33+
public override string ToString()
34+
{
35+
return $"{CodePath}";
36+
}
3237
}

src/Infrastructure/BotSharp.Abstraction/Agents/Settings/AgentSettings.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,5 @@ public class AgentSettings
1010
/// <summary>
1111
/// This is the default LLM config for agent
1212
/// </summary>
13-
public AgentLlmConfig LlmConfig { get; set; } = new AgentLlmConfig();
14-
15-
/// <summary>
16-
/// General coding settings
17-
/// </summary>
18-
public CodingSettings Coding { get; set; } = new CodingSettings();
13+
public AgentLlmConfig LlmConfig { get; set; } = new();
1914
}

src/Infrastructure/BotSharp.Abstraction/Coding/Options/CodeInterpretOptions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ public class CodeInterpretOptions
77
public string? ScriptName { get; set; }
88
public IEnumerable<KeyValue>? Arguments { get; set; }
99
public bool UseMutex { get; set; }
10+
public bool UseProcess { get; set; }
1011
public CancellationToken? CancellationToken { get; set; }
1112
}

src/Infrastructure/BotSharp.Abstraction/Coding/Responses/CodeInterpretResponse.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,9 @@ namespace BotSharp.Abstraction.Coding.Responses;
33
public class CodeInterpretResponse : ResponseBase
44
{
55
public string Result { get; set; } = string.Empty;
6+
7+
public override string ToString()
8+
{
9+
return Result ?? ErrorMsg ?? $"Success: {Success}";
10+
}
611
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
namespace BotSharp.Abstraction.Coding.Settings;
22

33
public class CodingSettings
4+
{
5+
public CodeScriptGenerationSettings CodeGeneration { get; set; } = new();
6+
7+
public CodeScriptExecutionSettings CodeExecution { get; set; } = new();
8+
}
9+
10+
public class CodeScriptGenerationSettings
411
{
512
/// <summary>
613
/// Llm provider to generate code script
@@ -12,3 +19,8 @@ public class CodingSettings
1219
/// </summary>
1320
public string? Model { get; set; }
1421
}
22+
23+
public class CodeScriptExecutionSettings
24+
{
25+
public int MaxConcurrency { get; set; } = 1;
26+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@ namespace BotSharp.Core.Coding;
22

33
public class CodeScriptExecutor
44
{
5+
private readonly CodingSettings _settings;
56
private readonly ILogger<CodeScriptExecutor> _logger;
67
private readonly SemaphoreSlim _semLock = new(initialCount: 1, maxCount: 1);
78

89
public CodeScriptExecutor(
10+
CodingSettings settings,
911
ILogger<CodeScriptExecutor> logger)
1012
{
13+
_settings = settings;
1114
_logger = logger;
15+
16+
var maxConcurrency = settings.CodeExecution?.MaxConcurrency > 0 ? settings.CodeExecution.MaxConcurrency : 1;
17+
_semLock = new(initialCount: maxConcurrency, maxCount: maxConcurrency);
1218
}
1319

1420
public async Task<T> ExecuteAsync<T>(Func<Task<T>> func, CancellationToken cancellationToken = default)

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ public class CodingPlugin : IBotSharpPlugin
1010

1111
public void RegisterDI(IServiceCollection services, IConfiguration config)
1212
{
13+
var coding = new CodingSettings();
14+
config.Bind("Coding", coding);
15+
services.AddSingleton(provider => coding);
16+
1317
services.AddSingleton<CodeScriptExecutor>();
1418
}
15-
}
19+
}

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,17 +255,24 @@ await hook.OnResponseGenerated(new InstructResponseModel
255255
}
256256

257257
// Run code script
258+
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
258259
var codeResponse = await codeProcessor.RunAsync(context.ScriptContent, options: new()
259260
{
260261
ScriptName = scriptName,
261-
Arguments = context.Arguments
262+
Arguments = context.Arguments,
263+
CancellationToken = cts.Token
262264
});
263265

266+
if (codeResponse == null || !codeResponse.Success)
267+
{
268+
return response;
269+
}
270+
264271
response = new InstructResult
265272
{
266273
MessageId = message.MessageId,
267274
Template = scriptName,
268-
Text = codeResponse?.Result ?? codeResponse?.ErrorMsg
275+
Text = codeResponse.Result
269276
};
270277

271278
if (context?.Arguments != null)
@@ -285,7 +292,7 @@ await hook.OnResponseGenerated(new InstructResponseModel
285292
Model = string.Empty,
286293
TemplateName = scriptName,
287294
UserMessage = message.Content,
288-
SystemInstruction = $"Code script name: {codeScript.Name}, Version: {codeScript.UpdatedTime.ToString("o")}",
295+
SystemInstruction = $"Code script name: {codeScript}, Version: {codeScript.UpdatedTime.ToString("o")}",
289296
CompletionText = response.Text
290297
});
291298
}

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

Lines changed: 140 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using Microsoft.Extensions.Logging;
22
using Python.Runtime;
3+
using System.Diagnostics;
4+
using System.Text;
35
using System.Threading;
46
using System.Threading.Tasks;
57

@@ -10,18 +12,18 @@ public class PyCodeInterpreter : ICodeProcessor
1012
private readonly IServiceProvider _services;
1113
private readonly ILogger<PyCodeInterpreter> _logger;
1214
private readonly CodeScriptExecutor _executor;
13-
private readonly AgentSettings _agentSettings;
15+
private readonly CodingSettings _settings;
1416

1517
public PyCodeInterpreter(
1618
IServiceProvider services,
1719
ILogger<PyCodeInterpreter> logger,
1820
CodeScriptExecutor executor,
19-
AgentSettings agentSettings)
21+
CodingSettings settings)
2022
{
2123
_services = services;
2224
_logger = logger;
2325
_executor = executor;
24-
_agentSettings = agentSettings;
26+
_settings = settings;
2527
}
2628

2729
public string Provider => BuiltInCodeProcessor.PyInterpreter;
@@ -32,10 +34,11 @@ public async Task<CodeInterpretResponse> RunAsync(string codeScript, CodeInterpr
3234
{
3335
return await _executor.ExecuteAsync(async () =>
3436
{
35-
return InnerRunCode(codeScript, options);
37+
return await InnerRunCode(codeScript, options);
3638
}, cancellationToken: options?.CancellationToken ?? CancellationToken.None);
3739
}
38-
return InnerRunCode(codeScript, options);
40+
41+
return await InnerRunCode(codeScript, options);
3942
}
4043

4144
public async Task<CodeGenerationResult> GenerateCodeScriptAsync(string text, CodeGenerationOptions? options = null)
@@ -91,30 +94,55 @@ public async Task<CodeGenerationResult> GenerateCodeScriptAsync(string text, Cod
9194
};
9295
}
9396

97+
9498
#region Private methods
95-
private CodeInterpretResponse InnerRunCode(string codeScript, CodeInterpretOptions? options = null)
99+
private async Task<CodeInterpretResponse> InnerRunCode(string codeScript, CodeInterpretOptions? options = null)
96100
{
101+
var response = new CodeInterpretResponse();
102+
var scriptName = options?.ScriptName ?? codeScript.SubstringMax(30);
103+
97104
try
98105
{
99-
return CoreRun(codeScript, options);
106+
_logger.LogWarning($"Begin running python code script in {Provider}: {scriptName}");
107+
108+
if (options?.UseProcess == true)
109+
{
110+
response = await CoreRunProcess(codeScript, options);
111+
}
112+
else
113+
{
114+
response = await CoreRunScript(codeScript, options);
115+
}
116+
117+
_logger.LogWarning($"End running python code script in {Provider}: {scriptName}");
118+
119+
return response;
120+
}
121+
catch (OperationCanceledException oce)
122+
{
123+
_logger.LogError(oce, $"Operation cancelled in {nameof(InnerRunCode)} in {Provider}.");
124+
response.ErrorMsg = oce.Message;
125+
return response;
100126
}
101127
catch (Exception ex)
102128
{
103-
var errorMsg = $"Error when executing inner python code in {nameof(PyCodeInterpreter)}: {Provider}.";
104-
_logger.LogError(ex, errorMsg);
105-
106-
return new CodeInterpretResponse
107-
{
108-
Success = false,
109-
ErrorMsg = errorMsg
110-
};
129+
_logger.LogError(ex, $"Error when executing code script ({scriptName}) in {nameof(InnerRunCode)} in {Provider}.");
130+
response.ErrorMsg = ex.Message;
131+
return response;
111132
}
112133
}
113134

114-
private CodeInterpretResponse CoreRun(string codeScript, CodeInterpretOptions? options = null)
135+
private async Task<CodeInterpretResponse> CoreRunScript(string codeScript, CodeInterpretOptions? options = null)
115136
{
137+
_logger.LogWarning($"Begin {nameof(CoreRunScript)} in {Provider}: ${options?.ScriptName}");
138+
139+
var token = options?.CancellationToken ?? CancellationToken.None;
140+
token.ThrowIfCancellationRequested();
141+
116142
using (Py.GIL())
117143
{
144+
token.ThrowIfCancellationRequested();
145+
118146
// Import necessary Python modules
119147
dynamic sys = Py.Import("sys");
120148
dynamic io = Py.Import("io");
@@ -139,7 +167,7 @@ private CodeInterpretResponse CoreRun(string codeScript, CodeInterpretOptions? o
139167
{
140168
list.Append(new PyString(options?.ScriptName ?? "script.py"));
141169

142-
foreach (var arg in options.Arguments)
170+
foreach (var arg in options!.Arguments)
143171
{
144172
if (!string.IsNullOrWhiteSpace(arg.Key) && !string.IsNullOrWhiteSpace(arg.Value))
145173
{
@@ -150,28 +178,26 @@ private CodeInterpretResponse CoreRun(string codeScript, CodeInterpretOptions? o
150178
}
151179
sys.argv = list;
152180

181+
token.ThrowIfCancellationRequested();
182+
153183
// Execute Python script
154184
PythonEngine.Exec(codeScript, globals);
155185

156186
// Get result
157187
var result = stringIO.getvalue()?.ToString() as string;
158188

189+
token.ThrowIfCancellationRequested();
190+
159191
return new CodeInterpretResponse
160192
{
161-
Result = result?.TrimEnd('\r', '\n'),
193+
Result = result?.TrimEnd('\r', '\n') ?? string.Empty,
162194
Success = true
163195
};
164196
}
165197
catch (Exception ex)
166198
{
167-
var errorMsg = $"Error when executing core python code in {nameof(PyCodeInterpreter)}: {Provider}. {ex.Message}";
168-
_logger.LogError(ex, errorMsg);
169-
170-
return new CodeInterpretResponse
171-
{
172-
Success = false,
173-
ErrorMsg = errorMsg
174-
};
199+
_logger.LogError(ex, $"Error in {nameof(CoreRunScript)} in {Provider}.");
200+
throw;
175201
}
176202
finally
177203
{
@@ -180,13 +206,99 @@ private CodeInterpretResponse CoreRun(string codeScript, CodeInterpretOptions? o
180206
sys.stderr = sys.__stderr__;
181207
sys.argv = new PyList();
182208
}
209+
};
210+
}
211+
212+
213+
private async Task<CodeInterpretResponse> CoreRunProcess(string codeScript, CodeInterpretOptions? options = null)
214+
{
215+
var token = options?.CancellationToken ?? CancellationToken.None;
216+
217+
var psi = new ProcessStartInfo
218+
{
219+
FileName = "python",
220+
UseShellExecute = false,
221+
RedirectStandardOutput = true,
222+
RedirectStandardError = true,
223+
CreateNoWindow = true,
224+
StandardOutputEncoding = Encoding.UTF8,
225+
StandardErrorEncoding = Encoding.UTF8
226+
};
227+
228+
// Add raw code script
229+
psi.ArgumentList.Add("-c");
230+
psi.ArgumentList.Add(codeScript);
231+
232+
// Add arguments (safe—no shared state)
233+
if (options?.Arguments?.Any() == true)
234+
{
235+
foreach (var arg in options.Arguments!)
236+
{
237+
if (!string.IsNullOrWhiteSpace(arg.Key) && !string.IsNullOrWhiteSpace(arg.Value))
238+
{
239+
psi.ArgumentList.Add($"--{arg.Key}");
240+
psi.ArgumentList.Add($"{arg.Value}");
241+
}
242+
}
243+
}
244+
245+
using var proc = new Process { StartInfo = psi, EnableRaisingEvents = true };
246+
if (!proc.Start())
247+
{
248+
throw new InvalidOperationException($"Failed to start Python process in {Provider}.");
249+
}
250+
251+
try
252+
{
253+
using var reg = token.Register(() =>
254+
{
255+
try
256+
{
257+
if (!proc.HasExited)
258+
{
259+
proc.Kill(entireProcessTree: true);
260+
}
261+
}
262+
catch { }
263+
});
264+
265+
var stdoutTask = proc.StandardOutput.ReadToEndAsync(token);
266+
var stderrTask = proc.StandardError.ReadToEndAsync(token);
267+
268+
await Task.WhenAll([proc.WaitForExitAsync(token), stdoutTask, stderrTask]);
269+
270+
token.ThrowIfCancellationRequested();
271+
272+
return new CodeInterpretResponse
273+
{
274+
Success = proc.ExitCode == 0,
275+
Result = stdoutTask.Result?.TrimEnd('\r', '\n') ?? string.Empty,
276+
ErrorMsg = stderrTask.Result
277+
};
278+
}
279+
catch (Exception ex)
280+
{
281+
_logger.LogError(ex, $"Error in {nameof(CoreRunProcess)} in {Provider}.");
282+
throw;
283+
}
284+
finally
285+
{
286+
try
287+
{
288+
if (!proc.HasExited)
289+
{
290+
proc.Kill(entireProcessTree: true);
291+
proc.WaitForExit();
292+
}
293+
}
294+
catch { }
183295
}
184296
}
185297

186298
private (string, string) GetLlmProviderModel()
187299
{
188-
var provider = _agentSettings.Coding?.Provider;
189-
var model = _agentSettings.Coding?.Model;
300+
var provider = _settings.CodeGeneration?.Provider;
301+
var model = _settings.CodeGeneration?.Model;
190302

191303
if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(model))
192304
{

0 commit comments

Comments
 (0)