Skip to content

fix(telemetry): record a LiteLlm streamed answer once, not chunks plus aggregate - #7694

Draft
ishan-1010 wants to merge 1 commit into
google:mainfrom
ishan-1010:fix-litellm-stream-output-once
Draft

ishan-1010 wants to merge 1 commit into
google:mainfrom
ishan-1010:fix-litellm-stream-output-once

Conversation

@ishan-1010

Copy link
Copy Markdown

Link to Issue or Description of Change

Problem:
LiteLlm streams chunks without a finish reason and then yields their aggregate with one. record_llm_response keeps recording until a finish reason arrives, so gen_ai.output.messages held every chunk and then the whole answer again.

Solution:
As suggested in #7630: when a complete response with a finish reason follows only partial chunks without one, drop the chunks' output messages and keep the aggregate. Streams whose last chunk carries the finish reason (Gemini) and streams that fail before their aggregate keep their chunks.

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.
  • All unit tests pass locally.

New tests in tests/unittests/telemetry/test_instrumentation.py:

  • test_aggregate_after_unfinished_chunks_is_recorded_once (fails on main: 3 messages)
  • test_tool_call_aggregate_replaces_its_partial_call_chunks (fails on main: a call with arguments: null plus the real one)
  • test_stream_cut_off_before_its_aggregate_keeps_its_chunks
  • test_stream_whose_last_chunk_finishes_keeps_its_chunks (Gemini shape)
tests/unittests/telemetry: 1023 passed, 9 xfailed (functional goldens unchanged)
tests/unittests/models + flows: 2818 passed
pre-commit: passed

Manual End-to-End (E2E) Tests:

gen_ai.output.messages on main vs this PR, LiteLlm with StreamingMode.SSE:

stream main this PR
groq openai/gpt-oss-20b, text answer 137 1
groq openai/gpt-oss-20b, tool call 36 incl. tool_call with arguments: null 1: text + tool_call({"city": "Oslo"})
openai provider vs a local SSE server, text in 5 chunks 6 1
same, tool call args in 3 pieces 5 1

Same counts in the completion log with EVENT_ONLY.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules.

Additional context

The silent cut-off from #7630 (a dropped stream is finalized as stop) is tracked separately in #7693.

…s aggregate

LiteLlm streams chunks without a finish reason and then yields their
aggregate with one. record_llm_response kept recording until that
finish reason, so gen_ai.output.messages held every chunk and then the
whole answer again (a 5-chunk answer was 6 messages, a streamed tool
call 4 half-built calls plus the real one).

When a complete response with a finish reason follows only partial
chunks without one, drop the chunks' output messages and keep the
aggregate. Streams whose last chunk carries the finish reason (Gemini)
and streams that fail before their aggregate keep their chunks.

Fixes google#7630
@GBX-Max1220

Copy link
Copy Markdown

Thanks, Ishan—I’ve attached two supplemental native tests against 2c5773f0:

  • An interrupted stream propagates the original exception, retains partial messages on the finished span, and records ERROR status.
  • EVENT_ONLY records the completed aggregate once.

The full test_instrumentation.py passes locally: 104 tests. I set the test process’s trace sampler to always_on because this environment defaults to 1% sampling.
ADK_7694_Supplemental_Native_Tests_20261023.zip

I’m not claiming a full telemetry-suite pass: the broader run hit a missing SOCKS dependency in the functional tests. The patch only changes tests and was AI-assisted.

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.

LiteLlm with SSE streaming records the answer twice in gen_ai.output.messages

3 participants