diff --git a/open-sse/config/providerModels.js b/open-sse/config/providerModels.js index 5accb7fe..c1456db9 100644 --- a/open-sse/config/providerModels.js +++ b/open-sse/config/providerModels.js @@ -10,11 +10,20 @@ export const PROVIDER_MODELS = { { id: "claude-haiku-4-5-20251001", name: "Claude 4.5 Haiku" }, ], cx: [ // OpenAI Codex + // GPT 5.3 Codex - all thinking levels + { id: "gpt-5.3-codex", name: "GPT 5.3 Codex" }, + { id: "gpt-5.3-codex-xhigh", name: "GPT 5.3 Codex (xHigh)" }, + { id: "gpt-5.3-codex-high", name: "GPT 5.3 Codex (High)" }, + { id: "gpt-5.3-codex-low", name: "GPT 5.3 Codex (Low)" }, + { id: "gpt-5.3-codex-none", name: "GPT 5.3 Codex (None)" }, + // Mini - medium and high only + { id: "gpt-5.1-codex-mini", name: "GPT 5.1 Codex Mini" }, + { id: "gpt-5.1-codex-mini-high", name: "GPT 5.1 Codex Mini (High)" }, + // Other models { id: "gpt-5.2-codex", name: "GPT 5.2 Codex" }, { id: "gpt-5.2", name: "GPT 5.2" }, { id: "gpt-5.1-codex-max", name: "GPT 5.1 Codex Max" }, { id: "gpt-5.1-codex", name: "GPT 5.1 Codex" }, - { id: "gpt-5.1-codex-mini", name: "GPT 5.1 Codex Mini" }, { id: "gpt-5.1", name: "GPT 5.1" }, { id: "gpt-5-codex", name: "GPT 5 Codex" }, { id: "gpt-5-codex-mini", name: "GPT 5 Codex Mini" }, diff --git a/open-sse/executors/codex.js b/open-sse/executors/codex.js index 5c8b7934..dd65fd41 100644 --- a/open-sse/executors/codex.js +++ b/open-sse/executors/codex.js @@ -23,6 +23,26 @@ export class CodexExecutor extends BaseExecutor { // Ensure store is false (Codex requirement) body.store = false; + // Extract thinking level from model name suffix + // e.g., gpt-5.3-codex-high → high, gpt-5.3-codex → medium (default) + const effortLevels = ['none', 'low', 'medium', 'high', 'xhigh']; + let modelEffort = null; + for (const level of effortLevels) { + if (model.endsWith(`-${level}`)) { + modelEffort = level; + // Strip suffix from model name for actual API call + body.model = body.model.replace(`-${level}`, ''); + break; + } + } + + // Priority: explicit reasoning.effort > reasoning_effort param > model suffix > default (medium) + if (!body.reasoning) { + const effort = body.reasoning_effort || modelEffort || 'medium'; + body.reasoning = { effort }; + } + delete body.reasoning_effort; + // Remove unsupported parameters for Codex API delete body.temperature; delete body.top_p; diff --git a/open-sse/handlers/chatCore.js b/open-sse/handlers/chatCore.js index 88dc9096..5bb2caf3 100644 --- a/open-sse/handlers/chatCore.js +++ b/open-sse/handlers/chatCore.js @@ -468,16 +468,22 @@ export async function handleChatCore({ body, modelInfo, credentials, log, onCred // Create transform stream with logger for streaming response let transformStream; - // For Codex provider, always translate response from openai-responses to openai format - // This ensures clients like Cursor get the expected chat completions format - // BUT: skip translation if client already sent in openai-responses format (like Droid CLI) - const needsCodexTranslation = (provider === 'codex' || provider === 'openai') && targetFormat === 'openai-responses' && sourceFormat !== 'openai-responses'; - if (needsCodexTranslation || needsTranslation(targetFormat, sourceFormat)) { - // For Codex, translate FROM openai-responses TO openai (client's expected format) - const responseSourceFormat = needsCodexTranslation ? 'openai-responses' : targetFormat; - const responseTargetFormat = needsCodexTranslation ? 'openai' : sourceFormat; - transformStream = createSSETransformStreamWithLogger(responseSourceFormat, responseTargetFormat, provider, reqLogger, toolNameMap, model, connectionId, body); + // For Codex provider, translate response from openai-responses to openai (Chat Completions) format + // UNLESS client originally sent in openai-responses format (like Droid CLI) - they expect same format back + const needsCodexTranslation = provider === 'codex' + && targetFormat === 'openai-responses' + && sourceFormat !== 'openai-responses'; + + if (needsCodexTranslation) { + // Codex returns openai-responses, translate to openai (Chat Completions) that clients expect + log?.debug?.("STREAM", `Codex translation mode: openai-responses → openai`); + transformStream = createSSETransformStreamWithLogger('openai-responses', 'openai', provider, reqLogger, toolNameMap, model, connectionId, body); + } else if (needsTranslation(targetFormat, sourceFormat)) { + // Standard translation for other providers + log?.debug?.("STREAM", `Translation mode: ${targetFormat} → ${sourceFormat}`); + transformStream = createSSETransformStreamWithLogger(targetFormat, sourceFormat, provider, reqLogger, toolNameMap, model, connectionId, body); } else { + log?.debug?.("STREAM", `Standard passthrough mode`); transformStream = createPassthroughStreamWithLogger(provider, reqLogger, model, connectionId, body); } diff --git a/src/sse/handlers/chat.js b/src/sse/handlers/chat.js index b9674147..49f9b907 100644 --- a/src/sse/handlers/chat.js +++ b/src/sse/handlers/chat.js @@ -38,7 +38,8 @@ export async function handleChat(request, clientRawRequest = null) { // Count messages (support both messages[] and input[] formats) const msgCount = body.messages?.length || body.input?.length || 0; const toolCount = body.tools?.length || 0; - log.request("POST", `${url.pathname} | ${modelStr} | ${msgCount} msgs${toolCount ? ` | ${toolCount} tools` : ""}`); + const effort = body.reasoning_effort || body.reasoning?.effort || null; + log.request("POST", `${url.pathname} | ${modelStr} | ${msgCount} msgs${toolCount ? ` | ${toolCount} tools` : ""}${effort ? ` | effort=${effort}` : ""}`); // Log API key (masked) const apiKey = request.headers.get("Authorization");