fix(codex): preserve Lite tool-use constraints
This commit is contained in:
@@ -279,14 +279,14 @@ export interface CodexLiteShapedBody {
|
||||
* `build_responses_request` with `use_responses_lite`): strips pinned image
|
||||
* detail, forces parallel tool calling off, moves tools into a leading
|
||||
* `additional_tools` developer item and the base instructions into a
|
||||
* developer message, then omits top-level `instructions`/`tools` and forces
|
||||
* `tool_choice: "auto"`. Because the rewrite removes top-level `tools`, any
|
||||
* forced hosted-tool choice (e.g. `{ type: "web_search" }`) would leave the
|
||||
* backend unable to validate the choice against a tools collection and it
|
||||
* rejects the request with HTTP 400 (#5771); codex-rs always sends
|
||||
* `tool_choice: "auto"` here. Shared by normal turns and both
|
||||
* remote-compaction paths — codex-rs routes `/responses/compact` through the
|
||||
* same builder.
|
||||
* developer message, then omits top-level `instructions`/`tools`. Because the
|
||||
* rewrite removes top-level `tools`, a forced hosted-tool choice (e.g.
|
||||
* `{ type: "web_search" }`) would leave the backend unable to validate the
|
||||
* choice against a tools collection and it rejects the request with HTTP 400
|
||||
* (#5771). Such choices must fall back to `"auto"`; explicit string constraints
|
||||
* such as `"none"` and `"required"` remain valid. Shared by normal turns and
|
||||
* both remote-compaction paths — codex-rs routes `/responses/compact` through
|
||||
* the same builder.
|
||||
*/
|
||||
export function applyCodexResponsesLiteShape(body: CodexLiteShapedBody): void {
|
||||
const input = Array.isArray(body.input) ? body.input : [];
|
||||
@@ -303,7 +303,9 @@ export function applyCodexResponsesLiteShape(body: CodexLiteShapedBody): void {
|
||||
});
|
||||
}
|
||||
body.input = [...prefix, ...input];
|
||||
body.tool_choice = "auto";
|
||||
if (body.tool_choice !== "none" && body.tool_choice !== "required") {
|
||||
body.tool_choice = "auto";
|
||||
}
|
||||
delete body.instructions;
|
||||
delete body.tools;
|
||||
}
|
||||
|
||||
@@ -344,6 +344,25 @@ describe("openai-codex Responses Lite input shaping", () => {
|
||||
expect(noTools.parallel_tool_calls).toBe(false);
|
||||
});
|
||||
|
||||
it("falls back from forced hosted tool choices without weakening explicit tool-use constraints", async () => {
|
||||
const model = createCodexModel("gpt-5.6-terra");
|
||||
const tools = [{ type: "function", name: "handoff", parameters: { type: "object" } }];
|
||||
|
||||
const forced = await transformRequestBody(
|
||||
{ model: model.id, tools, tool_choice: { type: "web_search" } },
|
||||
model,
|
||||
{ responsesLite: true },
|
||||
);
|
||||
expect(forced.tool_choice).toBe("auto");
|
||||
expect(forced.tools).toBeUndefined();
|
||||
|
||||
const disabled = await transformRequestBody({ model: model.id, tools, tool_choice: "none" }, model, {
|
||||
responsesLite: true,
|
||||
});
|
||||
expect(disabled.tool_choice).toBe("none");
|
||||
expect(disabled.tools).toBeUndefined();
|
||||
});
|
||||
|
||||
it("moves instructions and tools into input items under lite", async () => {
|
||||
const model = createCodexModel("gpt-5.6-terra");
|
||||
const tools = [{ type: "function", name: "shot", parameters: { type: "object" } }];
|
||||
|
||||
Reference in New Issue
Block a user