Skip to content

fix: report truncation between Claude max_tokens and OpenAI length - #147

Merged
lich0821 merged 2 commits into
lich0821:masterfrom
pcbeingused333:fix/truncation-stop-reasons
Oct 24, 2026
Merged

lich0821 merged 2 commits into
lich0821:masterfrom
pcbeingused333:fix/truncation-stop-reasons

Conversation

@pcbeingused333

Copy link
Copy Markdown
Contributor

What

The Claude <-> OpenAI Chat converters in internal/transformer/convert/claude_openai.go never mapped a reply that was cut off by the token limit:

  • Claude stop_reason: "max_tokens" always became OpenAI finish_reason: "stop" (ClaudeRespToOpenAI and the message_delta branch of ClaudeStreamToOpenAI).
  • OpenAI finish_reason: "length" always became Claude stop_reason: "end_turn" (OpenAIRespToClaude and the finish branch of OpenAIStreamToClaude).

A client behind ccNexus therefore saw a truncated reply as a complete one. Claude Code, for one, only continues or reports a cut-off answer when it sees max_tokens.

Both directions now map max_tokens <-> length, in the non-stream and stream converters, through one small helper (claudeStopReasonToOpenAI) for the Claude side. Tool-call mapping is unchanged. In OpenAIRespToClaude and OpenAIStreamToClaude, length wins over tool_use, since a tool call cut off mid-arguments is a truncated reply. The Gemini converters are left for a separate change.

Tests

In internal/transformer/convert/claude_openai_test.go:

  • TestClaudeRespToOpenAIMaxTokensReportsLength and TestClaudeStreamToOpenAIMaxTokensReportsLength: a Claude max_tokens stop gives finish_reason: "length".
  • TestOpenAIRespToClaudeLengthReportsMaxTokens and TestOpenAIStreamToClaudeLengthReportsMaxTokens: an OpenAI length finish gives stop_reason: "max_tokens" and never end_turn.

All four fail on the base commit and pass now. go test ./internal/transformer/... and go vet ./internal/transformer/... pass.

Stacked on #145 (the first commit, tool_use when an OpenAI stream ends a tool call with stop), which the stream branch here extends; only the second commit belongs to this PR.
This change was made with Claude Code; the tests above were run on the branch to check it.

pcbeingused333 and others added 2 commits October 19, 2026 23:58
…sh_reason stop

OpenAI-compatible providers such as Gemini finish tool calls with
finish_reason "stop". OpenAIStreamToClaude mapped that to end_turn even
though a tool_use block had been sent, so Claude clients stopped instead of
running the tool. It now reports tool_use when a tool block was started, also
when the stream ends with [DONE] without a finish_reason.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The Claude <-> OpenAI Chat converters never mapped a truncated reply. Claude
stop_reason max_tokens always became finish_reason stop, and OpenAI
finish_reason length always became end_turn, so clients treated a cut-off
reply as complete. Map them in the non-stream and stream converters, both
directions.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@lich0821
lich0821 merged commit 5e4aa86 into lich0821:master Oct 24, 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.

2 participants