Repository navigation
[issue-9069] [SDK] fix: keep function calls and all text in streamed Gemini traces - #9320
surlymochan wants to merge 1 commit into
Conversation
… in streamed Gemini traces
| """A part is mergeable text only when ``text`` is its sole set field. | ||
|
|
||
| Any part carrying another payload (a function call, inline data, a | ||
| thought signature, ...) must stay a separate part, otherwise merging | ||
| would silently drop that payload. | ||
| """ |
There was a problem hiding this comment.
Private helper docstring exposes implementation details
The private helper’s docstring duplicates the rationale in aggregate_response_content_items and documents implementation details like field inspection and payload types as if they were part of the contract; remove it or keep only a brief local rationale if needed.
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
`sdks/python/src/opik/integrations/genai/generations_aggregators.py` around lines 7-12,
simplify the `_is_plain_text_part` helper’s docstring. Remove the
implementation-specific explanation of `model_dump`, set-field inspection, and payload
examples, or replace it with one concise local rationale explaining that only text-only
parts may be merged; keep the helper’s behavior unchanged.
| merged_parts: List[genai_types.Part] = [] | ||
| for item in items: | ||
| for part in _parts_of(item): | ||
| part_copy = part.model_copy(deep=True) | ||
| if ( | ||
| _is_plain_text_part(part_copy) | ||
| and merged_parts | ||
| and _is_plain_text_part(merged_parts[-1]) | ||
| ): | ||
| merged_parts[-1].text = (merged_parts[-1].text or "") + ( | ||
| part_copy.text or "" | ||
| ) |
There was a problem hiding this comment.
Aggregation failures break the primary operation
A malformed provider part or serialization failure can escape aggregate_response_content_items and fail trace/span construction instead of letting the primary operation continue. Catch expected aggregation errors at the boundary, log them with exc_info=True and response context, and return the unaggregated/degraded response or omit aggregated output.
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents
Before applying, verify this suggestion against the current code. In
`sdks/python/src/opik/integrations/genai/generations_aggregators.py` around lines 47-60,
update `aggregate_response_content_items` so failures from `model_copy`,
`_is_plain_text_part`/`model_dump`, or part mutation cannot escape the stream
aggregator. Wrap the aggregation logic in a broad, appropriate exception boundary, log
the exception with `exc_info=True` and relevant response context, then return an
unaggregated or otherwise degraded response that preserves the underlying operation
instead of failing trace/span construction.
Details
Streamed Gemini traces lost function calls:
aggregate_response_content_itemsjoined every chunk's.text(which isNonefor a function-call-only chunk) and wrote the result into the last chunk's first part, dropping function calls from earlier chunks and overwriting a function-call part in the last chunk with plain text. The aggregator now walks every chunk's first candidate parts in arrival order, concatenating consecutive plain-text parts into one part and keeping every other part (function calls, inline data, ...) as its own part. Response-level metadata still comes from the last chunk, and a trailing usage-only chunk without candidates no longer discards the parts aggregated from earlier chunks.Note: #9094 addressed the same issue but was closed by the external-PR-limit bot (author at the 3-open-PR limit), not on its merits; this is a fresh implementation against current
main.Change checklist
Issues
AI-WATERMARK
AI-WATERMARK: yes
Testing
test_function_call_between_text_chunks_is_kept_in_orderfailed on the old code with a singlePart(text='Checking done')— the function call was gone.PYTHONPATH=src pytest tests/unit/integrations/genai/test_generations_aggregators.py— 6 passed, covering text split across chunks, function calls between/after text, a function call in the last chunk, text after a function call in the same chunk, a usage-only trailing chunk, and last-chunk metadata.ruff checkandruff format --checkclean on both changed files.opik_tracker.py, whose interface is unchanged.Documentation