fix(responses-api): simplify reasoning item handling for gpt-5-codex (#15815)

Simplifies the _handle_reasoning_item method to only filter out None values
without attempting to construct ResponseReasoningItem objects. This approach:
- Avoids mutation of encrypted_content in reasoning items
- Fixes compatibility with gpt-5-codex and Codex CLI
- Removes dependency on ResponseReasoningItem import
- Simplifies the code by removing complex fallback logic

The previous approach was trying to construct ResponseReasoningItem objects
with required fields (id, summary), which caused issues with encrypted
payloads. The new approach simply filters None values while preserving
all other data intact.

Issue: https://github.com/BerriAI/litellm/issues/13484
Reported-by: rajpathak (OpenAI)
Tested-by: rajpathak (OpenAI Codex CLI)
This commit is contained in:
Alexsander Hamir
2025-10-23 08:41:37 -07:00
committed by GitHub
parent 573306f3cd
commit cc63cf28ff
@@ -1,7 +1,6 @@
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
@@ -94,14 +93,12 @@ 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 status=None
# Handle reasoning items specifically to filter out None values without mutating contents
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:
@@ -112,43 +109,15 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig):
def _handle_reasoning_item(self, item: Dict[str, Any]) -> Dict[str, Any]:
"""
Handle reasoning items specifically to filter out status=None using OpenAI's model.
Handle reasoning items specifically to filter out None values.
Issue: https://github.com/BerriAI/litellm/issues/13484
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
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.
"""
if item.get("type") == "reasoning":
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
filtered_item = {k: v for k, v in item.items() if v is not None}
return filtered_item
return item
def transform_response_api_response(