add package handling
This commit is contained in:
parent
a5bf44a44a
commit
ed902b0f57
|
|
@ -108,7 +108,9 @@ public class PyProgrammerFn : IFunctionCallback
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, $"Error when executing python code.");
|
var errorMsg = $"Error when executing python code.";
|
||||||
|
message.Content = $"{errorMsg} {ex.Message}";
|
||||||
|
_logger.LogError(ex, errorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BotSharp.Plugin.PythonInterpreter.Helpers;
|
||||||
|
|
||||||
|
internal static class PyPackageHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Install python packages
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packages"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
internal static async Task<PackageInstallResult> InstallPackages(List<string>? packages)
|
||||||
|
{
|
||||||
|
if (packages.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return new PackageInstallResult { Success = true };
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
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"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
var output = await process.StandardOutput.ReadToEndAsync();
|
||||||
|
var error = await process.StandardError.ReadToEndAsync();
|
||||||
|
|
||||||
|
if (process.ExitCode == 0)
|
||||||
|
{
|
||||||
|
return new PackageInstallResult { Success = true };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var errorMsg = $"Failed to install packages. Exit code: {process.ExitCode}. Error: {error}";
|
||||||
|
return new PackageInstallResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
ErrorMsg = errorMsg
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
var errorMsg = $"Exception occurred while installing packages: {ex.Message}";
|
||||||
|
return new PackageInstallResult
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
ErrorMsg = errorMsg
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get packages that are not installed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="packages"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static async Task<List<string>> GetUninstalledPackages(List<string>? packages)
|
||||||
|
{
|
||||||
|
var missingPackages = new List<string>();
|
||||||
|
|
||||||
|
if (packages.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return missingPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var installedPackages = await GetInstalledPackages();
|
||||||
|
foreach (var package in packages)
|
||||||
|
{
|
||||||
|
// Check for common package name mappings
|
||||||
|
var mappedPackageName = MapToPackageName(package);
|
||||||
|
var isInstalled = installedPackages.Any(x => x.IsEqualTo(mappedPackageName));
|
||||||
|
if (!isInstalled)
|
||||||
|
{
|
||||||
|
missingPackages.Add(mappedPackageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return missingPackages;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static async Task<List<string>> GetInstalledPackages()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "pip",
|
||||||
|
Arguments = "list --format=freeze",
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using var process = Process.Start(startInfo);
|
||||||
|
if (process == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Failed to start pip process");
|
||||||
|
}
|
||||||
|
|
||||||
|
await process.WaitForExitAsync();
|
||||||
|
var output = await process.StandardOutput.ReadToEndAsync();
|
||||||
|
|
||||||
|
if (process.ExitCode != 0)
|
||||||
|
{
|
||||||
|
var error = await process.StandardError.ReadToEndAsync();
|
||||||
|
throw new InvalidOperationException($"pip list failed with exit code {process.ExitCode}: {error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse pip list output (format: package==version)
|
||||||
|
var packages = output.Split("\n", StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(line => line.Split("==", StringSplitOptions.None)[0].Trim().ToLowerInvariant())
|
||||||
|
.Where(pkg => !string.IsNullOrEmpty(pkg))
|
||||||
|
.ToList();
|
||||||
|
return packages;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Map common import name to actual package name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="importName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static string MapToPackageName(string importName)
|
||||||
|
{
|
||||||
|
var packageMappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "cv2", "opencv-python" },
|
||||||
|
{ "PIL", "Pillow" },
|
||||||
|
{ "sklearn", "scikit-learn" },
|
||||||
|
{ "yaml", "PyYAML" },
|
||||||
|
{ "bs4", "beautifulsoup4" },
|
||||||
|
{ "dateutil", "python-dateutil" },
|
||||||
|
{ "serial", "pyserial" },
|
||||||
|
{ "psutil", "psutil" },
|
||||||
|
{ "requests", "requests" },
|
||||||
|
{ "numpy", "numpy" },
|
||||||
|
{ "pandas", "pandas" },
|
||||||
|
{ "matplotlib", "matplotlib" },
|
||||||
|
{ "scipy", "scipy" },
|
||||||
|
{ "seaborn", "seaborn" },
|
||||||
|
{ "plotly", "plotly" }
|
||||||
|
};
|
||||||
|
|
||||||
|
return packageMappings.TryGetValue(importName, out var actualName) ? actualName : importName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,5 +5,8 @@ namespace BotSharp.Plugin.PythonInterpreter.LlmContext;
|
||||||
public class LlmContextIn
|
public class LlmContextIn
|
||||||
{
|
{
|
||||||
[JsonPropertyName("user_requirement")]
|
[JsonPropertyName("user_requirement")]
|
||||||
public string UserRquirement { get; set; }
|
public string UserRquirement { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[JsonPropertyName("imported_packages")]
|
||||||
|
public List<string> ImportedPackages { get; set; } = [];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,7 @@ public class LlmContextOut
|
||||||
{
|
{
|
||||||
[JsonPropertyName("python_code")]
|
[JsonPropertyName("python_code")]
|
||||||
public string PythonCode { get; set; }
|
public string PythonCode { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("imported_packages")]
|
||||||
|
public List<string>? ImportedPackages { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace BotSharp.Plugin.PythonInterpreter.Models;
|
||||||
|
|
||||||
|
internal class PackageInstallResult
|
||||||
|
{
|
||||||
|
internal bool Success { get; set; }
|
||||||
|
internal string ErrorMsg { get; set; }
|
||||||
|
}
|
||||||
|
|
@ -28,3 +28,5 @@ global using BotSharp.Plugin.PythonInterpreter.LlmContext;
|
||||||
global using BotSharp.Plugin.PythonInterpreter.Settings;
|
global using BotSharp.Plugin.PythonInterpreter.Settings;
|
||||||
global using BotSharp.Plugin.PythonInterpreter.Functions;
|
global using BotSharp.Plugin.PythonInterpreter.Functions;
|
||||||
global using BotSharp.Plugin.PythonInterpreter.Hooks;
|
global using BotSharp.Plugin.PythonInterpreter.Hooks;
|
||||||
|
global using BotSharp.Plugin.PythonInterpreter.Models;
|
||||||
|
global using BotSharp.Plugin.PythonInterpreter.Helpers;
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,12 @@ You must strictly follow the "Hard Requirements", "Code Requirements", and "Resp
|
||||||
|
|
||||||
|
|
||||||
***** Hard Requirements *****
|
***** Hard Requirements *****
|
||||||
1. Your output python code must be well constructed inside one or multiple functions.
|
1. You must import packages at the beginning of the code. DO NOT include any import statement in the middle of the functions.
|
||||||
2. You must not include any code, explanations, or comments outside these functions.
|
2. Your output python code must be well constructed inside one or multiple functions.
|
||||||
3. Do not include explanations, comments outside code.
|
3. You must not include any code, explanations, or comments outside these functions.
|
||||||
4. You must use print() once to output the final result only. Do not print intermediate values, logs, or debug info.
|
4. Do not include explanations, comments outside code.
|
||||||
5. If randomness is required, set a fixed seed inside a function.
|
5. You must use print() once to output the final result only. Do not print intermediate values, logs, or debug info.
|
||||||
|
6. If randomness is required, set a fixed seed inside a function.
|
||||||
|
|
||||||
|
|
||||||
***** Code Requirements *****
|
***** Code Requirements *****
|
||||||
|
|
@ -26,14 +27,21 @@ You must strictly follow the "Hard Requirements", "Code Requirements", and "Resp
|
||||||
c). Except the main() function, it is preferable to generate functions that accept parameters and return values.
|
c). Except the main() function, it is preferable to generate functions that accept parameters and return values.
|
||||||
3. Error handling
|
3. Error handling
|
||||||
a). If necessary, use try/except block inside main() to catch any errors and produce a single final printed message.
|
a). If necessary, use try/except block inside main() to catch any errors and produce a single final printed message.
|
||||||
|
b). Do not apply try/except block for package import.
|
||||||
4. Output
|
4. Output
|
||||||
a). You must print the final result once in main().
|
a). You must print the final result once in main().
|
||||||
5. Compatibility
|
5. Compatibility
|
||||||
a). You must keep the code compatible with "Python {{ python_version }}"
|
a). You must keep the code compatible with "Python {{ python_version }}"
|
||||||
|
6. Package Usage
|
||||||
|
a). Prefer using Python standard library modules when possible (os, sys, json, csv, math, statistics, datetime, etc.)
|
||||||
|
b). Only use external packages (like pandas, numpy, matplotlib) when absolutely necessary for the task
|
||||||
|
c). If using external packages, provide fallback implementations using standard library when feasible
|
||||||
|
d). Always include external packages in the "imported_packages" list in your response
|
||||||
|
|
||||||
|
|
||||||
***** Response Format *****
|
***** Response Format *****
|
||||||
You must output the response in the following JSON format:
|
You must output the response in the following JSON format:
|
||||||
{
|
{
|
||||||
"python_code": "The python code that can fulfill user's request."
|
"python_code": "The python code that can fulfill user's request.",
|
||||||
|
"imported_packages": a list of strings that contains the packages that are imported in the code you generated.
|
||||||
}
|
}
|
||||||
|
|
@ -567,7 +567,10 @@
|
||||||
|
|
||||||
"PythonInterpreter": {
|
"PythonInterpreter": {
|
||||||
"InstallLocation": "C:/Users/xxx/AppData/Local/Programs/Python/Python313/python313.dll",
|
"InstallLocation": "C:/Users/xxx/AppData/Local/Programs/Python/Python313/python313.dll",
|
||||||
"PythonVersion": "3.13",
|
"PythonVersion": "3.13.3",
|
||||||
|
"AutoInstallPackages": false,
|
||||||
|
"AllowedPackages": [ "pandas", "numpy", "matplotlib", "requests", "beautifulsoup4", "scipy", "seaborn", "plotly" ],
|
||||||
|
"EnableFallbackGeneration": true,
|
||||||
"CodeGeneration": {
|
"CodeGeneration": {
|
||||||
"LlmProvider": "openai",
|
"LlmProvider": "openai",
|
||||||
"LlmModel": "gpt-5",
|
"LlmModel": "gpt-5",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue