Skip to content

fix(sambanova): emit empty delta on usage-only final stream chunk - #22337

Closed
ebarkhordar wants to merge 1 commit into
run-llama:mainfrom
ebarkhordar:fix/sambanova-stream-usage-delta
Closed

fix(sambanova): emit empty delta on usage-only final stream chunk#22337
ebarkhordar wants to merge 1 commit into
run-llama:mainfrom
ebarkhordar:fix/sambanova-stream-usage-delta

Conversation

@ebarkhordar

Copy link
Copy Markdown

Description

SambaNovaCloud sets stream_options={"include_usage": True} by default (base.py:201-204), so a streaming response ends with a usage-only chunk where choices == []. In stream_chat, that final chunk takes the else branch (base.py:553-561), which builds additional_kwargs but never reassigns content_delta. The trailing yield then re-emits the previous chunk's token as its delta:

yield ChatResponse(
    message=ChatMessage(role=role, content=content, additional_kwargs=additional_kwargs),
    delta=content_delta,   # still the last text chunk's content on the usage chunk
    raw=partial_response,
)

So "".join(r.delta for r in client.stream_chat(...)) no longer equals the final message.content: any consumer that rebuilds the text from deltas (the usual streaming pattern) gets the last token duplicated. message.content itself stays correct, only the re-emitted delta is wrong.

The invariant a streaming generator should hold is that the concatenation of the deltas equals the final message.content, and a usage-only chunk carries no new text, so its delta must be "". This is what the OpenAI-compatible reference implementations do.

There is no tracking issue; this was found by reading the code and reproduced as described below.

Fix

Set content_delta = "" in the else (empty-choices) branch so the usage-only chunk yields an empty delta. One line plus a comment; no behavior changes for the text chunks or for message.content.

Version Bump?

  • Yes (0.6.0 to 0.6.1)

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • I added a new unit test.

Added test_stream_chat_usage_chunk_emits_empty_delta. It stubs _handle_streaming_request to yield a text chunk followed by the usage-only chunk (choices == [], plus usage/model/system_fingerprint/created) that the provider sends when include_usage=True, then asserts "".join(deltas) == final content == "Hello" and that the last delta is "".

Verified in a clean Docker container (python:3.11) against current main:

  • Before the fix the test fails with assert 'HelloHello' == 'Hello'.
  • After the fix it passes, and the full tests/test_llms_sambanovasystems.py file is green (the live-API tests are skipped without a key).
  • ruff check, ruff format --check, and codespell pass on the two changed files.

Not verified: I did not call the real SambaNova Cloud endpoint (no API key). The test reproduces the usage-chunk shape via a stub, using the exact fields the existing else branch already reads.

AI assistance: this change was prepared with AI assistance. I reproduced the bug on main, validated the fix with the Docker differential and repo lint above, and reviewed the diff line by line.

@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Jul 12, 2026
SambaNovaCloud enables stream_options={'include_usage': True} by default,
so the stream ends with a usage-only chunk where choices == []. That chunk
takes the else branch, which never reassigns content_delta, so the final
yield re-emits the previous chunk's token as its delta. This breaks the
streaming invariant that ''.join(deltas) equals the final message.content.

Set content_delta to '' on the usage-only chunk. Adds a regression test
that stubs the streaming request and asserts the concatenated deltas equal
the final content.
@ebarkhordar
ebarkhordar force-pushed the fix/sambanova-stream-usage-delta branch from dab83c2 to a17bb50 Compare July 13, 2026 08:45
@ebarkhordar

Copy link
Copy Markdown
Author

No rush on this. CI is green and the branch still applies cleanly on main, so it is ready whenever it fits your review queue. Happy to add a regression test for the usage-only final chunk if that would help.

@ebarkhordar

Copy link
Copy Markdown
Author

Correcting a line in my note above: I said CI is green, and that was wrong. The only check reporting on this PR is check-new-pyproject. Unit Testing, Build Package, Core Typecheck, Linting and CodeQL are all held awaiting approval of the fork workflow run, so none of them has executed against this branch and I have no CI evidence to point at.

What I do have is local: the added regression test fails on main and passes with the fix. The branch does still apply cleanly on main.

@ebarkhordar

Copy link
Copy Markdown
Author

Closing this to keep my open PR list at a size I can actually shepherd. Nothing here is
blocked on anyone and this is not a nudge.

The change still applies if it is useful later, and I am happy to reopen and rebase it
at any point. The branch stays up so reopening is a click.

@ebarkhordar ebarkhordar closed this Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant