Skip to content

fix(proxy): emit response.incomplete for incomplete Codex responses - #551

Merged
SaladDay merged 1 commit into
SaladDay:mainfrom
RGB-loop:fix/codex-response-incomplete-event
Nov 5, 2026
Merged

SaladDay merged 1 commit into
SaladDay:mainfrom
RGB-loop:fix/codex-response-incomplete-event

Conversation

@RGB-loop

@RGB-loop RGB-loop commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Summary

When a Codex request is converted to Anthropic Messages or Chat Completions, a response that stops early (max_tokens, length, refusal, truncated stream) gets status: "incomplete", but the terminal SSE event is always response.completed. Codex (rust-v0.162.1, codex-api/src/sse/responses.rs) only reads incomplete_details on a response.incomplete event, so it treats these turns as successful. The existing code comments say the status is there so Codex does not take truncation or refusal for a normal completion; that intent was not being met.

  • codex_responses_sse::response_terminal picks the event name and type from response.status. Both converters use it.
  • The usage parser, Codex OAuth SSE aggregation (handlers.rs) and the Chat history recorder now accept response.incomplete. Without this, the converter change would drop usage and Chat history for these responses.

This also fixes two of these consumers for native Responses upstreams, which already send response.incomplete: usage was lost, and Codex OAuth aggregation failed with No response.completed event in upstream SSE.

Behavior change for Codex users

Turns that used to end silently with a truncated result now end with an error, matching native OpenAI behavior:

  • max_output_tokens: Incomplete response returned, reason: max_output_tokens
  • refusal (content_filter): Codex's content filter error

This may show up more often than with native OpenAI. On Codex → Anthropic routes, when neither the request nor the provider sets max_output_tokens, the proxy injects max_tokens=8192 (request_builder.rs), so long outputs can hit the limit. Users can raise it with the provider's meta.max_output_tokens. This PR does not change that default.

Codex may retry on this stream error, which would replay the same limit and use more tokens. I did not confirm its retry policy.

Not changed: Chat finish_reason=content_filter still maps to completed, and truncated streams report max_output_tokens on the Anthropic path but stream_truncated on the Chat path.

Testing

  • 7 new or updated tests cover both converters (max_tokens/length, refusal, truncation), the usage parser, OAuth aggregation and Chat history. All 7 fail with the old behavior restored and pass with this change.
  • cargo fmt --check, cargo test --lib proxy:: -- --skip services::proxy and cargo test --test proxy_claude_streaming pass on Rust 1.95.
  • Run in parallel, some services::proxy tests fail on temp HOME races, also with the change reverted. With --test-threads=1 they pass 77/77.
  • These local runs are on Rust 1.95. The repo's pinned 1.91.1 is covered by Rust CI.
  • Not run end to end against a real Codex CLI or a real Anthropic/Chat upstream. Codex's handling is from reading its source.

Fixes #546

🤖 Generated with Claude Code

Converted Anthropic Messages and Chat Completions streams already set
response.status to "incomplete" (max_tokens, refusal, truncation,
finish_reason=length), but always sent the terminal event as
response.completed. Codex only reads incomplete_details from a
response.incomplete event, so these responses were treated as normal
completions.

Pick the terminal event name from the response status, and let the
usage parser, Codex OAuth SSE aggregation and Chat history recorder
accept response.incomplete so usage and history are not lost.

Fixes SaladDay#546

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@SaladDay SaladDay left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix!

@SaladDay
SaladDay merged commit 0d95f37 into SaladDay:main Nov 5, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Codex Anthropic 转换在响应未完成时仍发送 response.completed,导致完成状态识别错误

2 participants