diff --git a/litellm/llms/ollama/completion/transformation.py b/litellm/llms/ollama/completion/transformation.py index 3826a47038..3565f090b0 100644 --- a/litellm/llms/ollama/completion/transformation.py +++ b/litellm/llms/ollama/completion/transformation.py @@ -24,6 +24,8 @@ from litellm.types.utils import ( ModelResponse, ModelResponseStream, ProviderField, + StreamingChoices, + Delta, ) from ..common_utils import OllamaError, _convert_image @@ -423,7 +425,7 @@ class OllamaTextCompletionResponseIterator(BaseModelResponseIterator): ) -> Union[GenericStreamingChunk, ModelResponseStream]: return self.chunk_parser(json.loads(str_line)) - def chunk_parser(self, chunk: dict) -> GenericStreamingChunk: + def chunk_parser(self, chunk: dict) -> Union[GenericStreamingChunk, ModelResponseStream]: try: if "error" in chunk: raise Exception(f"Ollama Error - {chunk}") @@ -460,13 +462,15 @@ class OllamaTextCompletionResponseIterator(BaseModelResponseIterator): usage=None, ) elif "thinking" in chunk and not chunk["response"]: - # Handle GPT-OSS models that include 'thinking' field with empty response - # These are intermediate chunks that don't contain user-facing content - return GenericStreamingChunk( - text="", - is_finished=is_finished, - finish_reason="", - usage=None, + # Return reasoning content as ModelResponseStream so UIs can render it + thinking_content = chunk.get("thinking") or "" + return ModelResponseStream( + choices=[ + StreamingChoices( + index=0, + delta=Delta(reasoning_content=thinking_content), + ) + ] ) else: raise Exception(f"Unable to parse ollama chunk - {chunk}") diff --git a/tests/test_litellm/llms/ollama/test_ollama_completion_transformation.py b/tests/test_litellm/llms/ollama/test_ollama_completion_transformation.py index 36f282cff8..985d51f99d 100644 --- a/tests/test_litellm/llms/ollama/test_ollama_completion_transformation.py +++ b/tests/test_litellm/llms/ollama/test_ollama_completion_transformation.py @@ -14,7 +14,7 @@ from litellm.llms.ollama.completion.transformation import ( OllamaConfig, OllamaTextCompletionResponseIterator, ) -from litellm.types.utils import Message, ModelResponse +from litellm.types.utils import Message, ModelResponse, ModelResponseStream class TestOllamaConfig: @@ -178,11 +178,10 @@ class TestOllamaTextCompletionResponseIterator: result = iterator.chunk_parser(chunk_with_thinking) - # Should return empty text and not be finished - assert result["text"] == "" - assert result["is_finished"] is False - assert result["finish_reason"] == "" - assert result["usage"] is None + # Should return a ModelResponseStream with reasoning content + assert isinstance(result, ModelResponseStream) + assert result.choices and result.choices[0].delta is not None + assert getattr(result.choices[0].delta, "reasoning_content") == "User" def test_chunk_parser_normal_response(self): """Test that normal response chunks still work."""