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:
Adam Holmberg
2025-07-10 20:06:45 -05:00
committed by GitHub
parent 8aa616698e
commit 6ff06bb96f
2 changed files with 65 additions and 3 deletions
@@ -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
@@ -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