Problem
Several Responses→Chat format conversion bugs can cause upstream 400 errors or masked stream failures when routing Codex through Chat Completions providers.
1. cache_control passthrough causes 400 errors
During Anthropic→OpenAI format conversion, cache_control fields are preserved on system messages, text content blocks, and tools. Strict OpenAI-compatible endpoints (e.g. MiniMax, some Chinese providers) reject these unknown fields with 400 errors.
Additionally, when a user message has exactly one text content block with cache_control, the content is kept in array-of-objects format instead of being simplified to a plain string. Some providers expect the simpler format.
Location: src-tauri/src/proxy/providers/transform.rs — system message construction (line ~122), text block construction (line ~339), tool construction (line ~187), and single-block simplification (line ~407).
2. tool_choice sent without tools
In the Responses→Chat conversion, tool_choice is copied unconditionally from the input body, but tools is only included when non-empty. If all tools are filtered out during conversion, the request contains tool_choice without a corresponding tools field, which causes errors on providers that enforce this constraint.
Location: src-tauri/src/proxy/providers/transform_codex_chat.rs — lines 308-315. The tool_choice block should be inside the if !tools.is_empty() guard.
3. Truncated chat streams masked as normal completion
When a Codex Chat→Responses stream ends without receiving [DONE] or a finish_reason, the code unconditionally calls finalize() which emits response.completed. This masks truncation as successful completion. The fix should distinguish three cases:
- Normal completion:
finish_reason or [DONE] received → response.completed
- Incomplete with output: substantive output exists but no
finish_reason → response.incomplete
- Empty truncation: no output and no
finish_reason → response.failed with stream_truncated
Location: src-tauri/src/proxy/providers/streaming_codex_chat.rs — finalize() method (line ~525) and response_status_from_finish_reason() in transform_codex_chat.rs (line ~1566).
Expected Behavior
cache_control fields should be stripped from the OpenAI-format request body to avoid 400 errors on strict endpoints
- Single text content blocks should always be simplified to plain string format
tool_choice should be omitted when tools is empty
- Truncated streams should be reported as incomplete or failed, not completed
Problem
Several Responses→Chat format conversion bugs can cause upstream 400 errors or masked stream failures when routing Codex through Chat Completions providers.
1.
cache_controlpassthrough causes 400 errorsDuring Anthropic→OpenAI format conversion,
cache_controlfields are preserved on system messages, text content blocks, and tools. Strict OpenAI-compatible endpoints (e.g. MiniMax, some Chinese providers) reject these unknown fields with 400 errors.Additionally, when a user message has exactly one text content block with
cache_control, the content is kept in array-of-objects format instead of being simplified to a plain string. Some providers expect the simpler format.Location:
src-tauri/src/proxy/providers/transform.rs— system message construction (line ~122), text block construction (line ~339), tool construction (line ~187), and single-block simplification (line ~407).2.
tool_choicesent withouttoolsIn the Responses→Chat conversion,
tool_choiceis copied unconditionally from the input body, buttoolsis only included when non-empty. If all tools are filtered out during conversion, the request containstool_choicewithout a correspondingtoolsfield, which causes errors on providers that enforce this constraint.Location:
src-tauri/src/proxy/providers/transform_codex_chat.rs— lines 308-315. Thetool_choiceblock should be inside theif !tools.is_empty()guard.3. Truncated chat streams masked as normal completion
When a Codex Chat→Responses stream ends without receiving
[DONE]or afinish_reason, the code unconditionally callsfinalize()which emitsresponse.completed. This masks truncation as successful completion. The fix should distinguish three cases:finish_reasonor[DONE]received →response.completedfinish_reason→response.incompletefinish_reason→response.failedwithstream_truncatedLocation:
src-tauri/src/proxy/providers/streaming_codex_chat.rs—finalize()method (line ~525) andresponse_status_from_finish_reason()intransform_codex_chat.rs(line ~1566).Expected Behavior
cache_controlfields should be stripped from the OpenAI-format request body to avoid 400 errors on strict endpointstool_choiceshould be omitted whentoolsis empty