From d53a8b3e8d8c158aca976393c40cb13d3ee9c9cf Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 23 Oct 2025 11:17:36 -0700 Subject: [PATCH] Revert "fix(responses-api): simplify reasoning item handling for gpt-5-codex (#15815)" This reverts commit cc63cf28ffa1a30e7c7a39c3e59b8a18c50b97b3. --- .../llms/openai/responses/transformation.py | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/litellm/llms/openai/responses/transformation.py b/litellm/llms/openai/responses/transformation.py index bb39ce7a824..c3abd5155db 100644 --- a/litellm/llms/openai/responses/transformation.py +++ b/litellm/llms/openai/responses/transformation.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING, Any, Dict, Optional, Union, cast, get_type_hints import httpx +from openai.types.responses import ResponseReasoningItem from pydantic import BaseModel import litellm @@ -93,12 +94,14 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig): if isinstance(item, BaseModel): validated_input.append(item.model_dump(exclude_none=True)) elif isinstance(item, dict): - # Handle reasoning items specifically to filter out None values without mutating contents + # Handle reasoning items specifically to filter out status=None verbose_logger.debug(f"Handling reasoning item: {item}") if item.get("type") == "reasoning": + # Type assertion since we know it's a dict at this point dict_item = cast(Dict[str, Any], item) filtered_item = self._handle_reasoning_item(dict_item) else: + # For other dict items, just pass through filtered_item = cast(Dict[str, Any], item) validated_input.append(filtered_item) else: @@ -109,15 +112,43 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig): def _handle_reasoning_item(self, item: Dict[str, Any]) -> Dict[str, Any]: """ - Handle reasoning items specifically to filter out None values. + Handle reasoning items specifically to filter out status=None using OpenAI's model. Issue: https://github.com/BerriAI/litellm/issues/13484 - OpenAI API does not accept ReasoningItem(status=None), so we need to - make sure optional fields with None are stripped while leaving the - encrypted payload untouched. + OpenAI API does not accept ReasoningItem(status=None), so we need to: + 1. Check if the item is a reasoning type + 2. Create a ResponseReasoningItem object with the item data + 3. Convert it back to dict with exclude_none=True to filter None values """ if item.get("type") == "reasoning": - filtered_item = {k: v for k, v in item.items() if v is not None} - return filtered_item + try: + # Ensure required fields are present for ResponseReasoningItem + item_data = dict(item) + if "summary" not in item_data: + item_data["summary"] = ( + item_data.get("reasoning_content", "")[:100] + "..." + if len(item_data.get("reasoning_content", "")) > 100 + else item_data.get("reasoning_content", "") + ) + + # Create ResponseReasoningItem object from the item data + reasoning_item = ResponseReasoningItem(**item_data) + + # Convert back to dict with exclude_none=True to exclude None fields + dict_reasoning_item = reasoning_item.model_dump(exclude_none=True) + + return dict_reasoning_item + except Exception as e: + verbose_logger.debug( + f"Failed to create ResponseReasoningItem, falling back to manual filtering: {e}" + ) + # Fallback: manually filter out known None fields + filtered_item = { + k: v + for k, v in item.items() + if v is not None + or k not in {"status", "content", "encrypted_content"} + } + return filtered_item return item def transform_response_api_response(