Improve Python package recovery with venv fallback

This commit is contained in:
ilya-bov
2026-03-05 16:51:02 +03:00
parent 5c4721ddf8
commit 8c8ea9e04b
4 changed files with 82 additions and 6 deletions

View File

@@ -391,13 +391,15 @@ function prepareExecution(params: {
cwd: string;
}): PreparedExecution {
if (params.runtime === "python") {
const pythonCommand = resolvePythonCommand(params.cwd);
const pythonLabel = path.basename(pythonCommand) === "python3" ? "python3" : pythonCommand;
return {
runtime: "python",
command: "python3",
command: pythonCommand,
args: ["-c", params.code],
cwd: params.cwd,
env: { ...process.env, PYTHONUNBUFFERED: "1" },
commandPreview: `python3 -c ${previewText(params.code)}`,
env: buildPythonEnv(params.cwd),
commandPreview: `${pythonLabel} -c ${previewText(params.code)}`,
};
}
@@ -443,6 +445,50 @@ function prepareExecution(params: {
};
}
function buildPythonEnv(cwd: string): NodeJS.ProcessEnv {
const env: NodeJS.ProcessEnv = { ...process.env, PYTHONUNBUFFERED: "1" };
const venvDir = resolveProjectVenvDir(cwd);
if (!venvDir) {
return env;
}
const currentPath = env.PATH || "";
env.VIRTUAL_ENV = venvDir;
env.PATH = [path.join(venvDir, "bin"), currentPath].filter(Boolean).join(path.delimiter);
return env;
}
function resolvePythonCommand(cwd: string): string {
const venvPython = resolveProjectVenvPython(cwd);
if (venvPython) {
return venvPython;
}
return "python3";
}
function resolveProjectVenvDir(cwd: string): string | null {
const candidates = [".venv", "venv"];
for (const name of candidates) {
const candidateDir = path.join(cwd, name);
const candidatePython = path.join(candidateDir, "bin", "python");
if (fs.existsSync(candidatePython)) {
return candidateDir;
}
}
return null;
}
function resolveProjectVenvPython(cwd: string): string | null {
const venvDir = resolveProjectVenvDir(cwd);
if (!venvDir) {
return null;
}
const pythonBin = path.join(venvDir, "bin", "python");
return fs.existsSync(pythonBin) ? pythonBin : null;
}
function startManagedExecution(params: {
prepared: PreparedExecution;
timeoutMs: number;

View File

@@ -234,6 +234,21 @@ function buildPythonPlans(params: {
},
],
});
plans.push({
manager: "pip-venv",
steps: [
{
manager: "pip-venv",
argv: ["python3", "-m", "venv", ".venv"],
cwd: params.cwd,
},
{
manager: "pip-venv",
argv: [path.join(".venv", "bin", "python"), "-m", "pip", "install", ...params.packages],
cwd: params.cwd,
},
],
});
} else if (commandExists("python")) {
plans.push({
manager: "pip",
@@ -245,6 +260,21 @@ function buildPythonPlans(params: {
},
],
});
plans.push({
manager: "pip-venv",
steps: [
{
manager: "pip-venv",
argv: ["python", "-m", "venv", ".venv"],
cwd: params.cwd,
},
{
manager: "pip-venv",
argv: [path.join(".venv", "bin", "python"), "-m", "pip", "install", ...params.packages],
cwd: params.cwd,
},
],
});
}
if (!uvPreferred && commandExists("uv")) {

View File

@@ -25,7 +25,7 @@ You are a powerful AI agent with access to tools that allow you to interact with
- Use the **code_execution** tool to run code
- Choose the appropriate runtime: `python` for data processing and scripting, `nodejs` for web/JS tasks, `terminal` for shell commands
- Always handle errors and edge cases in your code
- If Python fails with `ModuleNotFoundError`, install the missing dependency with `python3 -m pip install <package>` using `terminal`, then retry
- If Python fails with `ModuleNotFoundError`, install the missing dependency via `install_packages` (`kind=python`) and retry. If system pip is blocked (`externally-managed-environment`), use a project-local virtualenv (`python3 -m venv .venv`) and install there.
- If Node.js fails with `Cannot find module '<name>'`, install the missing package via `install_packages` (`kind=node`) or the project's package manager, then retry once
- For OS-level packages on Debian/Ubuntu, use `apt-get`/`apt` and add `sudo` only when needed and available
- For file operations, prefer dedicated file tools (`read_text_file`, `read_pdf_file`, `write_text_file`, `copy_file`) over code execution

View File

@@ -16,7 +16,7 @@ Execute code in a specified runtime environment. The code runs on the user's mac
4. **Check prerequisites** — verify packages are installed before importing
5. **Use sessions wisely** — session 0 is the default; reuse the same session to keep terminal working-directory state between calls
6. **Prefer dedicated file tools first** — use `read_text_file`, `read_pdf_file`, `write_text_file`, and `copy_file` for common file tasks; use `code_execution` only when those tools are insufficient
7. **Auto-resolve missing Python deps** — if you see `ModuleNotFoundError`, run `python3 -m pip install <package>` in `terminal`, then rerun Python code
7. **Auto-resolve missing Python deps** — if you see `ModuleNotFoundError`, call `install_packages` with `kind=python` and retry. If pip reports `externally-managed-environment`, install into a project-local virtualenv (`python3 -m venv .venv` + `.venv/bin/python -m pip install <package>`).
8. **Auto-resolve missing Node deps** — if you see `Cannot find module '<name>'`, install it via `install_packages` (`kind=node`, package `<name>`) or package manager command, then rerun Node code once
9. **Install system packages carefully** — for Debian/Ubuntu, use `apt-get`/`apt`; add `sudo` only when required and available
10. **Use background mode for long jobs** — set `background=true` or `yield_ms` and then use the `process` tool to poll/log/kill
@@ -43,7 +43,7 @@ Do not stop after first failure for these classes:
## Examples
### Install a package then use it
First execution: `python3 -m pip install requests` (runtime: terminal)
First execution: `install_packages(kind="python", packages=["requests"])`
Second execution: `import requests; r = requests.get('...'); print(r.json())` (runtime: python)
### Install a system package