Files
oh-my-pi/packages/coding-agent/test/core/python-prelude.test.ts
T
can1357 8b92ec937e feat: gpt-5 harmony errata fixes
- Replaced `===== ... =====` eval cell headers with `*** Begin ` / `*** End ` markers; legacy format remains renderable in HTML exports.
- Replaced hashline patch grammar with `*** Begin Patch` / `*** End Patch` envelope; old inputs without the envelope are still accepted.
- Extracted `sniffEvalLanguage` into a shared `sniff.ts` module reused by the parser and tool.
- Added `docs/ERRATA-GPT5-HARMONY.md` and `scripts/session-stats/harmony_backtest.py` documenting and backtesting the GPT-5 Harmony-header leak defect.
2026-05-10 19:52:44 +02:00

74 lines
2.6 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: `*** Begin PY\n*** Title: prelude helpers\n${code}\n*** End PY\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");
});
});