mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
Add reasoning' field to 'reasoning_content' field in delta
This commit is contained in:
parent
2517c069ca
commit
9678c723b0
2 changed files with 99 additions and 2 deletions
|
|
@ -770,14 +770,36 @@ class OpenAIGPTConfig(BaseLLMModelInfo, BaseConfig):
|
|||
|
||||
|
||||
class OpenAIChatCompletionStreamingHandler(BaseModelResponseIterator):
|
||||
def _map_reasoning_to_reasoning_content(self, choices: list) -> list:
|
||||
"""
|
||||
Map 'reasoning' field to 'reasoning_content' field in delta.
|
||||
|
||||
Some OpenAI-compatible providers (e.g., GLM-5, hosted_vllm) return
|
||||
delta.reasoning, but LiteLLM expects delta.reasoning_content.
|
||||
|
||||
Args:
|
||||
choices: List of choice objects from the streaming chunk
|
||||
|
||||
Returns:
|
||||
List of choices with reasoning field mapped to reasoning_content
|
||||
"""
|
||||
for choice in choices:
|
||||
delta = choice.get("delta", {})
|
||||
if "reasoning" in delta:
|
||||
delta["reasoning_content"] = delta.pop("reasoning")
|
||||
return choices
|
||||
|
||||
def chunk_parser(self, chunk: dict) -> ModelResponseStream:
|
||||
try:
|
||||
choices = chunk.get("choices", [])
|
||||
choices = self._map_reasoning_to_reasoning_content(choices)
|
||||
|
||||
kwargs = {
|
||||
"id": chunk["id"],
|
||||
"object": "chat.completion.chunk",
|
||||
"created": chunk.get("created"),
|
||||
"model": chunk.get("model"),
|
||||
"choices": chunk.get("choices", []),
|
||||
"choices": choices,
|
||||
}
|
||||
if "usage" in chunk and chunk["usage"] is not None:
|
||||
kwargs["usage"] = chunk["usage"]
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ import pytest
|
|||
sys.path.insert(0, os.path.abspath("../../../../.."))
|
||||
|
||||
from litellm.llms.openai.chat.gpt_transformation import (
|
||||
OpenAIGPTConfig,
|
||||
OpenAIChatCompletionStreamingHandler,
|
||||
OpenAIGPTConfig,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -204,6 +204,81 @@ class TestOpenAIChatCompletionStreamingHandler:
|
|||
assert result.choices[0].delta.content == "Hello"
|
||||
assert not hasattr(result, "usage") or result.usage is None
|
||||
|
||||
def test_chunk_parser_maps_reasoning_to_reasoning_content(self):
|
||||
"""
|
||||
Test that chunk_parser maps 'reasoning' field to 'reasoning_content'.
|
||||
|
||||
Some OpenAI-compatible providers (e.g., GLM-5, hosted_vllm) return
|
||||
delta.reasoning, but LiteLLM expects delta.reasoning_content.
|
||||
|
||||
Regression test for: Streaming responses with delta.reasoning field
|
||||
coming back empty when using openai/ or hosted_vllm/ providers.
|
||||
"""
|
||||
handler = OpenAIChatCompletionStreamingHandler(
|
||||
streaming_response=None, sync_stream=True
|
||||
)
|
||||
|
||||
# Simulate a chunk with reasoning field (as returned by GLM-5)
|
||||
chunk = {
|
||||
"id": "chatcmpl-8e3d624de9b12528",
|
||||
"object": "chat.completion.chunk",
|
||||
"created": 1771411455,
|
||||
"model": "glm-5",
|
||||
"choices": [
|
||||
{
|
||||
"index": 0,
|
||||
"delta": {
|
||||
"reasoning": "The capital of France",
|
||||
"role": None,
|
||||
},
|
||||
"finish_reason": None,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
# Parse the chunk
|
||||
parsed_chunk = handler.chunk_parser(chunk)
|
||||
|
||||
# Verify that reasoning was mapped to reasoning_content
|
||||
assert parsed_chunk.choices[0].delta.reasoning_content == "The capital of France"
|
||||
# Verify that the original 'reasoning' field was removed
|
||||
assert not hasattr(parsed_chunk.choices[0].delta, "reasoning")
|
||||
|
||||
def test_chunk_parser_reasoning_field_not_present(self):
|
||||
"""
|
||||
Test that chunks without reasoning field still work correctly.
|
||||
"""
|
||||
handler = OpenAIChatCompletionStreamingHandler(
|
||||
streaming_response=None, sync_stream=True
|
||||
)
|
||||
|
||||
# Simulate a chunk without reasoning field
|
||||
chunk = {
|
||||
"id": "chatcmpl-test",
|
||||
"object": "chat.completion.chunk",
|
||||
"created": 1769511767,
|
||||
"model": "gpt-4o",
|
||||
"choices": [
|
||||
{
|
||||
"delta": {
|
||||
"content": "Regular content",
|
||||
"role": "assistant",
|
||||
},
|
||||
"finish_reason": None,
|
||||
"index": 0,
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
# Parse the chunk
|
||||
parsed_chunk = handler.chunk_parser(chunk)
|
||||
|
||||
# Verify that content is present
|
||||
assert parsed_chunk.choices[0].delta.content == "Regular content"
|
||||
assert parsed_chunk.choices[0].delta.role == "assistant"
|
||||
# Verify that reasoning_content is not set (it should be deleted by Delta.__init__)
|
||||
assert not hasattr(parsed_chunk.choices[0].delta, "reasoning_content")
|
||||
|
||||
|
||||
class TestPromptCacheKeyIntegration:
|
||||
"""Tests for prompt_cache_key support"""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue