Skip to content

fix(openai,openrouter,huggingface): keep streamed tool calls apart when the provider omits index - #2641

Merged
Luca Forstner (lforst) merged 3 commits into
braintrustdata:mainfrom
pcbeingused333:fix/openai-stream-tool-calls-without-index
Oct 20, 2026
Merged

Luca Forstner (lforst) merged 3 commits into
braintrustdata:mainfrom
pcbeingused333:fix/openai-stream-tool-calls-without-index

Conversation

@pcbeingused333

Copy link
Copy Markdown
Contributor

What

The chat stream aggregators of the OpenAI, OpenRouter and Hugging Face plugins group streamed tool call deltas by delta.index. Some providers (Gemini's OpenAI-compatible endpoint, for example) send each parallel call whole in its own chunk and leave index unset. Without an index the calls end up in one slot (the OpenAI aggregator keys them under undefined; OpenRouter and Hugging Face fall back to slot 0): a new id overwrites or replaces the previous call and the arguments are concatenated, so the logged output shows one broken call instead of several.

Change

When index is missing, it is resolved from the latest call: a delta with an id that differs from the latest call's id starts the next call, and a delta without an id continues the latest one (so argument fragments still merge). Deltas that carry an index behave exactly as before. The same rule is applied in the three aggregators. Adds a patch changeset.

Tests

Two new cases in openai-plugin.test.ts (id-less continuation fragments followed by a second call, and several calls without index in one chunk) plus one case each in openrouter-plugin.test.ts and huggingface-plugin.test.ts. All four fail on main (the OpenAI chunk case loses call_1 entirely) and pass with the change; the other tests in those three files still pass (102 in total). prettier --check and tsc --noEmit are clean. These are unit tests of the aggregation functions, so they need no recorded API calls.

This PR was made with Claude Code (Anthropic); the tests above were run on main and on this branch.

…ndex

aggregateChatCompletionChunks keyed streamed tool calls by delta.index.
Some OpenAI-compatible providers send tool call deltas without an index,
so every call landed under the key `undefined`: the second call overwrote
the id of the first and the arguments of all calls were concatenated.

Resolve a missing index from the latest call: a delta with an id that
differs from the latest call's id starts the next call, a delta without an
id continues it. Deltas that carry an index behave as before.

Tests cover id-less continuation fragments and several unindexed calls in
one chunk; both failed before the change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ndex

Same shape as the OpenAI plugin fix. Both aggregators fell back to slot 0
when a tool call delta had no index: OpenRouter replaced slot 0 when a new
id arrived (losing the first call), Hugging Face kept the first call's id
and name and concatenated the arguments of every call.

A missing index is now resolved from the latest call: a delta with an id
that differs from the latest id starts the next call, a delta without an id
continues it. Deltas with an index behave as before.

Tests for both aggregators failed before the change.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@lforst
Luca Forstner (lforst) merged commit 403ae94 into braintrustdata:main Oct 20, 2026
56 checks passed
@AbhiPrasad

Copy link
Copy Markdown
Member

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.

3 participants