mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-09 09:08:47 +00:00
fix: handle reasoning parameters and response in responses bridge (#12433)
* fix: handle reasoning parameters and response in responses bridge Updates the OpenAI completions/responses bridge to map reasoning_effort to reasoning parameters, and the chunk parser to return reasoning_content. ref: 12432 * fix: using type checked objects in responses bridge transform ref: 12432
This commit is contained in:
@@ -24,6 +24,7 @@ from litellm.llms.base_llm.base_model_iterator import BaseModelResponseIterator
|
||||
from litellm.llms.base_llm.bridges.completion_transformation import (
|
||||
CompletionTransformationBridge,
|
||||
)
|
||||
from litellm.types.llms.openai import Reasoning
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from openai.types.responses import ResponseInputImageParam
|
||||
@@ -152,11 +153,12 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
|
||||
)
|
||||
elif key in ResponsesAPIOptionalRequestParams.__annotations__.keys():
|
||||
responses_api_request[key] = value # type: ignore
|
||||
elif key == "metadata":
|
||||
elif key in ("metadata"):
|
||||
responses_api_request["metadata"] = value
|
||||
elif key == "previous_response_id":
|
||||
# Support for responses API session management
|
||||
elif key in ("previous_response_id"):
|
||||
responses_api_request["previous_response_id"] = value
|
||||
elif key == "reasoning_effort":
|
||||
responses_api_request["reasoning"] = self._map_reasoning_effort(value)
|
||||
|
||||
# Get stream parameter from litellm_params if not in optional_params
|
||||
stream = optional_params.get("stream") or litellm_params.get("stream", False)
|
||||
@@ -465,6 +467,16 @@ class LiteLLMResponsesTransformationHandler(CompletionTransformationBridge):
|
||||
)
|
||||
return cast(List["ALL_RESPONSES_API_TOOL_PARAMS"], responses_tools)
|
||||
|
||||
def _map_reasoning_effort(self, reasoning_effort: str) -> Optional[Reasoning]:
|
||||
if reasoning_effort == "high":
|
||||
return Reasoning(effort="high", summary="detailed")
|
||||
elif reasoning_effort == "medium":
|
||||
# docs say "summary": "concise" is also an option, but it was rejected in practice, so defaulting "auto"
|
||||
return Reasoning(effort="medium", summary="auto")
|
||||
elif reasoning_effort == "low":
|
||||
return Reasoning(effort="low", summary="auto")
|
||||
return None
|
||||
|
||||
def _map_responses_status_to_finish_reason(self, status: Optional[str]) -> str:
|
||||
"""Map responses API status to chat completion finish_reason"""
|
||||
if not status:
|
||||
@@ -623,6 +635,22 @@ class OpenAiResponsesToChatCompletionStreamIterator(BaseModelResponseIterator):
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Chat provider: Invalid text delta {parsed_chunk}")
|
||||
elif event_type == "response.reasoning_summary_text.delta":
|
||||
content_part = parsed_chunk.get("delta", None)
|
||||
if content_part:
|
||||
from litellm.types.utils import (
|
||||
Delta,
|
||||
ModelResponseStream,
|
||||
StreamingChoices,
|
||||
)
|
||||
|
||||
return ModelResponseStream(
|
||||
choices=[
|
||||
StreamingChoices(
|
||||
index=parsed_chunk.get("summary_index"), delta=Delta(reasoning_content=content_part)
|
||||
)
|
||||
]
|
||||
)
|
||||
else:
|
||||
pass
|
||||
# For any unhandled event types, create a minimal valid chunk or skip
|
||||
|
||||
+34
@@ -50,3 +50,37 @@ def test_convert_chat_completion_messages_to_responses_api_image_input():
|
||||
|
||||
print("response: ", response)
|
||||
assert response[0]["content"][1]["image_url"] == user_image
|
||||
|
||||
|
||||
def test_openai_responses_chunk_parser_reasoning_summary():
|
||||
from litellm.completion_extras.litellm_responses_transformation.transformation import (
|
||||
OpenAiResponsesToChatCompletionStreamIterator,
|
||||
)
|
||||
from litellm.types.utils import Delta, ModelResponseStream, StreamingChoices
|
||||
|
||||
iterator = OpenAiResponsesToChatCompletionStreamIterator(
|
||||
streaming_response=None, sync_stream=True
|
||||
)
|
||||
|
||||
chunk = {
|
||||
"delta": "**Compar",
|
||||
"item_id": "rs_686d544208748198b6912e27b7c299c00e24bd875d35bade",
|
||||
"output_index": 0,
|
||||
"sequence_number": 4,
|
||||
"summary_index": 0,
|
||||
"type": "response.reasoning_summary_text.delta",
|
||||
}
|
||||
|
||||
result = iterator.chunk_parser(chunk)
|
||||
|
||||
assert isinstance(result, ModelResponseStream)
|
||||
assert len(result.choices) == 1
|
||||
choice = result.choices[0]
|
||||
assert isinstance(choice, StreamingChoices)
|
||||
assert choice.index == 0
|
||||
delta = choice.delta
|
||||
assert isinstance(delta, Delta)
|
||||
assert delta.content is None
|
||||
assert delta.reasoning_content == "**Compar"
|
||||
assert delta.tool_calls is None
|
||||
assert delta.function_call is None
|
||||
|
||||
Reference in New Issue
Block a user