Stop sending strict:null in Codex/Responses tool defs
The gpt-account (Codex) turn built its function tools with strict:null. The Responses API expects strict to be a boolean or omitted; null is malformed, so the endpoint dropped the whole tool list — leaving the model with nothing to call. That is exactly the "model returned no tool calls" failure on the GPT-account path. Omit strict when null; keep the boolean otherwise. Locked with a test, alongside the Bedrock/Gemini forced-tool-choice encoding. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f7543e506d
commit
74422da4f8
2 files changed
+31
-2
No files matched your search
@@ -7,7 +7,16 @@ import { isRecord, type JsonRecord, type ProviderEventStreamMessage } from "../p
|
||||
import { parseArguments, type AgentTurnMessage, type AgentTurnResult, type ToolCall, type ToolDefinition } from "../provider-tool-turn";
|
||||
|
||||
export function responsesToolDefs(tools: ToolDefinition[], strict: boolean | null = false): unknown[] {
|
||||
return tools.map((tool) => ({ type: "function", name: tool.name, description: tool.description, parameters: tool.parameters, strict }));
|
||||
// `strict` must be a boolean or omitted. Passing `strict: null` (the Codex
|
||||
// path) is malformed and makes the endpoint drop the whole tool list, so the
|
||||
// model has nothing to call. Omit the field instead of sending null.
|
||||
return tools.map((tool) => ({
|
||||
type: "function",
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
parameters: tool.parameters,
|
||||
...(strict === null ? {} : { strict }),
|
||||
}));
|
||||
}
|
||||
|
||||
export function toResponsesInput(messages: AgentTurnMessage[]): unknown[] {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import type { JsonRecord, ProviderEventStreamMessage } from "../../src/main/services/provider-http";
|
||||
import type { AgentTurnMessage } from "../../src/main/services/provider-tool-turn";
|
||||
import { accumulateResponsesStream, parseResponsesOutput, toResponsesInput } from "../../src/main/services/provider-tool-turns/responses-format";
|
||||
import { accumulateResponsesStream, parseResponsesOutput, responsesToolDefs, toResponsesInput } from "../../src/main/services/provider-tool-turns/responses-format";
|
||||
import { parseChatChoice, toChatMessages } from "../../src/main/services/provider-tool-turns/chat-format";
|
||||
import { parseAnthropicContent, toAnthropicMessages } from "../../src/main/services/provider-tool-turns/anthropic-format";
|
||||
import { accumulateGeminiStream, antigravityRequestBody } from "../../src/main/services/provider-tool-turns/gemini-format";
|
||||
@@ -209,3 +209,23 @@ describe("credential resolver", () => {
|
||||
})();
|
||||
});
|
||||
});
|
||||
|
||||
describe("tool definitions and forced tool choice", () => {
|
||||
const tools = [{ name: "hire_agent", description: "Add a report.", parameters: { type: "object", properties: {} } }];
|
||||
|
||||
test("Responses tool defs omit strict when null, keep it when boolean", () => {
|
||||
const codex = responsesToolDefs(tools, null)[0] as Record<string, unknown>;
|
||||
expect("strict" in codex).toBe(false); // null would make the endpoint drop the tools
|
||||
expect(responsesToolDefs(tools)[0]).toMatchObject({ type: "function", name: "hire_agent", strict: false });
|
||||
expect(responsesToolDefs(tools, true)[0]).toMatchObject({ strict: true });
|
||||
});
|
||||
|
||||
test("Bedrock and Gemini encode a forced tool choice", () => {
|
||||
expect(bedrockToolConfig(tools, "required").toolChoice).toEqual({ any: {} });
|
||||
expect(bedrockToolConfig(tools).toolChoice).toEqual({ auto: {} });
|
||||
const vertex = vertexRequestBody({ model: "m", instructions: null, messages: [], tools, toolChoice: "required" });
|
||||
expect(vertex.toolConfig).toEqual({ functionCallingConfig: { mode: "ANY" } });
|
||||
const anti = antigravityRequestBody({ model: "m", projectId: "p", instructions: null, messages: [], tools, requestId: "r" });
|
||||
expect((anti.request as Record<string, unknown>).toolConfig).toEqual({ functionCallingConfig: { mode: "AUTO" } });
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user