mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
fix: update Codex executor for gpt-5.3-codex support
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
decolua
co-authored by
Cursor
parent
9c9af25acd
commit
d7d5dc90bc
@@ -39,9 +39,8 @@ export const PROVIDERS = {
|
|||||||
baseUrl: "https://chatgpt.com/backend-api/codex/responses",
|
baseUrl: "https://chatgpt.com/backend-api/codex/responses",
|
||||||
format: "openai-responses", // Use OpenAI Responses API format (reuse translator)
|
format: "openai-responses", // Use OpenAI Responses API format (reuse translator)
|
||||||
headers: {
|
headers: {
|
||||||
"Version": "0.92.0",
|
"originator": "codex-cli",
|
||||||
"Openai-Beta": "responses=experimental",
|
"User-Agent": "codex-cli/1.0.18 (macOS; arm64)"
|
||||||
"User-Agent": "codex-cli/0.92.0 (Windows 10.0.26100; x64)"
|
|
||||||
},
|
},
|
||||||
// OpenAI OAuth configuration
|
// OpenAI OAuth configuration
|
||||||
clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
|
clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
|
||||||
|
|||||||
@@ -11,10 +11,27 @@ export class CodexExecutor extends BaseExecutor {
|
|||||||
super("codex", PROVIDERS.codex);
|
super("codex", PROVIDERS.codex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override headers to add session_id per request
|
||||||
|
*/
|
||||||
|
buildHeaders(credentials, stream = true) {
|
||||||
|
const headers = super.buildHeaders(credentials, stream);
|
||||||
|
headers["session_id"] = `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform request before sending - inject default instructions if missing
|
* Transform request before sending - inject default instructions if missing
|
||||||
*/
|
*/
|
||||||
transformRequest(model, body, stream, credentials) {
|
transformRequest(model, body, stream, credentials) {
|
||||||
|
// Ensure input is present and non-empty (Codex API rejects empty input)
|
||||||
|
if (!body.input || (Array.isArray(body.input) && body.input.length === 0)) {
|
||||||
|
body.input = [{ type: "message", role: "user", content: [{ type: "input_text", text: "..." }] }];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure streaming is enabled (Codex API requires it)
|
||||||
|
body.stream = true;
|
||||||
|
|
||||||
// If no instructions provided, inject default Codex instructions
|
// If no instructions provided, inject default Codex instructions
|
||||||
if (!body.instructions || body.instructions.trim() === "") {
|
if (!body.instructions || body.instructions.trim() === "") {
|
||||||
body.instructions = CODEX_DEFAULT_INSTRUCTIONS;
|
body.instructions = CODEX_DEFAULT_INSTRUCTIONS;
|
||||||
@@ -39,10 +56,17 @@ export class CodexExecutor extends BaseExecutor {
|
|||||||
// Priority: explicit reasoning.effort > reasoning_effort param > model suffix > default (medium)
|
// Priority: explicit reasoning.effort > reasoning_effort param > model suffix > default (medium)
|
||||||
if (!body.reasoning) {
|
if (!body.reasoning) {
|
||||||
const effort = body.reasoning_effort || modelEffort || 'medium';
|
const effort = body.reasoning_effort || modelEffort || 'medium';
|
||||||
body.reasoning = { effort };
|
body.reasoning = { effort, summary: "auto" };
|
||||||
|
} else if (!body.reasoning.summary) {
|
||||||
|
body.reasoning.summary = "auto";
|
||||||
}
|
}
|
||||||
delete body.reasoning_effort;
|
delete body.reasoning_effort;
|
||||||
|
|
||||||
|
// Include reasoning encrypted content (required by Codex backend for reasoning models)
|
||||||
|
if (body.reasoning && body.reasoning.effort && body.reasoning.effort !== 'none') {
|
||||||
|
body.include = ["reasoning.encrypted_content"];
|
||||||
|
}
|
||||||
|
|
||||||
// Remove unsupported parameters for Codex API
|
// Remove unsupported parameters for Codex API
|
||||||
delete body.temperature;
|
delete body.temperature;
|
||||||
delete body.top_p;
|
delete body.top_p;
|
||||||
|
|||||||
@@ -368,7 +368,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
|||||||
id: state.chatId || `chatcmpl-${Date.now()}`,
|
id: state.chatId || `chatcmpl-${Date.now()}`,
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: state.created || Math.floor(Date.now() / 1000),
|
created: state.created || Math.floor(Date.now() / 1000),
|
||||||
model: state.model || "gpt-4",
|
model: state.model || "unknown",
|
||||||
choices: [{
|
choices: [{
|
||||||
index: 0,
|
index: 0,
|
||||||
delta: {},
|
delta: {},
|
||||||
@@ -401,7 +401,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
|||||||
id: state.chatId,
|
id: state.chatId,
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: state.created,
|
created: state.created,
|
||||||
model: state.model || "gpt-4",
|
model: state.model || "unknown",
|
||||||
choices: [{
|
choices: [{
|
||||||
index: 0,
|
index: 0,
|
||||||
delta: { content: delta },
|
delta: { content: delta },
|
||||||
@@ -424,7 +424,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
|||||||
id: state.chatId,
|
id: state.chatId,
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: state.created,
|
created: state.created,
|
||||||
model: state.model || "gpt-4",
|
model: state.model || "unknown",
|
||||||
choices: [{
|
choices: [{
|
||||||
index: 0,
|
index: 0,
|
||||||
delta: {
|
delta: {
|
||||||
@@ -452,7 +452,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
|||||||
id: state.chatId,
|
id: state.chatId,
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: state.created,
|
created: state.created,
|
||||||
model: state.model || "gpt-4",
|
model: state.model || "unknown",
|
||||||
choices: [{
|
choices: [{
|
||||||
index: 0,
|
index: 0,
|
||||||
delta: {
|
delta: {
|
||||||
@@ -511,7 +511,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
|||||||
id: state.chatId,
|
id: state.chatId,
|
||||||
object: "chat.completion.chunk",
|
object: "chat.completion.chunk",
|
||||||
created: state.created,
|
created: state.created,
|
||||||
model: state.model || "gpt-4",
|
model: state.model || "unknown",
|
||||||
choices: [{
|
choices: [{
|
||||||
index: 0,
|
index: 0,
|
||||||
delta: {},
|
delta: {},
|
||||||
@@ -529,6 +529,32 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error events from Responses API (e.g. model_not_found)
|
||||||
|
if (eventType === "error" || eventType === "response.failed") {
|
||||||
|
// Avoid emitting duplicate errors (error + response.failed arrive back-to-back)
|
||||||
|
if (state.finishReasonSent) return null;
|
||||||
|
|
||||||
|
const error = data.error || data.response?.error;
|
||||||
|
if (error) {
|
||||||
|
state.error = error;
|
||||||
|
state.finishReasonSent = true;
|
||||||
|
|
||||||
|
// Surface the error as an OpenAI-compatible error chunk
|
||||||
|
return {
|
||||||
|
id: state.chatId || `chatcmpl-${Date.now()}`,
|
||||||
|
object: "chat.completion.chunk",
|
||||||
|
created: state.created || Math.floor(Date.now() / 1000),
|
||||||
|
model: state.model || "unknown",
|
||||||
|
choices: [{
|
||||||
|
index: 0,
|
||||||
|
delta: { content: `[Error] ${error.message || JSON.stringify(error)}` },
|
||||||
|
finish_reason: "stop"
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Reasoning events (convert to content or skip)
|
// Reasoning events (convert to content or skip)
|
||||||
if (eventType === "response.reasoning_summary_text.delta") {
|
if (eventType === "response.reasoning_summary_text.delta") {
|
||||||
// Optionally include reasoning as content, or skip
|
// Optionally include reasoning as content, or skip
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export function createSSEStream(options = {}) {
|
|||||||
let buffer = "";
|
let buffer = "";
|
||||||
let usage = null;
|
let usage = null;
|
||||||
|
|
||||||
const state = mode === STREAM_MODE.TRANSLATE ? { ...initState(sourceFormat), provider, toolNameMap } : null;
|
const state = mode === STREAM_MODE.TRANSLATE ? { ...initState(sourceFormat), provider, toolNameMap, model } : null;
|
||||||
|
|
||||||
let totalContentLength = 0;
|
let totalContentLength = 0;
|
||||||
let accumulatedContent = "";
|
let accumulatedContent = "";
|
||||||
|
|||||||
@@ -17,10 +17,7 @@ const OAUTH_TEST_CONFIG = {
|
|||||||
checkExpiry: true,
|
checkExpiry: true,
|
||||||
},
|
},
|
||||||
codex: {
|
codex: {
|
||||||
url: "https://api.openai.com/v1/models",
|
checkExpiry: true,
|
||||||
method: "GET",
|
|
||||||
authHeader: "Authorization",
|
|
||||||
authPrefix: "Bearer ",
|
|
||||||
refreshable: true,
|
refreshable: true,
|
||||||
},
|
},
|
||||||
"gemini-cli": {
|
"gemini-cli": {
|
||||||
|
|||||||
Reference in New Issue
Block a user