run_code_rmcp/python_runner/
python_runner.rs1use crate::{
3 cache::CodeFileCache,
4 model::{
5 CodeExecutor, CodeScriptExecutionResult, CommandExecutor, LanguageScript, RunCode,
6 TokioHeapSize,
7 },
8 python_runner::parse_import,
9};
10use anyhow::{Context, Result};
11use log::{debug, error, info, warn};
12use serde_json;
13use tokio::process::Command;
14
15#[derive(Default)]
16pub struct PythonRunner;
17
18impl RunCode for PythonRunner {
19 async fn run_with_params(
20 &self,
21 code: &str,
22 params: Option<serde_json::Value>,
23 timeout_seconds: Option<u64>,
24 ) -> Result<CodeScriptExecutionResult> {
25 debug!("开始执行Python脚本...,执行参数: {:?}", params);
26 let hash = CodeFileCache::obtain_code_hash(code);
28 let cache_exist =
29 CodeFileCache::check_code_file_cache_exisht(&hash, &LanguageScript::Python).await;
30
31 let run_code_script_file_tuple = if cache_exist {
32 let cache_code =
34 CodeFileCache::get_code_file_cache(&hash, &LanguageScript::Python).await;
35 debug!("从缓存中读取代码:hash值 {:?}", &hash);
36 cache_code?
37 } else {
38 let dependencies = parse_import(code)?;
40 let wrapped_code = self.prepare_python_code(code, true);
42 CodeFileCache::save_code_file_cache(&hash, &wrapped_code, &LanguageScript::Python)
44 .await?;
45
46 let code_script_file_tuple =
48 CodeFileCache::get_code_file_cache(&hash, &LanguageScript::Python).await?;
49 let run_code_script_file_path = code_script_file_tuple.1.clone();
50 if !dependencies.is_empty() {
55 info!("正在添加依赖: {:?}", dependencies);
56 let mut cmd = Command::new("uv");
57 cmd.arg("add")
58 .arg("--script")
59 .arg(&run_code_script_file_path);
60
61 for dep in &dependencies {
63 cmd.arg(dep);
64 }
65 let cmd_str = format!("{:?}", &cmd);
67 info!("uv命令字符串: {}", cmd_str);
68
69 let cmd_output = match cmd.kill_on_drop(true).output().await {
70 Ok(output) => output,
71 Err(e) => {
72 error!("安装Python依赖失败: {:?}", e);
73 error!("失败的命令: {:?}", cmd);
74 return Err(e).context("Failed to add dependencies with uv");
75 }
76 };
77
78 let stdout = String::from_utf8_lossy(&cmd_output.stdout).to_string();
79 let stderr = String::from_utf8_lossy(&cmd_output.stderr).to_string();
80 info!("添加依赖结果 - stdout: {}", stdout);
81 info!("添加依赖结果 - stderr: {}", stderr);
82
83 if !cmd_output.status.success() {
84 warn!("添加依赖失败,状态码: {}", cmd_output.status);
85 }
86 }
87 debug!("创建脚本缓存:hash值 {:?}", &hash);
88 code_script_file_tuple
89 };
90
91 let temp_path = run_code_script_file_tuple.1;
92
93 let params_json = match params {
95 Some(p) => serde_json::to_string(&p)?,
96 None => "{}".to_string(),
97 };
98
99 let mut execute_command = Command::new("uv");
101 execute_command
102 .arg("run")
103 .arg("-s") .arg("-p")
105 .arg("3.13") .env("INPUT_JSON", ¶ms_json) .arg(&temp_path)
108 .kill_on_drop(true);
109
110 info!("执行命令: {:?}", &execute_command);
111
112 let executor = match timeout_seconds {
120 Some(timeout) => CommandExecutor::with_timeout(execute_command.output(), timeout),
121 None => CommandExecutor::default(execute_command.output()),
122 };
123
124 let executor_result = executor.await;
125 let output = match executor_result {
126 Ok(cmd_result) => match cmd_result {
127 Ok(output) => output,
128 Err(e) => {
129 error!("Python命令执行失败: {:?}", e);
130 return Err(e.into());
131 }
132 },
133 Err(e) => {
134 error!("Python任务执行异常: {:?}", e);
135 return Err(e.into());
136 }
137 };
138 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
140 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
141 debug!("Python stdout: {}", stdout);
142 debug!("Python stderr: {}", stderr);
143
144 CodeExecutor::parse_execution_output(&output.stdout, &output.stderr).await
146 }
147}
148
149impl PythonRunner {
150 fn prepare_python_code(&self, code: &str, show_logs: bool) -> String {
152 let show_logs_value = if show_logs { "True" } else { "False" };
153
154 let template = include_str!("../templates/python_template.py");
155
156 template
157 .replace("{{USER_CODE}}", code)
158 .replace("{{SHOW_LOGS}}", show_logs_value)
159 }
160}