74 lines
2.5 KiB
TypeScript
74 lines
2.5 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
import * as fs from "node:fs";
|
|
import * as path from "node:path";
|
|
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
|
import { EvalTool } from "@oh-my-pi/pi-coding-agent/tools/eval";
|
|
import { $which, getProjectDir } from "@oh-my-pi/pi-utils";
|
|
|
|
const resolvePythonPath = (): string | null => {
|
|
const venvPath = Bun.env.VIRTUAL_ENV;
|
|
const candidates = [venvPath, path.join(getProjectDir(), ".venv"), path.join(getProjectDir(), "venv")].filter(
|
|
Boolean,
|
|
) as string[];
|
|
for (const candidate of candidates) {
|
|
const binDir = process.platform === "win32" ? "Scripts" : "bin";
|
|
const exeName = process.platform === "win32" ? "python.exe" : "python";
|
|
const pythonCandidate = path.join(candidate, binDir, exeName);
|
|
if (fs.existsSync(pythonCandidate)) {
|
|
return pythonCandidate;
|
|
}
|
|
}
|
|
return $which("python") ?? $which("python3");
|
|
};
|
|
|
|
const pythonPath = resolvePythonPath();
|
|
const hasKernelDeps = (() => {
|
|
if (!pythonPath) return false;
|
|
const result = Bun.spawnSync(
|
|
[
|
|
pythonPath,
|
|
"-c",
|
|
"import importlib.util,sys;sys.exit(0 if importlib.util.find_spec('kernel_gateway') and importlib.util.find_spec('ipykernel') else 1)",
|
|
],
|
|
{ stdin: "ignore", stdout: "pipe", stderr: "pipe" },
|
|
);
|
|
return result.exitCode === 0;
|
|
})();
|
|
|
|
const shouldRun = Boolean(pythonPath) && hasKernelDeps;
|
|
|
|
describe.skipIf(!shouldRun)("PYTHON_PRELUDE integration", () => {
|
|
it("exposes prelude helpers via eval python backend", async () => {
|
|
const helpers = ["env", "read", "write", "append", "tree", "diff", "run", "output"];
|
|
|
|
const session = {
|
|
cwd: getProjectDir(),
|
|
hasUI: false,
|
|
getSessionFile: () => null,
|
|
getSessionSpawns: () => null,
|
|
settings: Settings.isolated({
|
|
"lsp.diagnosticsOnWrite": false,
|
|
"eval.py": true,
|
|
"python.kernelMode": "per-call",
|
|
"python.sharedGateway": true,
|
|
}),
|
|
};
|
|
const tool = new EvalTool(session);
|
|
const code = [
|
|
`helpers = ${JSON.stringify(helpers)}`,
|
|
"missing = [name for name in helpers if name not in globals() or not callable(globals()[name])]",
|
|
'print("HELPERS_OK=" + ("1" if not missing else "0"))',
|
|
"if missing:",
|
|
' print("MISSING=" + ",".join(missing))',
|
|
].join("\n");
|
|
|
|
const result = await tool.execute("tool-call-1", {
|
|
input: `===== py:"prelude helpers" =====\n${code}\n`,
|
|
});
|
|
const output = result.content.find(item => item.type === "text")?.text ?? "";
|
|
expect(output).toContain("HELPERS_OK=1");
|
|
expect(tool.description).toContain("read");
|
|
expect(tool.description).not.toContain("Documentation unavailable");
|
|
});
|
|
});
|