From 24e806bdc83c84d2702ed96fa86817a290e2841e Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Fri, 20 Feb 2026 12:57:11 -0800 Subject: [PATCH] =?UTF-8?q?fix(responses):=20fix=20O(n=C2=B2)=20CPU=20over?= =?UTF-8?q?head=20in=20reasoning=20streaming=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit stream_chunk_builder was called on every reasoning chunk, rebuilding the entire response from all collected chunks each time. Replace with incremental accumulation of reasoning_content parts, only joining at reasoning end. --- .../streaming_iterator.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/litellm/responses/litellm_completion_transformation/streaming_iterator.py b/litellm/responses/litellm_completion_transformation/streaming_iterator.py index 5c05526442d..817575f30d3 100644 --- a/litellm/responses/litellm_completion_transformation/streaming_iterator.py +++ b/litellm/responses/litellm_completion_transformation/streaming_iterator.py @@ -102,6 +102,7 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): self._reasoning_active = False self._reasoning_done_emitted = False self._reasoning_item_id: Optional[str] = None + self._accumulated_reasoning_content_parts: List[str] = [] def _get_or_assign_tool_output_index(self, call_id: str) -> int: @@ -812,17 +813,13 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator): # Proceed to transformation self.collected_chat_completion_chunks.append(chunk) if self._reasoning_active and not self._reasoning_done_emitted: - # get raw ModelResponse - text_reasoning = self.create_litellm_model_response() - # reasoning_content only + # Incrementally accumulate reasoning content instead of + # calling stream_chunk_builder on every chunk (O(n²)) + delta = chunk.choices[0].delta if chunk.choices else None + if delta and hasattr(delta, "reasoning_content") and delta.reasoning_content: + self._accumulated_reasoning_content_parts.append(delta.reasoning_content) if self._is_reasoning_end(chunk): - reasoning_content = "" - # best effort to obtain reasoning_content from chat model response - if text_reasoning and text_reasoning.choices: - choice = text_reasoning.choices[0] - # Check if it's a Choices object (has message) or StreamingChoices (has delta) - if hasattr(choice, "message"): - reasoning_content = getattr(choice.message, "reasoning_content", "") or "" + reasoning_content = "".join(self._accumulated_reasoning_content_parts) # Ensure we have a valid reasoning_item_id reasoning_item_id = self._reasoning_item_id or self._cached_reasoning_item_id or f"rs_{uuid.uuid4()}"