From 6dccdf91932cd29034d3d54d3599752b69c811e8 Mon Sep 17 00:00:00 2001 From: can1357 Date: Fri, 1 May 2026 15:27:40 +0200 Subject: [PATCH] refactor(coding-agent/eval): drop python warmup path The warmup path no longer produces prelude docs, so the cached-session warmup it implemented added no value over the create-on-first-execute path that withKernelSession already covers. Remove warmPythonEnvironment, the backend warm() hook, the eval-tool warmup loop, the createTools warmup preflight, and the forcePythonWarmup option. Simplify ExecutorBackendCallOptions into ExecutorBackendExecOptions since execute is now the only consumer. --- packages/coding-agent/DEVELOPMENT.md | 4 +- packages/coding-agent/src/eval/backend.ts | 12 +- packages/coding-agent/src/eval/py/executor.ts | 19 --- packages/coding-agent/src/eval/py/index.ts | 22 +-- packages/coding-agent/src/sdk.ts | 4 - packages/coding-agent/src/tools/eval.ts | 19 --- packages/coding-agent/src/tools/index.ts | 44 +----- .../test/agent-session-python-cleanup.test.ts | 149 +----------------- .../python-executor-owner-cleanup.test.ts | 76 --------- .../test/core/python-prelude.test.ts | 11 +- 10 files changed, 16 insertions(+), 344 deletions(-) diff --git a/packages/coding-agent/DEVELOPMENT.md b/packages/coding-agent/DEVELOPMENT.md index 9c54725ca..10e280e79 100644 --- a/packages/coding-agent/DEVELOPMENT.md +++ b/packages/coding-agent/DEVELOPMENT.md @@ -391,7 +391,7 @@ A `ToolFactory` is `(session: ToolSession) => Tool | null | Promise 1. Normalizes requested tool names (`toolNames`) and always injects `exit_plan_mode`. 2. Resolves eval backend allowance via `PI_PY` override (`getEvalBackendsFromEnv()`) or `eval.py` / `eval.js` settings. -3. Performs Python kernel preflight/warmup when applicable (`checkPythonKernelAvailability`, `warmPythonEnvironment`). +3. Performs Python kernel preflight when applicable (`checkPythonKernelAvailability`). 4. Computes effective gating (`isToolAllowed`) from settings and runtime state: - feature toggles (`find.enabled`, `grep.enabled`, etc.) - recursion guard for `task` (`task.maxRecursionDepth` vs `session.taskDepth`) @@ -777,7 +777,7 @@ This subsystem is split into two layers: - Entry points: - `executePython(code, options)` - `executePythonWithKernel(kernel, code, options)` - - warmup/session utilities (`warmPythonEnvironment`, `disposeAllKernelSessions`). + - session utilities (`disposeAllKernelSessions`, `disposeKernelSessionsByOwner`). - Manages kernel session lifecycle in `kernelSessions: Map` with: - bounded session count (`MAX_KERNEL_SESSIONS`), LRU eviction (`evictOldestSession`) - idle cleanup timer (`cleanupIdleSessions`) diff --git a/packages/coding-agent/src/eval/backend.ts b/packages/coding-agent/src/eval/backend.ts index badacbd31..80a256829 100644 --- a/packages/coding-agent/src/eval/backend.ts +++ b/packages/coding-agent/src/eval/backend.ts @@ -1,18 +1,14 @@ import type { ToolSession } from "../tools"; import type { EvalDisplayOutput, EvalLanguage } from "./types"; -/** Per-call options shared across warm and execute. */ -export interface ExecutorBackendCallOptions { +/** Per-cell execute() options. */ +export interface ExecutorBackendExecOptions { cwd: string; sessionId: string; sessionFile: string | undefined; kernelOwnerId: string | undefined; signal?: AbortSignal; session: ToolSession; -} - -/** Per-cell execute() options. */ -export interface ExecutorBackendExecOptions extends ExecutorBackendCallOptions { deadlineMs: number; reset: boolean; artifactPath: string | undefined; @@ -40,10 +36,8 @@ export interface ExecutorBackend { readonly label: string; /** Source language identifier passed to the syntax highlighter (e.g. "python", "javascript"). */ readonly highlightLang: string; - /** Cheap availability check (no full warmup). Used by fallback resolution. */ + /** Cheap availability check. Used by fallback resolution. */ isAvailable(session: ToolSession): Promise; - /** Optional pre-warm performed once per session (no-op on backends that don't need it). */ - warm?(opts: ExecutorBackendCallOptions): Promise<{ ok: boolean; reason?: string }>; /** Execute one cell. Caller invokes once per cell and aggregates results. */ execute(code: string, opts: ExecutorBackendExecOptions): Promise; } diff --git a/packages/coding-agent/src/eval/py/executor.ts b/packages/coding-agent/src/eval/py/executor.ts index fda829b50..7aedb1de6 100644 --- a/packages/coding-agent/src/eval/py/executor.ts +++ b/packages/coding-agent/src/eval/py/executor.ts @@ -453,25 +453,6 @@ async function ensureKernelAvailable( } } -export async function warmPythonEnvironment( - cwd: string, - sessionId?: string, - _useSharedGateway?: boolean, - _sessionFile?: string, - kernelOwnerId?: string, - signal?: AbortSignal, -): Promise<{ ok: boolean; reason?: string }> { - const resolvedSessionId = sessionId ?? `session:${cwd}`; - try { - await logger.time("warmPython:ensureKernelAvailable", ensureKernelAvailable, cwd, { signal }); - } catch (err: unknown) { - const reason = err instanceof Error ? err.message : String(err); - return { ok: false, reason }; - } - attachKernelOwner(resolvedSessionId, kernelOwnerId); - return { ok: true }; -} - function isResourceExhaustionError(error: unknown): boolean { const message = error instanceof Error ? error.message : String(error); return ( diff --git a/packages/coding-agent/src/eval/py/index.ts b/packages/coding-agent/src/eval/py/index.ts index 9e2caff46..8eec1a9a1 100644 --- a/packages/coding-agent/src/eval/py/index.ts +++ b/packages/coding-agent/src/eval/py/index.ts @@ -1,11 +1,6 @@ import type { ToolSession } from "../../tools"; -import type { - ExecutorBackend, - ExecutorBackendCallOptions, - ExecutorBackendExecOptions, - ExecutorBackendResult, -} from "../backend"; -import { executePython, type PythonExecutorOptions, warmPythonEnvironment } from "./executor"; +import type { ExecutorBackend, ExecutorBackendExecOptions, ExecutorBackendResult } from "../backend"; +import { executePython, type PythonExecutorOptions } from "./executor"; import { checkPythonKernelAvailability } from "./kernel"; const PYTHON_SESSION_PREFIX = "python:"; @@ -29,19 +24,6 @@ export default { return availability.ok; }, - async warm(opts: ExecutorBackendCallOptions): Promise<{ ok: boolean; reason?: string }> { - const useSharedGateway = readSetting(opts.session, "python.sharedGateway"); - const result = await warmPythonEnvironment( - opts.cwd, - namespaceSessionId(opts.sessionId), - useSharedGateway, - opts.sessionFile, - opts.kernelOwnerId, - opts.signal, - ); - return { ok: result.ok, reason: result.reason }; - }, - async execute(code: string, opts: ExecutorBackendExecOptions): Promise { const useSharedGateway = readSetting(opts.session, "python.sharedGateway"); const kernelMode = readSetting(opts.session, "python.kernelMode"); diff --git a/packages/coding-agent/src/sdk.ts b/packages/coding-agent/src/sdk.ts index 5a852e517..abfddafd6 100644 --- a/packages/coding-agent/src/sdk.ts +++ b/packages/coding-agent/src/sdk.ts @@ -206,9 +206,6 @@ export interface CreateAgentSessionOptions { enableLsp?: boolean; /** Skip Python kernel availability check and prelude warmup */ skipPythonPreflight?: boolean; - /** Force Python prelude warmup even when test env would normally skip it */ - forcePythonWarmup?: boolean; - /** Tool names explicitly requested (enables disabled-by-default tools) */ toolNames?: string[]; @@ -956,7 +953,6 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} return !requestedToolNames || requestedToolNames.includes("edit"); }, skipPythonPreflight: options.skipPythonPreflight, - forcePythonWarmup: options.forcePythonWarmup, contextFiles, skills, eventBus, diff --git a/packages/coding-agent/src/tools/eval.ts b/packages/coding-agent/src/tools/eval.ts index 0c842e07f..e6915f9ed 100644 --- a/packages/coding-agent/src/tools/eval.ts +++ b/packages/coding-agent/src/tools/eval.ts @@ -348,8 +348,6 @@ export class EvalTool implements AgentTool { }); const sessionId = sessionFile ? `session:${sessionFile}:cwd:${session.cwd}` : `cwd:${session.cwd}`; - const warmedBackends = new Set(); - for (let i = 0; i < cells.length; i++) { const cell = cells[i]; const backend = cell.resolved.backend; @@ -360,23 +358,6 @@ export class EvalTool implements AgentTool { ? AbortSignal.any([signal, timeoutSignal, sessionAbortController.signal]) : AbortSignal.any([timeoutSignal, sessionAbortController.signal]); - if (!warmedBackends.has(backend.id) && backend.warm) { - const warmup = await backend.warm({ - cwd: session.cwd, - sessionId, - sessionFile: sessionFile ?? undefined, - kernelOwnerId, - signal: combinedSignal, - session, - }); - if (!warmup.ok) { - if (combinedSignal.aborted) throw new ToolAbortError(); - throw new ToolError(warmup.reason ?? `${backend.label} prelude helpers unavailable`); - } - session.assertEvalExecutionAllowed?.(); - } - warmedBackends.add(backend.id); - const cellResult = cellResults[i]; cellResult.status = "running"; cellResult.output = ""; diff --git a/packages/coding-agent/src/tools/index.ts b/packages/coding-agent/src/tools/index.ts index 1a0b3a441..43f93ba4a 100644 --- a/packages/coding-agent/src/tools/index.ts +++ b/packages/coding-agent/src/tools/index.ts @@ -1,11 +1,10 @@ import type { AgentTool } from "@oh-my-pi/pi-agent-core"; import type { ToolChoice } from "@oh-my-pi/pi-ai"; -import { $env, $flag, isBunTestRuntime, logger } from "@oh-my-pi/pi-utils"; +import { $env, $flag, logger } from "@oh-my-pi/pi-utils"; import type { AsyncJobManager } from "../async"; import type { PromptTemplate } from "../config/prompt-templates"; import type { Settings } from "../config/settings"; import { EditTool } from "../edit"; -import { warmPythonEnvironment } from "../eval/py/executor"; import { checkPythonKernelAvailability } from "../eval/py/kernel"; import type { Skill } from "../extensibility/skills"; import type { InternalUrlRouter } from "../internal-urls"; @@ -108,8 +107,6 @@ export interface ToolSession { hasUI: boolean; /** Skip Python kernel availability check and warmup */ skipPythonPreflight?: boolean; - /** Force Python prelude warmup even when test env would normally skip it */ - forcePythonWarmup?: boolean; /** Pre-loaded context files (AGENTS.md, etc) */ contextFiles?: ContextFileEntry[]; /** Pre-loaded skills */ @@ -300,9 +297,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P let pythonAvailable = true; const shouldCheckPython = !skipPythonPreflight && allowPython && (requestedTools === undefined || requestedTools.includes("eval")); - const isTestEnv = isBunTestRuntime(); - const forcePythonWarmup = session.forcePythonWarmup === true; - const skipPythonWarm = (isTestEnv && !forcePythonWarmup) || $flag("PI_PYTHON_SKIP_CHECK"); + if (shouldCheckPython) { const availability = await logger.time("createTools:pythonCheck", checkPythonKernelAvailability, session.cwd); pythonAvailable = availability.ok; @@ -310,41 +305,6 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P logger.warn("Python kernel unavailable; eval will dispatch to JavaScript backend", { reason: availability.reason, }); - } else if (!skipPythonWarm) { - const sessionFile = session.getSessionFile?.() ?? undefined; - const kernelOwnerId = session.getEvalKernelOwnerId?.() ?? undefined; - const warmSessionId = sessionFile ? `session:${sessionFile}:cwd:${session.cwd}` : `cwd:${session.cwd}`; - const warmupAbortController = new AbortController(); - try { - session.assertEvalExecutionAllowed?.(); - - const warmupExecution = session.trackEvalExecution - ? logger.time( - "createTools:warmPython", - warmPythonEnvironment, - session.cwd, - warmSessionId, - session.settings.get("python.sharedGateway"), - sessionFile, - kernelOwnerId, - warmupAbortController.signal, - ) - : logger.time( - "createTools:warmPython", - warmPythonEnvironment, - session.cwd, - warmSessionId, - session.settings.get("python.sharedGateway"), - sessionFile, - kernelOwnerId, - ); - await (session.trackEvalExecution?.(warmupExecution, warmupAbortController) ?? warmupExecution); - session.assertEvalExecutionAllowed?.(); - } catch (err) { - logger.warn("Failed to warm Python environment", { - error: err instanceof Error ? err.message : String(err), - }); - } } } diff --git a/packages/coding-agent/test/agent-session-python-cleanup.test.ts b/packages/coding-agent/test/agent-session-python-cleanup.test.ts index 7bb449787..d0eae4e7c 100644 --- a/packages/coding-agent/test/agent-session-python-cleanup.test.ts +++ b/packages/coding-agent/test/agent-session-python-cleanup.test.ts @@ -95,18 +95,15 @@ const createSession = async ( slashCommands: [], enableMCP: false, enableLsp: false, - forcePythonWarmup: true, toolNames: ["eval"], }) ).session; -const stubPythonWarmup = () => vi.spyOn(pythonExecutor, "warmPythonEnvironment").mockResolvedValue({ ok: true }); - -const createWarmupKernel = () => { +const createMockKernel = () => { let alive = true; return { execute: vi.fn(async () => { - if (!alive) throw new Error("Expected warmup kernel to be restarted after shutdown"); + if (!alive) throw new Error("Expected mock kernel to be restarted after shutdown"); return OK_EXECUTION; }), ping: vi.fn(async () => alive), @@ -132,7 +129,7 @@ describe("AgentSession python cleanup", () => { it("does not dispose unrelated Python owners when createAgentSession fails before session construction", async () => { const { tempDir, cwd } = createTempProject(); tempDirs.push(tempDir); - const unrelatedKernel = createWarmupKernel(); + const unrelatedKernel = createMockKernel(); const unrelatedCwd = path.join(tempDir, "unrelated-before"); const throwingExtension: ExtensionFactory = () => { throw new Error("Extension init failed"); @@ -164,7 +161,6 @@ describe("AgentSession python cleanup", () => { slashCommands: [], enableMCP: false, enableLsp: false, - forcePythonWarmup: true, toolNames: ["eval"], }), ).rejects.toThrow("Extension init failed"); @@ -172,7 +168,7 @@ describe("AgentSession python cleanup", () => { expect(startSpy).toHaveBeenCalledTimes(1); expect(unrelatedKernel.shutdown).not.toHaveBeenCalled(); - const replacementKernel = createWarmupKernel(); + const replacementKernel = createMockKernel(); startSpy.mockResolvedValueOnce(replacementKernel as unknown as PythonKernelInstance); await pythonExecutor.executePython("print('fresh warmup before')", { cwd, @@ -198,7 +194,7 @@ describe("AgentSession python cleanup", () => { it("does not dispose unrelated Python owners when createAgentSession fails after session construction", async () => { const { tempDir, cwd } = createTempProject(); tempDirs.push(tempDir); - const unrelatedKernel = createWarmupKernel(); + const unrelatedKernel = createMockKernel(); const unrelatedCwd = path.join(tempDir, "unrelated-after"); vi.spyOn(pythonKernel, "checkPythonKernelAvailability").mockResolvedValue({ ok: true }); const startSpy = vi @@ -229,7 +225,6 @@ describe("AgentSession python cleanup", () => { slashCommands: [], enableMCP: false, enableLsp: false, - forcePythonWarmup: true, toolNames: ["eval"], }), ).rejects.toThrow("Memory startup failed"); @@ -237,7 +232,7 @@ describe("AgentSession python cleanup", () => { expect(startSpy).toHaveBeenCalledTimes(1); expect(unrelatedKernel.shutdown).not.toHaveBeenCalled(); - const replacementKernel = createWarmupKernel(); + const replacementKernel = createMockKernel(); startSpy.mockResolvedValueOnce(replacementKernel as unknown as PythonKernelInstance); await pythonExecutor.executePython("print('fresh warmup after')", { cwd, @@ -263,8 +258,6 @@ describe("AgentSession python cleanup", () => { it("waits for active SDK session Python work before releasing a shared retained kernel", async () => { const { tempDir, cwd } = createTempProject(); tempDirs.push(tempDir); - stubPythonWarmup(); - const kernel = new FakeKernel(); const blockedExecution = Promise.withResolvers(); const blockedExecutionStarted = Promise.withResolvers(); @@ -336,128 +329,9 @@ describe("AgentSession python cleanup", () => { expect(kernel.shutdownCalls).toBe(1); }); - - it("aborts tracked eval warmup during session dispose before executePython starts", async () => { - const { tempDir, cwd } = createTempProject(); - tempDirs.push(tempDir); - const blockedWarmupStarted = Promise.withResolvers(); - const executeSpy = vi.spyOn(pythonExecutor, "executePython").mockResolvedValue({ - output: "tool ok", - exitCode: 0, - cancelled: false, - truncated: false, - totalLines: 1, - totalBytes: 7, - outputLines: 1, - outputBytes: 7, - displayOutputs: [], - stdinRequested: false, - }); - let warmupCallCount = 0; - const warmupSpy = vi - .spyOn(pythonExecutor, "warmPythonEnvironment") - .mockImplementation(async (_cwd, _sessionId, _useSharedGateway, _sessionFile, _kernelOwnerId, signal) => { - warmupCallCount += 1; - if (warmupCallCount === 1) { - return { ok: true }; - } - blockedWarmupStarted.resolve(); - return await new Promise<{ ok: boolean; reason?: string }>(resolve => { - const onAbort = () => resolve({ ok: false, reason: "Warmup aborted" }); - if (signal?.aborted) { - onAbort(); - return; - } - signal?.addEventListener("abort", onAbort, { once: true }); - }); - }); - vi.spyOn(pythonKernel, "checkPythonKernelAvailability").mockResolvedValue({ ok: true }); - - const session = await createSession(tempDir, cwd); - const EvalTool = session.getToolByName("eval"); - expect(EvalTool).toBeDefined(); - let toolExecutionSettled = false; - const toolExecution = EvalTool! - .execute("call-id", { input: "```py\nprint('tool')\n```" }, undefined, undefined, undefined) - .finally(() => { - toolExecutionSettled = true; - }); - await blockedWarmupStarted.promise; - - let disposed = false; - const disposeSession = session.dispose().then(() => { - disposed = true; - }); - await Bun.sleep(0); - - expect(disposed).toBe(false); - expect(toolExecutionSettled).toBe(false); - expect(warmupSpy).toHaveBeenCalledTimes(2); - expect(executeSpy).not.toHaveBeenCalled(); - - await expect(toolExecution).rejects.toThrow("Operation aborted"); - await disposeSession; - - expect(disposed).toBe(true); - expect(toolExecutionSettled).toBe(true); - expect(executeSpy).not.toHaveBeenCalled(); - }); - - it("rejects eval starts when warmup finishes after dispose begins", async () => { - const { tempDir, cwd } = createTempProject(); - tempDirs.push(tempDir); - const blockedWarmupStarted = Promise.withResolvers(); - const releaseWarmup = Promise.withResolvers(); - const executeSpy = vi.spyOn(pythonExecutor, "executePython").mockResolvedValue({ - output: "late", - exitCode: 0, - cancelled: false, - truncated: false, - totalLines: 1, - totalBytes: 4, - outputLines: 1, - outputBytes: 4, - displayOutputs: [], - stdinRequested: false, - }); - let warmupCallCount = 0; - const warmupSpy = vi.spyOn(pythonExecutor, "warmPythonEnvironment").mockImplementation(async () => { - warmupCallCount += 1; - if (warmupCallCount === 1) { - return { ok: true }; - } - blockedWarmupStarted.resolve(); - await releaseWarmup.promise; - return { ok: true }; - }); - vi.spyOn(pythonKernel, "checkPythonKernelAvailability").mockResolvedValue({ ok: true }); - - const session = await createSession(tempDir, cwd); - const EvalTool = session.getToolByName("eval"); - expect(EvalTool).toBeDefined(); - const toolExecution = EvalTool!.execute( - "call-id", - { input: "```py\nprint('tool after warmup')\n```" }, - undefined, - undefined, - undefined, - ); - await blockedWarmupStarted.promise; - const disposeSession = session.dispose(); - releaseWarmup.resolve(); - await expect(toolExecution).rejects.toThrow( - "Python execution is unavailable while session disposal is in progress", - ); - await disposeSession; - expect(warmupSpy).toHaveBeenCalledTimes(2); - expect(executeSpy).not.toHaveBeenCalled(); - }); it("aborts tracked eval execution during session dispose after warmup completes", async () => { const { tempDir, cwd } = createTempProject(); tempDirs.push(tempDir); - const warmupSpy = vi.spyOn(pythonExecutor, "warmPythonEnvironment").mockImplementation(async () => { - return { ok: true }; - }); const blockedExecuteStarted = Promise.withResolvers(); const executeSpy = vi.spyOn(pythonExecutor, "executePython").mockImplementation(async (_code, options) => { const signal = options?.signal; @@ -507,14 +381,12 @@ describe("AgentSession python cleanup", () => { expect(disposed).toBe(false); expect(toolExecutionSettled).toBe(false); - expect(warmupSpy).toHaveBeenCalledTimes(2); expect(executeSpy).toHaveBeenCalledTimes(1); const [toolResult] = await Promise.all([toolExecution, disposeSession]); expect(disposed).toBe(true); expect(toolExecutionSettled).toBe(true); - expect(warmupSpy).toHaveBeenCalledTimes(2); expect(executeSpy).toHaveBeenCalledTimes(1); expect(toolResult.details?.isError).toBe(true); expect(toolResult.content).toContainEqual( @@ -525,8 +397,6 @@ describe("AgentSession python cleanup", () => { it("detaches retained kernel ownership even when dispose times out waiting for Python work", async () => { const { tempDir, cwd } = createTempProject(); tempDirs.push(tempDir); - stubPythonWarmup(); - const kernel = new FakeKernel(); const blockedExecution = Promise.withResolvers(); const blockedExecutionStarted = Promise.withResolvers(); @@ -583,7 +453,6 @@ describe("AgentSession python cleanup", () => { it("rejects direct session Python starts once dispose begins", async () => { const { tempDir, cwd } = createTempProject(); tempDirs.push(tempDir); - stubPythonWarmup(); const executeSpy = vi.spyOn(pythonExecutor, "executePython").mockResolvedValue({ output: "late", exitCode: 0, @@ -705,7 +574,6 @@ describe("AgentSession python cleanup", () => { it("rejects eval starts once dispose begins", async () => { const { tempDir, cwd } = createTempProject(); tempDirs.push(tempDir); - const warmupSpy = stubPythonWarmup(); const executeSpy = vi.spyOn(pythonExecutor, "executePython").mockResolvedValue({ output: "late", exitCode: 0, @@ -727,14 +595,12 @@ describe("AgentSession python cleanup", () => { EvalTool!.execute("call-id", { input: "```py\nprint('late')\n```" }, undefined, undefined, undefined), ).rejects.toThrow("Python execution is unavailable while session disposal is in progress"); await disposeSession; - expect(warmupSpy).toHaveBeenCalledTimes(1); expect(executeSpy).not.toHaveBeenCalled(); }); it("rejects eval starts that reach async preflight after dispose begins", async () => { const { tempDir, cwd } = createTempProject(); tempDirs.push(tempDir); - const warmupSpy = stubPythonWarmup(); const executeSpy = vi.spyOn(pythonExecutor, "executePython").mockResolvedValue({ output: "late", exitCode: 0, @@ -771,15 +637,12 @@ describe("AgentSession python cleanup", () => { releaseArtifact.resolve(); await expect(execution).rejects.toThrow("Python execution is unavailable while session disposal is in progress"); await disposeSession; - expect(warmupSpy).toHaveBeenCalledTimes(1); expect(executeSpy).not.toHaveBeenCalled(); }); it("aborts every active Python execution owned by the session during dispose", async () => { const { tempDir, cwd } = createTempProject(); tempDirs.push(tempDir); - stubPythonWarmup(); - const kernel = new FakeKernel(); const blockedExecution = Promise.withResolvers(); const blockedExecutionStarted = Promise.withResolvers(); diff --git a/packages/coding-agent/test/core/python-executor-owner-cleanup.test.ts b/packages/coding-agent/test/core/python-executor-owner-cleanup.test.ts index 963757468..1377ec0ee 100644 --- a/packages/coding-agent/test/core/python-executor-owner-cleanup.test.ts +++ b/packages/coding-agent/test/core/python-executor-owner-cleanup.test.ts @@ -3,7 +3,6 @@ import { disposeAllKernelSessions, disposeKernelSessionsByOwner, executePython, - warmPythonEnvironment, } from "@oh-my-pi/pi-coding-agent/eval/py/executor"; import * as gatewayCoordinator from "@oh-my-pi/pi-coding-agent/eval/py/gateway-coordinator"; import type { @@ -13,7 +12,6 @@ import type { } from "@oh-my-pi/pi-coding-agent/eval/py/kernel"; import * as pythonKernel from "@oh-my-pi/pi-coding-agent/eval/py/kernel"; import { PythonKernel } from "@oh-my-pi/pi-coding-agent/eval/py/kernel"; -import { TempDir } from "@oh-my-pi/pi-utils"; const OK_RESULT: KernelExecuteResult = { status: "ok", @@ -775,80 +773,6 @@ describe("python executor owner cleanup", () => { expect(globalKernel.shutdown).toHaveBeenCalledTimes(1); await globalCleanup; }); - - it("attaches cached warmup sessions to newly provided owners", async () => { - using tempDir = TempDir.createSync("@python-owner-warmup-"); - const kernel = { - execute: vi.fn(async () => OK_RESULT), - ping: vi.fn(async () => true), - isAlive: () => true, - shutdown: vi.fn(async (): Promise => ({ confirmed: true })), - }; - vi.spyOn(pythonKernel, "checkPythonKernelAvailability").mockResolvedValue({ ok: true }); - const startSpy = vi.spyOn(PythonKernel, "start").mockResolvedValue(kernel as unknown as PythonKernelInstance); - - const firstWarmup = await warmPythonEnvironment(tempDir.path(), "warm-session", true, undefined, "owner-a"); - expect(firstWarmup.ok).toBe(true); - - const cachedWarmup = await warmPythonEnvironment(tempDir.path(), "warm-session", true, undefined, "owner-b"); - expect(cachedWarmup.ok).toBe(true); - expect(startSpy).toHaveBeenCalledTimes(1); - - await disposeKernelSessionsByOwner("owner-a"); - expect(kernel.shutdown).not.toHaveBeenCalled(); - - await executePython("1 + 1", { - cwd: tempDir.path(), - sessionId: "warm-session", - kernelMode: "session", - kernelOwnerId: "owner-b", - }); - - expect(startSpy).toHaveBeenCalledTimes(1); - expect(kernel.execute).toHaveBeenCalledTimes(1); - - await disposeKernelSessionsByOwner("owner-b"); - expect(kernel.shutdown).toHaveBeenCalledTimes(1); - }); - - it("keeps cache-hit ownerless warmups provisional until an explicit owner takes over", async () => { - using tempDir = TempDir.createSync("@python-owner-fallback-"); - const kernel = { - execute: vi.fn(async () => OK_RESULT), - ping: vi.fn(async () => true), - isAlive: () => true, - shutdown: vi.fn(async (): Promise => ({ confirmed: true })), - }; - vi.spyOn(pythonKernel, "checkPythonKernelAvailability").mockResolvedValue({ ok: true }); - const startSpy = vi.spyOn(PythonKernel, "start").mockResolvedValue(kernel as unknown as PythonKernelInstance); - - const firstWarmup = await warmPythonEnvironment(tempDir.path(), "warm-fallback-session", true); - expect(firstWarmup.ok).toBe(true); - - const cachedWarmup = await warmPythonEnvironment(tempDir.path(), "warm-fallback-session", true); - expect(cachedWarmup.ok).toBe(true); - expect(startSpy).toHaveBeenCalledTimes(1); - - await executePython("1 + 1", { - cwd: tempDir.path(), - sessionId: "warm-fallback-session", - kernelMode: "session", - kernelOwnerId: "owner-a", - }); - - expect(kernel.execute).toHaveBeenCalledTimes(1); - - const postTakeoverWarmup = await warmPythonEnvironment(tempDir.path(), "warm-fallback-session", true); - expect(postTakeoverWarmup.ok).toBe(true); - expect(startSpy).toHaveBeenCalledTimes(1); - - await disposeKernelSessionsByOwner("owner-a"); - expect(kernel.shutdown).toHaveBeenCalledTimes(1); - - await disposeKernelSessionsByOwner("warm-fallback-session"); - expect(kernel.shutdown).toHaveBeenCalledTimes(1); - }); - it("leaves per-call kernels out of owner-scoped retained cleanup and keeps global cleanup intact", async () => { const perCallKernel = new FakeKernel(); const retainedKernel = new FakeKernel(); diff --git a/packages/coding-agent/test/core/python-prelude.test.ts b/packages/coding-agent/test/core/python-prelude.test.ts index 8058af26b..4082e48a9 100644 --- a/packages/coding-agent/test/core/python-prelude.test.ts +++ b/packages/coding-agent/test/core/python-prelude.test.ts @@ -2,8 +2,7 @@ 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 { warmPythonEnvironment } from "@oh-my-pi/pi-coding-agent/eval/py/executor"; -import { EvalTool, getEvalToolDescription } from "@oh-my-pi/pi-coding-agent/tools/eval"; +import { EvalTool } from "@oh-my-pi/pi-coding-agent/tools/eval"; import { $which, getProjectDir } from "@oh-my-pi/pi-utils"; const resolvePythonPath = (): string | null => { @@ -89,12 +88,4 @@ ${code} expect(tool.description).toContain("read"); expect(tool.description).not.toContain("Documentation unavailable"); }); - - it("renders prelude docs in eval tool description", async () => { - const result = await warmPythonEnvironment(getProjectDir()); - expect(result.ok).toBe(true); - const description = getEvalToolDescription(); - expect(description).toContain("read"); - expect(description).not.toContain("Documentation unavailable"); - }); });