Skip to content

fix(codex): handle function_call_arguments streaming for both spark and non-spark models#235

Open
thebtf wants to merge 4 commits intorouter-for-me:mainfrom
thebtf:fix/codex-spark-function-call-arguments-done
Open

fix(codex): handle function_call_arguments streaming for both spark and non-spark models#235
thebtf wants to merge 4 commits intorouter-for-me:mainfrom
thebtf:fix/codex-spark-function-call-arguments-done

Conversation

@thebtf
Copy link

@thebtf thebtf commented Feb 15, 2026

Summary

Fixes tool call argument streaming in the Codex→Claude response translator for all Codex model variants:

  • Spark models (e.g. gpt-5.3-codex-spark): Send arguments only in a single response.function_call_arguments.done event without preceding delta events. The translator didn't handle done, so arguments were lost → empty input: {} → infinite retry loops.

  • Non-spark models (e.g. gpt-5.3-codex, gpt-5.2-codex): Send arguments via multiple response.function_call_arguments.delta events followed by a done event. After the spark fix, the done handler unconditionally emitted the full arguments, duplicating what deltas already streamed → invalid double JSON → parse failure → infinite retry loops.

Changes

  • Emit full arguments from response.function_call_arguments.done as input_json_delta (spark fix)
  • Track HasReceivedArgumentsDelta flag to skip done emission when delta events already streamed the arguments (non-spark fix)
  • Reset the flag on each new response.output_item.added function_call (supports multiple tool calls per response)

Tested with

Model Tool types Result
gpt-5.3-codex-spark Read, Grep, Glob All pass
gpt-5.3-codex Read, Grep, Glob All pass (was infinite loop before)
gpt-5.2-codex Read, Grep, Glob All pass (was infinite loop before)

Test plan

  • Spark models: tool calls work (done-only path)
  • Non-spark 5.3: tool calls work (delta+done path)
  • Non-spark 5.2: tool calls work (delta+done path)
  • Multiple tool types verified (Read, Grep, Glob)
  • Both sonnet and haiku model slots verified

Kirill Turanskiy added 3 commits February 16, 2026 00:20
…eaming translator

Some Codex models (e.g. gpt-5.3-codex-spark) send function call arguments
in a single "done" event without preceding "delta" events. The streaming
translator only handled "delta" events, causing tool call arguments to be
lost — resulting in empty tool inputs and infinite retry loops in clients
like Claude Code.

Emit the full arguments from the "done" event as a single input_json_delta
so downstream clients receive the complete tool input.
Build and push Docker image to GitHub Container Registry on push
to fix/* and feat/* branches. Fork-only, not intended for upstream.
@gemini-code-assist
Copy link

Summary of Changes

Hello @thebtf, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue in the streaming translation of Codex responses to Claude, specifically concerning function call arguments. Previously, certain models that transmit these arguments solely via response.function_call_arguments.done events would cause the arguments to be lost, leading to an infinite loop of empty tool call retries in the Claude client. The fix introduces a new handler to correctly parse and emit these arguments, ensuring proper tool execution and preventing system instability.

Highlights

  • Function Call Argument Handling: Implemented handling for response.function_call_arguments.done events in the ConvertCodexResponseToClaude streaming translator. This ensures that tool call arguments are correctly processed when models like gpt-5.3-codex-spark send them exclusively in a single done event, preventing them from being dropped and causing infinite retry loops.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • internal/translator/codex/claude/codex_claude_response.go
    • Added a new conditional block to detect and process response.function_call_arguments.done events.
    • Implemented logic to extract arguments from the done event and format them as a content_block_delta with input_json_delta for Claude.
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/ghcr-build.yml
Activity
  • No specific activity (comments, reviews, progress) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly fixes a bug where function call arguments were being dropped for certain models in the streaming translator. The change adds a handler for response.function_call_arguments.done events, which is a necessary addition. My review includes one suggestion to improve code maintainability by addressing code duplication.

Comment on lines 180 to 193
} else if typeStr == "response.function_call_arguments.done" {
// Some models (e.g. gpt-5.3-codex-spark) send function call arguments
// in a single "done" event without preceding "delta" events.
// Emit the full arguments as a single input_json_delta so the
// downstream Claude client receives the complete tool input.
if args := rootResult.Get("arguments").String(); args != "" {
template = `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}`
template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex)
template, _ = sjson.Set(template, "delta.partial_json", args)

output += "event: content_block_delta\n"
output += fmt.Sprintf("data: %s\n\n", template)
}
}

Choose a reason for hiding this comment

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

medium

This new block for handling response.function_call_arguments.done is functionally correct, but it introduces significant code duplication with the existing handler for response.function_call_arguments.delta on lines 173-179.

To improve maintainability, consider refactoring this duplicated logic. You could combine both else if blocks or extract the common code into a helper function.

For example, you could combine them like this:

} else if typeStr == "response.function_call_arguments.delta" || typeStr == "response.function_call_arguments.done" {
	var args string
	if typeStr == "response.function_call_arguments.delta" {
		args = rootResult.Get("delta").String()
	} else {
		args = rootResult.Get("arguments").String()
	}

	if args != "" {
		template = `{"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""}}`
		template, _ = sjson.Set(template, "index", (*param).(*ConvertCodexResponseToClaudeParams).BlockIndex)
		template, _ = sjson.Set(template, "delta.partial_json", args)

		output += "event: content_block_delta\n"
		output += fmt.Sprintf("data: %s\n\n", template)
	}
}

While this change would also affect lines outside the current diff, it's a good candidate for a follow-up refactoring to keep the code DRY.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks for the suggestion! However, the second commit in this PR (2de17c8) adds a HasReceivedArgumentsDelta flag that makes the done handler conditional — it only emits when no delta events preceded it. This means the two handlers have different semantics:

  • delta: always emits + sets the flag
  • done: only emits if flag is false (spark models that skip deltas)

Merging them into one block would obscure this important distinction. The current structure makes the intent clear: spark (done-only) vs non-spark (delta+done) paths.

…ede done

Non-spark codex models (gpt-5.3-codex, gpt-5.2-codex) stream function call
arguments via multiple delta events followed by a done event. The done handler
unconditionally emitted the full arguments, duplicating what deltas already
streamed. This produced invalid double JSON that Claude Code couldn't parse,
causing tool calls to fail with missing parameters and infinite retry loops.

Add HasReceivedArgumentsDelta flag to track whether delta events were received.
The done handler now only emits arguments when no deltas preceded it (spark
models), while delta-based streaming continues to work for non-spark models.
@thebtf thebtf changed the title fix(codex): handle response.function_call_arguments.done in streaming translator fix(codex): handle function_call_arguments streaming for both spark and non-spark models Feb 15, 2026
@luispater
Copy link

This project only accepts pull requests that relate to third-party provider support. Any pull requests unrelated to third-party provider support will be rejected.

If you need to submit any non-third-party provider changes, please open them against the mainline repository.

Would you mind recreate a new PR to mainline?

@thebtf
Copy link
Author

thebtf commented Feb 16, 2026

Thank you for the clarification! Understood — CLIProxyAPIplus is for third-party provider changes only.

I've moved this to the mainline repository:

Closing this PR.

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.

2 participants