mirror of
https://github.com/decolua/9router.git
synced 2026-05-08 12:01:28 +00:00
fix: update Codex executor for gpt-5.3-codex support
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -39,9 +39,8 @@ export const PROVIDERS = {
|
||||
baseUrl: "https://chatgpt.com/backend-api/codex/responses",
|
||||
format: "openai-responses", // Use OpenAI Responses API format (reuse translator)
|
||||
headers: {
|
||||
"Version": "0.92.0",
|
||||
"Openai-Beta": "responses=experimental",
|
||||
"User-Agent": "codex-cli/0.92.0 (Windows 10.0.26100; x64)"
|
||||
"originator": "codex-cli",
|
||||
"User-Agent": "codex-cli/1.0.18 (macOS; arm64)"
|
||||
},
|
||||
// OpenAI OAuth configuration
|
||||
clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
|
||||
|
||||
@@ -11,10 +11,27 @@ export class CodexExecutor extends BaseExecutor {
|
||||
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
|
||||
*/
|
||||
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 (!body.instructions || body.instructions.trim() === "") {
|
||||
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)
|
||||
if (!body.reasoning) {
|
||||
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;
|
||||
|
||||
// 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
|
||||
delete body.temperature;
|
||||
delete body.top_p;
|
||||
|
||||
@@ -368,7 +368,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
||||
id: state.chatId || `chatcmpl-${Date.now()}`,
|
||||
object: "chat.completion.chunk",
|
||||
created: state.created || Math.floor(Date.now() / 1000),
|
||||
model: state.model || "gpt-4",
|
||||
model: state.model || "unknown",
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {},
|
||||
@@ -401,7 +401,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
||||
id: state.chatId,
|
||||
object: "chat.completion.chunk",
|
||||
created: state.created,
|
||||
model: state.model || "gpt-4",
|
||||
model: state.model || "unknown",
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { content: delta },
|
||||
@@ -424,7 +424,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
||||
id: state.chatId,
|
||||
object: "chat.completion.chunk",
|
||||
created: state.created,
|
||||
model: state.model || "gpt-4",
|
||||
model: state.model || "unknown",
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {
|
||||
@@ -452,7 +452,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
||||
id: state.chatId,
|
||||
object: "chat.completion.chunk",
|
||||
created: state.created,
|
||||
model: state.model || "gpt-4",
|
||||
model: state.model || "unknown",
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {
|
||||
@@ -511,7 +511,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
||||
id: state.chatId,
|
||||
object: "chat.completion.chunk",
|
||||
created: state.created,
|
||||
model: state.model || "gpt-4",
|
||||
model: state.model || "unknown",
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {},
|
||||
@@ -529,6 +529,32 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
|
||||
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)
|
||||
if (eventType === "response.reasoning_summary_text.delta") {
|
||||
// Optionally include reasoning as content, or skip
|
||||
|
||||
@@ -49,7 +49,7 @@ export function createSSEStream(options = {}) {
|
||||
let buffer = "";
|
||||
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 accumulatedContent = "";
|
||||
|
||||
@@ -17,10 +17,7 @@ const OAUTH_TEST_CONFIG = {
|
||||
checkExpiry: true,
|
||||
},
|
||||
codex: {
|
||||
url: "https://api.openai.com/v1/models",
|
||||
method: "GET",
|
||||
authHeader: "Authorization",
|
||||
authPrefix: "Bearer ",
|
||||
checkExpiry: true,
|
||||
refreshable: true,
|
||||
},
|
||||
"gemini-cli": {
|
||||
|
||||
Reference in New Issue
Block a user