fix(codex): avoid re-exposing image tool on vision turns

This commit is contained in:
zhulijin1991
2026-04-12 08:16:03 +08:00
committed by Peter Steinberger
parent 22bff819ab
commit 15258921ee
2 changed files with 45 additions and 5 deletions

View File

@@ -365,10 +365,14 @@ async function buildDynamicTools(input: DynamicToolBuildParams) {
input.runAbortController.abort("sessions_yield");
},
});
const visionFilteredTools = filterToolsForVisionInputs(allTools, {
modelHasVision,
hasInboundImages: (params.images?.length ?? 0) > 0,
});
const filteredTools =
params.toolsAllow && params.toolsAllow.length > 0
? allTools.filter((tool) => params.toolsAllow?.includes(tool.name))
: allTools;
? visionFilteredTools.filter((tool) => params.toolsAllow?.includes(tool.name))
: visionFilteredTools;
return normalizeProviderToolSchemas({
tools: filteredTools,
provider: params.provider,
@@ -381,6 +385,19 @@ async function buildDynamicTools(input: DynamicToolBuildParams) {
});
}
function filterToolsForVisionInputs<T extends { name?: string }>(
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<T>(params: {
timeoutMs: number;
timeoutFloorMs?: number;
@@ -495,6 +512,9 @@ function handleApprovalRequest(params: {
});
}
export const __testing = createCodexAppServerClientFactoryTestHooks((factory) => {
clientFactory = factory;
});
export const __testing = {
filterToolsForVisionInputs,
...createCodexAppServerClientFactoryTestHooks((factory) => {
clientFactory = factory;
}),
} as const;

View File

@@ -0,0 +1,20 @@
import { describe, expect, it } from "vitest";
import { __testing } from "./run-attempt.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" }],
{
modelHasVision: true,
hasInboundImages: true,
},
)
.map((tool) => tool.name);
expect(toolNames).toContain("read");
expect(toolNames).toContain("write");
expect(toolNames).not.toContain("image");
});
});