From 913f97c9563743232fc7bd9dc609827cbe365206 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 24 Apr 2026 03:41:55 +0100 Subject: [PATCH] perf: lazy codex app server test imports --- .../codex/media-understanding-provider.ts | 29 ++++++++++--------- .../codex/src/app-server/client-factory.ts | 6 ++-- extensions/codex/src/app-server/models.ts | 6 ++-- .../codex/src/app-server/run-attempt.ts | 22 ++++---------- .../run-attempt.vision-tools.test.ts | 15 +++++----- .../codex/src/app-server/vision-tools.ts | 12 ++++++++ 6 files changed, 47 insertions(+), 43 deletions(-) create mode 100644 extensions/codex/src/app-server/vision-tools.ts diff --git a/extensions/codex/media-understanding-provider.ts b/extensions/codex/media-understanding-provider.ts index 41661601c0a..0c7a90369fb 100644 --- a/extensions/codex/media-understanding-provider.ts +++ b/extensions/codex/media-understanding-provider.ts @@ -8,6 +8,12 @@ import { type CodexAppServerClientFactory } from "./src/app-server/client-factor import type { CodexAppServerClient } from "./src/app-server/client.js"; import { resolveCodexAppServerRuntimeOptions } from "./src/app-server/config.js"; import { readModelListResult } from "./src/app-server/models.js"; +import { + assertCodexThreadStartResponse, + assertCodexTurnStartResponse, + readCodexErrorNotification, + readCodexTurnCompletedNotification, +} from "./src/app-server/protocol-validators.js"; import { isJsonObject, type CodexServerNotification, @@ -17,13 +23,6 @@ import { type CodexTurnStartParams, type JsonObject, } from "./src/app-server/protocol.js"; -import { - assertCodexThreadStartResponse, - assertCodexTurnStartResponse, - readCodexErrorNotification, - readCodexTurnCompletedNotification, -} from "./src/app-server/protocol-validators.js"; -import { createIsolatedCodexAppServerClient } from "./src/app-server/shared-client.js"; const DEFAULT_CODEX_IMAGE_MODEL = FALLBACK_CODEX_MODELS.find((model) => model.inputModalities.includes("image"))?.id ?? @@ -83,11 +82,14 @@ async function describeCodexImages( const ownsClient = !options.clientFactory; const client = options.clientFactory ? await options.clientFactory(appServer.start, req.profile) - : await createIsolatedCodexAppServerClient({ - startOptions: appServer.start, - timeoutMs, - authProfileId: req.profile, - }); + : await import("./src/app-server/shared-client.js").then( + ({ createIsolatedCodexAppServerClient }) => + createIsolatedCodexAppServerClient({ + startOptions: appServer.start, + timeoutMs, + authProfileId: req.profile, + }), + ); const abortController = new AbortController(); const timeout = setTimeout(() => abortController.abort("timeout"), timeoutMs); timeout.unref?.(); @@ -229,7 +231,8 @@ function createCodexImageTurnCollector(threadId: string) { return; } if (notification.method === "turn/completed") { - completedTurn = readCodexTurnCompletedNotification(notification.params)?.turn ?? completedTurn; + completedTurn = + readCodexTurnCompletedNotification(notification.params)?.turn ?? completedTurn; resolveCompletion?.(); return; } diff --git a/extensions/codex/src/app-server/client-factory.ts b/extensions/codex/src/app-server/client-factory.ts index a28b2ad275f..6a15975f278 100644 --- a/extensions/codex/src/app-server/client-factory.ts +++ b/extensions/codex/src/app-server/client-factory.ts @@ -1,6 +1,5 @@ import type { CodexAppServerClient } from "./client.js"; import type { CodexAppServerStartOptions } from "./config.js"; -import { getSharedCodexAppServerClient } from "./shared-client.js"; export type CodexAppServerClientFactory = ( startOptions?: CodexAppServerStartOptions, @@ -10,7 +9,10 @@ export type CodexAppServerClientFactory = ( export const defaultCodexAppServerClientFactory: CodexAppServerClientFactory = ( startOptions, authProfileId, -) => getSharedCodexAppServerClient({ startOptions, authProfileId }); +) => + import("./shared-client.js").then(({ getSharedCodexAppServerClient }) => + getSharedCodexAppServerClient({ startOptions, authProfileId }), + ); export function createCodexAppServerClientFactoryTestHooks( setFactory: (factory: CodexAppServerClientFactory) => void, diff --git a/extensions/codex/src/app-server/models.ts b/extensions/codex/src/app-server/models.ts index decd4f2ca3c..2dcc00e9803 100644 --- a/extensions/codex/src/app-server/models.ts +++ b/extensions/codex/src/app-server/models.ts @@ -1,10 +1,6 @@ import type { CodexAppServerStartOptions } from "./config.js"; import type { v2 } from "./protocol-generated/typescript/index.js"; import { readCodexModelListResponse } from "./protocol-validators.js"; -import { - createIsolatedCodexAppServerClient, - getSharedCodexAppServerClient, -} from "./shared-client.js"; export type CodexAppServerModel = { id: string; @@ -38,6 +34,8 @@ export async function listCodexAppServerModels( ): Promise { const timeoutMs = options.timeoutMs ?? 2500; const useSharedClient = options.sharedClient !== false; + const { createIsolatedCodexAppServerClient, getSharedCodexAppServerClient } = + await import("./shared-client.js"); const client = useSharedClient ? await getSharedCodexAppServerClient({ startOptions: options.startOptions, diff --git a/extensions/codex/src/app-server/run-attempt.ts b/extensions/codex/src/app-server/run-attempt.ts index cbdbf419a67..1f021f8b509 100644 --- a/extensions/codex/src/app-server/run-attempt.ts +++ b/extensions/codex/src/app-server/run-attempt.ts @@ -32,6 +32,10 @@ import { resolveCodexAppServerRuntimeOptions } from "./config.js"; import { createCodexDynamicToolBridge } from "./dynamic-tools.js"; import { handleCodexAppServerElicitationRequest } from "./elicitation-bridge.js"; import { CodexAppServerEventProjector } from "./event-projector.js"; +import { + assertCodexTurnStartResponse, + readCodexDynamicToolCallParams, +} from "./protocol-validators.js"; import { isJsonObject, type CodexServerNotification, @@ -40,10 +44,6 @@ import { type JsonObject, type JsonValue, } from "./protocol.js"; -import { - assertCodexTurnStartResponse, - readCodexDynamicToolCallParams, -} from "./protocol-validators.js"; import { readCodexAppServerBinding, type CodexAppServerThreadBinding } from "./session-binding.js"; import { clearSharedCodexAppServerClient } from "./shared-client.js"; import { @@ -58,6 +58,7 @@ import { recordCodexTrajectoryContext, } from "./trajectory.js"; import { mirrorCodexAppServerTranscript } from "./transcript-mirror.js"; +import { filterToolsForVisionInputs } from "./vision-tools.js"; let clientFactory = defaultCodexAppServerClientFactory; @@ -601,19 +602,6 @@ async function buildDynamicTools(input: DynamicToolBuildParams) { }); } -function filterToolsForVisionInputs( - tools: T[], - params: { - modelHasVision: boolean; - hasInboundImages: boolean; - }, -): T[] { - if (!params.modelHasVision || !params.hasInboundImages) { - return tools; - } - return tools.filter((tool) => tool.name !== "image"); -} - async function withCodexStartupTimeout(params: { timeoutMs: number; timeoutFloorMs?: number; diff --git a/extensions/codex/src/app-server/run-attempt.vision-tools.test.ts b/extensions/codex/src/app-server/run-attempt.vision-tools.test.ts index 8f3c1240aff..0b3a9791bee 100644 --- a/extensions/codex/src/app-server/run-attempt.vision-tools.test.ts +++ b/extensions/codex/src/app-server/run-attempt.vision-tools.test.ts @@ -1,14 +1,15 @@ import { describe, expect, it } from "vitest"; -import { __testing } from "./run-attempt.js"; +import { filterToolsForVisionInputs } from "./vision-tools.js"; describe("Codex dynamic tool filtering", () => { it("drops the image tool when the model already has inbound vision input", () => { - const toolNames = __testing - .filterToolsForVisionInputs([{ name: "image" }, { name: "read" }, { name: "write" }], { + const toolNames = filterToolsForVisionInputs( + [{ name: "image" }, { name: "read" }, { name: "write" }], + { modelHasVision: true, hasInboundImages: true, - }) - .map((tool) => tool.name); + }, + ).map((tool) => tool.name); expect(toolNames).toContain("read"); expect(toolNames).toContain("write"); @@ -19,13 +20,13 @@ describe("Codex dynamic tool filtering", () => { const tools = [{ name: "image" }, { name: "read" }]; expect( - __testing.filterToolsForVisionInputs(tools, { + filterToolsForVisionInputs(tools, { modelHasVision: false, hasInboundImages: true, }), ).toBe(tools); expect( - __testing.filterToolsForVisionInputs(tools, { + filterToolsForVisionInputs(tools, { modelHasVision: true, hasInboundImages: false, }), diff --git a/extensions/codex/src/app-server/vision-tools.ts b/extensions/codex/src/app-server/vision-tools.ts new file mode 100644 index 00000000000..2729dfa213d --- /dev/null +++ b/extensions/codex/src/app-server/vision-tools.ts @@ -0,0 +1,12 @@ +export function filterToolsForVisionInputs( + tools: T[], + params: { + modelHasVision: boolean; + hasInboundImages: boolean; + }, +): T[] { + if (!params.modelHasVision || !params.hasInboundImages) { + return tools; + } + return tools.filter((tool) => tool.name !== "image"); +}