Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions open-sse/executors/codex.js
Original file line number Diff line number Diff line change
Expand Up @@ -414,17 +414,28 @@ export class CodexExecutor extends BaseExecutor {
}

// Priority: explicit reasoning.effort > reasoning_effort param > model suffix > default (medium)
if (!body.reasoning) {
const effort = body.reasoning_effort || modelEffort || 'low';
body.reasoning = { effort, summary: "auto" };
} else if (!body.reasoning.summary) {
body.reasoning.summary = "auto";
if (!body.reasoning || typeof body.reasoning !== 'object' || Array.isArray(body.reasoning)) {
const effort = body.reasoning_effort || modelEffort || 'medium';
body.reasoning = { effort, summary: 'auto' };
} else {
if (!body.reasoning.effort) {
body.reasoning.effort = body.reasoning_effort || modelEffort || 'medium';
}
if (!body.reasoning.summary) {
body.reasoning.summary = 'auto';
}
}
delete body.reasoning_effort;

// Include reasoning encrypted content (required by Codex backend for reasoning models)
const include = Array.isArray(body.include) ? body.include : [];
if (body.reasoning && body.reasoning.effort && body.reasoning.effort !== 'none') {
body.include = ["reasoning.encrypted_content"];
body.include = include.includes("reasoning.encrypted_content")
? include
: [...include, "reasoning.encrypted_content"];
} else if (include.length > 0) {
body.include = include.filter(item => item !== "reasoning.encrypted_content");
if (body.include.length === 0) delete body.include;
}

// Remove unsupported parameters for Codex API
Expand Down
1 change: 1 addition & 0 deletions open-sse/translator/request/openai-responses.js
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ export function openaiToOpenAIResponsesRequest(model, body, stream, credentials)
if (body.temperature !== undefined) result.temperature = body.temperature;
if (body.max_tokens !== undefined) result.max_tokens = body.max_tokens;
if (body.top_p !== undefined) result.top_p = body.top_p;
if (body.reasoning_effort !== undefined) result.reasoning_effort = body.reasoning_effort;

return result;
}
Expand Down
154 changes: 154 additions & 0 deletions tests/unit/codex-request-transform.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { describe, expect, it } from "vitest";
import { CodexExecutor } from "../../open-sse/executors/codex.js";
import { openaiToOpenAIResponsesRequest } from "../../open-sse/translator/request/openai-responses.js";

function makeBody(overrides = {}) {
return {
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] }],
...overrides,
};
}

describe("CodexExecutor request transform", () => {
it("defaults base Codex models to medium effort", () => {
const executor = new CodexExecutor();
const out = executor.transformRequest("gpt-5.3-codex", makeBody({ model: "gpt-5.3-codex" }), true, {});

expect(out.model).toBe("gpt-5.3-codex");
expect(out.reasoning).toEqual({ effort: "medium", summary: "auto" });
expect(out.include).toEqual(["reasoning.encrypted_content"]);
expect(out.reasoning_effort).toBeUndefined();
});

it("uses model effort suffix and strips suffix before upstream call", () => {
const executor = new CodexExecutor();
const out = executor.transformRequest("gpt-5.3-codex-high", makeBody({ model: "gpt-5.3-codex-high" }), true, {});

expect(out.model).toBe("gpt-5.3-codex");
expect(out.reasoning).toEqual({ effort: "high", summary: "auto" });
});

it("keeps explicit reasoning.effort over model suffix", () => {
const executor = new CodexExecutor();
const out = executor.transformRequest(
"gpt-5.3-codex-high",
makeBody({ model: "gpt-5.3-codex-high", reasoning: { effort: "low" } }),
true,
{},
);

expect(out.model).toBe("gpt-5.3-codex");
expect(out.reasoning).toEqual({ effort: "low", summary: "auto" });
});

it("applies reasoning_effort when reasoning object lacks effort", () => {
const executor = new CodexExecutor();
const out = executor.transformRequest(
"gpt-5.3-codex",
makeBody({ model: "gpt-5.3-codex", reasoning: { summary: "detailed" }, reasoning_effort: "xhigh" }),
true,
{},
);

expect(out.reasoning).toEqual({ effort: "xhigh", summary: "detailed" });
expect(out.reasoning_effort).toBeUndefined();
});

it("preserves Chat Completions reasoning_effort through OpenAI Responses translation", () => {
const translated = openaiToOpenAIResponsesRequest(
"gpt-5.3-codex",
{
model: "gpt-5.3-codex",
messages: [{ role: "user", content: "hi" }],
reasoning_effort: "high",
},
true,
null,
);

const executor = new CodexExecutor();
const out = executor.transformRequest("gpt-5.3-codex", translated, true, {});

expect(out.reasoning).toEqual({ effort: "high", summary: "auto" });
expect(out.reasoning_effort).toBeUndefined();
});

it("does not request encrypted reasoning content when effort is none", () => {
const executor = new CodexExecutor();
const out = executor.transformRequest("gpt-5.3-codex-none", makeBody({ model: "gpt-5.3-codex-none" }), true, {});

expect(out.model).toBe("gpt-5.3-codex");
expect(out.reasoning).toEqual({ effort: "none", summary: "auto" });
expect(out.include).toBeUndefined();
});

it("removes encrypted reasoning include when effort is none", () => {
const executor = new CodexExecutor();
const out = executor.transformRequest(
"gpt-5.3-codex",
makeBody({
model: "gpt-5.3-codex",
reasoning_effort: "none",
include: ["reasoning.encrypted_content"],
}),
true,
{},
);

expect(out.reasoning).toEqual({ effort: "none", summary: "auto" });
expect(out.include).toBeUndefined();
});

it("preserves existing include values when adding encrypted reasoning content", () => {
const executor = new CodexExecutor();
const out = executor.transformRequest(
"gpt-5.3-codex",
makeBody({
model: "gpt-5.3-codex",
include: ["web_search_call.action.sources"],
}),
true,
{},
);

expect(out.include).toEqual(["web_search_call.action.sources", "reasoning.encrypted_content"]);
});

it("maps review aliases before effort suffix parsing", () => {
const executor = new CodexExecutor();
const out = executor.transformRequest(
"gpt-5.3-codex-high-review",
makeBody({ model: "gpt-5.3-codex-high-review" }),
true,
{},
);

expect(out.model).toBe("gpt-5.3-codex");
expect(out.reasoning).toEqual({ effort: "high", summary: "auto" });
});

it("strips parameters Codex backend rejects", () => {
const executor = new CodexExecutor();
const out = executor.transformRequest(
"gpt-5.3-codex",
makeBody({
model: "gpt-5.3-codex",
max_tokens: 100,
max_completion_tokens: 100,
max_output_tokens: 100,
temperature: 0.2,
stream_options: { include_usage: true },
previous_response_id: "resp_abc",
}),
true,
{},
);

expect(out.max_tokens).toBeUndefined();
expect(out.max_completion_tokens).toBeUndefined();
expect(out.max_output_tokens).toBeUndefined();
expect(out.temperature).toBeUndefined();
expect(out.stream_options).toBeUndefined();
expect(out.previous_response_id).toBeUndefined();
});
});