11using Microsoft . Extensions . Logging ;
22using Python . Runtime ;
3+ using System . Diagnostics ;
4+ using System . Text ;
35using System . Threading ;
46using 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