Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix: synthesize function_call_arguments events for one-shot Codex tool calls#6

Open
srizzo wants to merge 1 commit into
EvanZhouDev:main from
srizzo:fix/codex-responses-arguments-sse
Open

fix: synthesize function_call_arguments events for one-shot Codex tool calls #6
srizzo wants to merge 1 commit into
EvanZhouDev:main from
srizzo:fix/codex-responses-arguments-sse

Conversation

@srizzo

@srizzo srizzo commented Apr 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Some Codex reasoning models (observed on gpt-5.3-codex-spark) emit tool-call arguments in a single shot on response.output_item.done and skip the response.function_call_arguments.delta / .done pair that the OpenAI Responses API spec requires between output_item.added and output_item.done.

Downstream clients that assemble the final arguments string from the delta events (LiteLLM's Anthropic-Messages adapter, Cursor, etc.) end up with empty arguments — e.g. an Anthropic tool_use with input: {} — which breaks any tool that needs parameters (Bash, file writes, ...).

This is the symmetric counterpart to #3: that PR fixed the same class of issue on the Chat Completions path (via the Vercel AI SDK tool-call handler); this one fixes it on the Responses API path.

What changes

  • New normalizeCodexResponsesSseStream transformer in openai-oauth-core/src/sse.ts. Wraps the upstream Codex SSE ReadableStream and, when a function-call output_item.done arrives with non-empty arguments and no prior delta for the same item_id, injects a spec-compliant .delta + .done pair immediately before re-emitting the done event.
  • handleResponsesRequest in packages/openai-oauth/src/responses.ts now pipes the upstream streaming body through that transformer instead of passing it through verbatim.
  • Exposed the new function from openai-oauth-core's barrel.
  • 6 new unit tests in packages/openai-oauth-core/test/sse.test.ts covering:
    • synthesis when Codex skips delta events
    • no duplication when Codex already streams deltas
    • non-function-call output items pass through unchanged
    • multiple function calls tracked independently
    • skipped synthesis when arguments are empty
    • tolerance for non-JSON SSE payloads

Why this is the right layer

openai-oauth publishes /v1/responses as an OpenAI-public endpoint on top of the Codex-internal backend. It already does request-side normalization (stripping temperature/top_p/user, renaming web_search_previewweb_search, ...). The response-side pass-through in handleResponsesRequest assumed the upstream stream was already spec-conforming; for codex-spark that assumption is violated. Fixing it here means every downstream consumer of the proxy — not just LiteLLM — benefits without needing its own workaround.

Test plan

  • bun run test — 64 tests pass (26 in openai-oauth-core, incl. 6 new)
  • Rebuilt via bun run build and deployed to a local install; restarted openai-oauth; confirmed Claude Code subagents routed through LiteLLM → codex-gpt-5.3-codex-spark now receive populated tool_use.input for Bash calls where previously input: {} appeared and agents looped on InputValidationError: command is missing.
  • Recommended reviewer verification:
    • bun run test
    • ANTHROPIC_DEFAULT_HAIKU_MODEL=codex-gpt-5.3-codex-spark claude-codex -p "list /tmp" and check the Bash tool call receives a real command argument
    • Regression: same thing with codex-gpt-5.4 to confirm delta-streaming models behave unchanged

...l calls
Some Codex reasoning models (observed on gpt-5.3-codex-spark) emit tool-call
arguments in a single shot on response.output_item.done and skip the
response.function_call_arguments.delta / .done pair that the OpenAI
Responses API spec requires between output_item.added and output_item.done.
Downstream clients that assemble the final arguments string from the delta
events (LiteLLM's Anthropic-Messages adapter, Cursor, etc.) end up with
empty arguments — e.g. an Anthropic tool_use with input: {} — which breaks
any tool that requires parameters (Bash, file writes, ...).
openai-oauth is the layer that publishes OpenAI-public /v1/responses on
top of the Codex-internal backend, and already does request-side
normalization (stripping unsupported params, renaming tools). This commit
adds the symmetric response-side translation: a ReadableStream transformer
that tracks per-item_id whether any delta was seen, and when an
output_item.done arrives carrying a function_call with non-empty
arguments and no prior delta, synthesizes a spec-compliant
.delta + .done pair immediately before re-emitting the done event.
Models that already stream deltas (gpt-5.4 family) are unaffected — the
transformer's seenDeltas set ensures it only synthesizes when real deltas
were absent.
@srizzo srizzo force-pushed the fix/codex-responses-arguments-sse branch from 7ac0ade to 8909bb7 Compare April 18, 2026 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /