Skip to content

gateway: keep one Chat choice when translating, including relay indexes - #168

Merged
yetone merged 8 commits into
yetone:mainfrom
charan-rathore:chat-choice-zero-translation
Sep 28, 2026
Merged

yetone merged 8 commits into
yetone:mainfrom
charan-rathore:chat-choice-zero-translation

Conversation

@charan-rathore

@charan-rathore charan-rathore commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

A translated reply has room for one choice. When a provider streams several choices, magpie now keeps the first choice it sees rather than joining their text, tool calls and stop reasons.

Relays are messy about indexes, so the decoder is lenient: it locks onto the first readable choice index it sees (whether that is 0, 1, a string "0", or a decimal like 1.0), and a chunk with a missing, null or unreadable index simply continues the current choice instead of being dropped. Chat-to-Chat passthrough remains unchanged.

Tests cover interleaved choices, a single choice indexed 1, a string index, missing/null/malformed indexes, a decimal index, an unindexed choice after a usage-only chunk, and existing parallel tool calls. Tested with GOPROXY=off GOSUMDB=off go test ./internal/gateway -count=1.

This follows yetone's requested-changes reviews on #168. Instinct assisted with the change and tests.

@yetone yetone 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.

@charan-rathore 感谢!思路是对的:流式和非流式翻译都走 chatDecoder,新测试在 main 上失败(BetaAlpha again done!),加上修复后通过;choice 1 先到、缺 index 或 index 为 null、只有 usage 的块、choice 0 里的并行工具调用、Chat→Chat 带 n:2 的透传,我都实际跑过,结果正确。不过有两种以前能用的中转站返回会被这个改动弄坏,合并前需要修一下:

  1. 总是发非 0 index 的中转站会丢掉整条回复(chat.go:391-395,if c.Index != 0 { continue }):上游只有一个 choice、但写成 "index":1 时,Anthropic 和 Responses 的回复都是 200 但 content/output 为空、usage 为 0,流式和非流式都一样;main 上会正常返回 "Hello"。magpie 从不发 n,所以这正是最可能出现的情况,而且是悄悄失败。
  2. index 是字符串时每个块都被丢掉(chat.go:365,Index int):"index":"0" 会让 json.Unmarshal 报类型错误,decode 出错就返回 nil,整条回复为空;main 不解析这个字段,所以没问题。

建议:锁定第一个出现的 choice 的 index,而不是写死 0;index 宽松解析(比如 json.RawMessage,或者可空数字并忽略错误)。改好后我再拉下来验证。

Updated the chatDecoder struct to include choice as a string and modified the handling of the index in the decode method to support various formats.
Refactor comments for clarity and update test cases to ensure proper handling of choice indices in chat translation.
Refactor chat handling by removing unused types and functions. Introduce new functions for processing chat responses and tool calls.
Refactor chat handling to support new request structure and improve parsing logic.
@charan-rathore charan-rathore changed the title gateway: keep only first Chat choice when translating gateway: keep one Chat choice when translating, including relay indexes Sep 28, 2026
@yetone

yetone commented Sep 28, 2026

Copy link
Copy Markdown
Owner

Thanks for the update — both points from my last review are fixed; I checked through /v1/messages, streaming and non-streaming, against a scripted Chat upstream: a relay that only sends "index":1 now gets its reply through, and "0"/"1" string indexes work. Choice 1 before choice 0, usage-only chunks first, parallel tool calls and Chat→Chat n:2 passthrough all hold too.

One thing left (internal/gateway/chat.go, the index := "0" block): a missing / null / unreadable index is turned into "0" and then compared with the locked index. When that disagrees with the upstream's own numbering the whole reply is dropped and the client gets a 200 with empty content. These all work on main and break here:

  • a role chunk with no index, then "index":1 for the rest → lock becomes 0, "Hello" is lost, content: []
  • "index":1, then later chunks with no index → only "Hel"
  • "index":1.0, then 1 → only "Hel"

Suggested fix: a missing, null or unparseable index should neither set the lock nor be filtered — treat it as the current choice. Only a readable index locks or filters (and parse 1.0 as 1). A test for each of the three would be great.

@yetone yetone 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.

See comment above: a missing/null index is coerced to 0 and can drop the whole reply.

Updated tool message handling to include images in user messages. Enhanced the tool result message format to include image URLs and additional notes about returned images.
@charan-rathore

Copy link
Copy Markdown
Contributor Author

Thanks for the careful retest. I reproduced all three cases on the previous branch, then changed the decoder so only a readable choice index selects or filters the reply. Missing, null and malformed indexes continue the current choice; 1.0 and 1 match. The new regression tests failed before the fix, and the full gateway suite passes after it. I've updated #168 for another look.

@yetone yetone reopened this Sep 28, 2026
@yetone
yetone merged commit ff4f730 into yetone:main Sep 28, 2026
3 of 4 checks passed
@yetone

yetone commented Sep 28, 2026

Copy link
Copy Markdown
Owner

Thanks! Re-reviewed the new head line by line and ran it end to end through the gateway against scripted upstreams — all the cases from my previous review now pass, and the new tests fail without the change. Merged; ships in v0.1.350. (#168 had been closed at the same moment as your update comment, which looked accidental, so I reopened it to merge.)

@charan-rathore
charan-rathore deleted the chat-choice-zero-translation branch October 1, 2026 09:12
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