The empty-completion detector in sdk/cliproxy/auth/empty_completion.go has no Interactions-protocol branch: no interactionsEventTypes set, no evalInteractions, no interactionsChunk. Every Interactions-protocol stream is therefore classified as unknown data, sawUnknownData flips the accumulator to non-empty, and an empty turn is committed as a valid response — the empty-completion failover never engages for the Interactions protocol.
The repo does ship the Interactions translator (internal/translator/gemini/interactions/), and that translator treats "finish" as a terminal event alongside "interaction.completed" (interactions_gemini_response.go:106), taking usage from metadata.total_usage first (internal/translator/common/interactions_usage.go:5-12). The detector does not know any of these events.
Reproduction
Feed the detector a complete Interactions turn that produced zero output:
payload := []byte("event: interaction.created\ndata: {\"event_type\":\"interaction.created\",\"interaction\":{\"id\":\"int_1\",\"object\":\"interaction\",\"status\":\"in_progress\"}}\n\nevent: step.start\ndata: {\"event_type\":\"step.start\",\"index\":0,\"step\":{\"type\":\"model_output\"}}\n\nevent: finish\ndata: {\"event_type\":\"finish\",\"metadata\":{\"total_usage\":{\"total_output_tokens\":0}}}\n\n")
isEmptyCompletionPayload(payload) // returns false, must be true
With the Interactions branch absent this returns false (unknown data commits the stream). The expected result is true so the conductor can rotate to the next auth instead of handing an empty response to the client.
Context
This gap came to light while addressing the equivalent review on the upstream project: router-for-me/CLIProxyAPI#4881 now recognizes "finish" as a terminal Interactions event reading metadata.total_usage (upstream commit baf43ba5 on fix/empty-completion-retry). The paired failover work here is #195; this detector gap is separate and older — porting the upstream Interactions branch (event set, interactionsChunk, evalInteractions, evalInteractionsUsage, and the finish handling) would close it.
Test expectations
The upstream TestEmptyCompletionPredicate cases that cover this, worth porting together with the branch:
interactions sse stream ending in bare finish with zero output is empty → true
interactions sse stream ending in bare finish with output is not empty → false
The empty-completion detector in
sdk/cliproxy/auth/empty_completion.gohas no Interactions-protocol branch: nointeractionsEventTypesset, noevalInteractions, nointeractionsChunk. Every Interactions-protocol stream is therefore classified as unknown data,sawUnknownDataflips the accumulator to non-empty, and an empty turn is committed as a valid response — the empty-completion failover never engages for the Interactions protocol.The repo does ship the Interactions translator (
internal/translator/gemini/interactions/), and that translator treats"finish"as a terminal event alongside"interaction.completed"(interactions_gemini_response.go:106), taking usage frommetadata.total_usagefirst (internal/translator/common/interactions_usage.go:5-12). The detector does not know any of these events.Reproduction
Feed the detector a complete Interactions turn that produced zero output:
With the Interactions branch absent this returns
false(unknown data commits the stream). The expected result istrueso the conductor can rotate to the next auth instead of handing an empty response to the client.Context
This gap came to light while addressing the equivalent review on the upstream project: router-for-me/CLIProxyAPI#4881 now recognizes
"finish"as a terminal Interactions event readingmetadata.total_usage(upstream commit baf43ba5 onfix/empty-completion-retry). The paired failover work here is #195; this detector gap is separate and older — porting the upstream Interactions branch (event set,interactionsChunk,evalInteractions,evalInteractionsUsage, and the finish handling) would close it.Test expectations
The upstream
TestEmptyCompletionPredicatecases that cover this, worth porting together with the branch:interactions sse stream ending in bare finish with zero output is empty→trueinteractions sse stream ending in bare finish with output is not empty→false