Skip to content

fix(translator): pad empty text for thinking-only Claude stops - #5858

Open
rome-xi wants to merge 1 commit into
router-for-me:devfrom
rome-xi:fix/claude-sse-empty-text-pad
Open

rome-xi wants to merge 1 commit into
router-for-me:devfrom
rome-xi:fix/claude-sse-empty-text-pad

Conversation

@rome-xi

@rome-xi rome-xi commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Why

Antigravity→Claude and Gemini→Claude streams (and non-stream Messages) can end normally with only thinking and no text or tool_use. Claude Code SDK treats that silent end_turn as an incomplete turn, retries, and eventually reports a fake 32k output-token error.

A legal empty Claude text content block before message_delta / message_stop lets the SDK complete the turn.

What

  • Add shared Claude SSE empty-text pad helpers.
  • Inject that empty text block on thinking-only normal stops (end_turn / STOP / empty) in the Antigravity and Gemini Claude translators.
  • Do not pad max_tokens / MAX_TOKENS, tool turns, or empty streams with no thinking.
  • Do not forge max_tokens or change real truncation semantics.
  • Native Claude passthrough is unchanged.

Tests

go test ./internal/translator/common/ -count=1 -run 'Pad|EmptyText'
go test ./internal/translator/antigravity/claude/ ./internal/translator/gemini/claude/ -count=1 -run 'ThinkingOnly|EmptyText|Pad'
go test ./internal/translator/common/ ./internal/translator/antigravity/claude/ ./internal/translator/gemini/claude/ -count=1
  • Common helpers: pad on thinking-only end_turn / STOP / empty; no pad on max_tokens, existing text, tool_use, empty stream, or already padded.
  • Antigravity stream + non-stream: thinking-only STOP emits empty text before message_delta; MAX_TOKENS does not pad.
  • Gemini stream + non-stream: same.

Inject a legal empty Claude text content block before message_delta on
normal thinking-only stops so Claude Code SDK can complete the turn.

Do not pad max_tokens, tool turns, or empty streams, and do not forge
truncation semantics.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-16T03:33:17.090037Z b7dfa16 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b7dfa16dd6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +267 to +271
stopReason := "end_turn"
if params.SawToolCall {
stopReason = "tool_use"
} else if finish := gjson.GetBytes(rawJSON, "candidates.0.finishReason"); finish.Exists() && finish.String() == "MAX_TOKENS" {
stopReason = "max_tokens"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict padding to actual normal finish reasons

When a thinking-only response ends with an abnormal Gemini reason such as SAFETY, RECITATION, or MALFORMED_FUNCTION_CALL, this logic leaves stopReason as end_turn, so NeedsEmptyTextPad inserts a visible empty-text block and causes the client to accept the blocked response as a normal completion. Gate padding on the original finish reason being STOP/unspecified rather than the already-collapsed Claude stop reason; the non-stream and Antigravity padding paths have the same issue.

Useful? React with 👍 / 👎.

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.

1 participant