Repository: openai/openai-agents-python

Issue:
[Bug]: Reasoning summaries not returned when using GPT models through LiteLLM (Agents SDK compatibility issue)
### What happened?

When using the OpenAI Agents SDK with GPT reasoning models (e.g., gpt-5), the SDK correctly returns the reasoning.summary items.
However, when routing the same agent through LiteLLM using LitellmModel(model="gpt-5"), the reasoning summary is missing, even though the same model, same instructions, and same ModelSettings(reasoning={"summary": "auto"}) are used.

This results in inconsistent behavior:

OpenAI models → return ReasoningItem + summary

Same model via LiteLLM → No ReasoningItem nor summary

### Steps to Reproduce

1. Install agents SDK and litellm

2. Create an agent using model="gpt-5" and enable reasoning summaries:

```
ModelSettings(reasoning={"summary": "auto"})
```

3. Run:
```
result = await Runner.run(agent, prompt)
```
→ Result contains ReasoningItem with summary

4. Now swap the model for:
```
model = LitellmModel(model="gpt-5")
```

5. Run the same code again.

6. The result does not contain the reasoning item nor summary.

### Expected Behavior

LiteLLM should forward all reasoning-related parameters (e.g., reasoning={"summary": "auto"}) to the upstream OpenAI API exactly as the Agents SDK does.

The response returned through LiteLLM should include the same ResponseReasoningItem.summary objects so that:

reasoning.summary appears in the result

the agent’s output structure remains consistent

reasoning-enabled models via LiteLLM behave the same as direct OpenAI calls

### Actual Behavior

When using LiteLLM:

The reasoning parameter is not forwarded or not mapped correctly.

The OpenAI response returned through LiteLLM contains no reasoning summary.

The Agents SDK receives an incomplete response and produces no ResponseReasoningItem.summary.

But when using direct OpenAI models:

Reasoning summaries appear correctly.

### Root Cause

LiteLLM does not pass reasoning-related fields (e.g., reasoning, reasoning_effort, response_format for reasoning) through to the OpenAI API.

OpenAI reasoning models require these fields to be transmitted or the API will not return reasoning summaries.

### Suggested Fix

Forward the reasoning block exactly as provided to LiteLLM, including:

reasoning.summary

reasoning.effort

any other reasoning-related OpenAI parameters

Ensure the request body passed to OpenAI supports the new reasoning schema introduced for GPT-4.1-R / GPT-5 reasoning models.

Add tests verifying that reasoning summaries appear when using LitellmModel in the Agents SDK.

### Relevant log output

```shell
from agents import Agent, ModelSettings, Runner
from agents.extensions.models.litellm_model import LitellmModel

# Works – returns reasoning summary
reasoning_agent = Agent(
    name="Reasoning Agent",
    instructions="Largest city in the 3rd largest country in the world?",
    model="gpt-5",
    model_settings=ModelSettings(reasoning={"summary": "auto"}),
)

result = await Runner.run(
    reasoning_agent,
    "Largest city in the 3rd largest country in the world?"
)

for item in result.new_items:
    print(item)

# Fails – summary missing
reasoning_agent_with_litellm = Agent(
    name="Reasoning Agent with Litellm",
    instructions="Largest city in the 3rd largest country in the world?",
    model=LitellmModel(model="gpt-5"),
    model_settings=ModelSettings(reasoning={"summary": "auto"}),
)

result_litellm = await Runner.run(
    reasoning_agent_with_litellm,
    "Largest city in the 3rd largest country in the world?"
)

for item in result_litellm.new_items:
    print(item)

Output without LiteLLM:

ReasoningItem(agent=Agent(name='Reasoning Agent', ... status=None), type='reasoning_item')
MessageOutputItem(agent=Agent(name='Reasoning Agent', ..., type='message'), type='message_output_item')

Output with LiteLLM:

MessageOutputItem(agent=Agent(name='Reasoning Agent with Litellm', handoff_description=None, ..., type='message'), type='message_output_item')
```

### Are you a ML Ops Team?

No

### What LiteLLM version are you on ?

v1.77.3

### Twitter / LinkedIn details

_No response_

Discussion:

Fix (patch):
diff --git a/src/agents/extensions/models/litellm_model.py b/src/agents/extensions/models/litellm_model.py
index 6389b38b2..439146c6c 100644
--- a/src/agents/extensions/models/litellm_model.py
+++ b/src/agents/extensions/models/litellm_model.py
@@ -339,10 +339,23 @@ async def _fetch_response(
                 f"Response format: {response_format}\n"
             )
 
-        reasoning_effort = model_settings.reasoning.effort if model_settings.reasoning else None
+        # Build reasoning_effort - use dict only when summary is present (OpenAI feature)
+        # Otherwise pass string for backward compatibility with all providers
+        reasoning_effort: dict[str, Any] | str | None = None
+        if model_settings.reasoning:
+            if model_settings.reasoning.summary is not None:
+                # Dict format when summary is needed (OpenAI only)
+                reasoning_effort = {
+                    "effort": model_settings.reasoning.effort,
+                    "summary": model_settings.reasoning.summary,
+                }
+            elif model_settings.reasoning.effort is not None:
+                # String format for compatibility with all providers
+                reasoning_effort = model_settings.reasoning.effort
+
         # Enable developers to pass non-OpenAI compatible reasoning_effort data like "none"
         # Priority order:
-        #  1. model_settings.reasoning.effort
+        #  1. model_settings.reasoning (effort + summary)
         #  2. model_settings.extra_body["reasoning_effort"]
         #  3. model_settings.extra_args["reasoning_effort"]
         if (


Test changes:
diff --git a/tests/models/test_litellm_extra_body.py b/tests/models/test_litellm_extra_body.py
index c33e09da6..e85d2c3e8 100644
--- a/tests/models/test_litellm_extra_body.py
+++ b/tests/models/test_litellm_extra_body.py
@@ -115,6 +115,7 @@ async def fake_acompletion(model, messages=None, **kwargs):
         previous_response_id=None,
     )
 
+    # reasoning_effort is string when no summary is provided (backward compatible)
     assert captured["reasoning_effort"] == "low"
     assert settings.extra_body == {"reasoning_effort": "high"}
 
@@ -155,3 +156,46 @@ async def fake_acompletion(model, messages=None, **kwargs):
     assert captured["reasoning_effort"] == "none"
     assert captured["custom_param"] == "custom"
     assert settings.extra_args == {"reasoning_effort": "low", "custom_param": "custom"}
+
+
+@pytest.mark.allow_call_model_methods
+@pytest.mark.asyncio
+async def test_reasoning_summary_is_preserved(monkeypatch):
+    """
+    Ensure reasoning.summary is preserved when passing ModelSettings.reasoning.
+
+    This test verifies the fix for GitHub issue:
+    https://github.com/BerriAI/litellm/issues/17428
+
+    Previously, only reasoning.effort was extracted, losing the summary field.
+    Now we pass a dict with both effort and summary to LiteLLM.
+    """
+    from openai.types.shared import Reasoning
+
+    captured: dict[str, object] = {}
+
+    async def fake_acompletion(model, messages=None, **kwargs):
+        captured.update(kwargs)
+        msg = Message(role="assistant", content="ok")
+        choice = Choices(index=0, message=msg)
+        return ModelResponse(choices=[choice], usage=Usage(0, 0, 0))
+
+    monkeypatch.setattr(litellm, "acompletion", fake_acompletion)
+    settings = ModelSettings(
+        reasoning=Reasoning(effort="medium", summary="auto"),
+    )
+    model = LitellmModel(model="test-model")
+
+    await model.get_response(
+        system_instructions=None,
+        input=[],
+        model_settings=settings,
+        tools=[],
+        output_schema=None,
+        handoffs=[],
+        tracing=ModelTracing.DISABLED,
+        previous_response_id=None,
+    )
+
+    # Both effort and summary should be preserved in the dict
+    assert captured["reasoning_effort"] == {"effort": "medium", "summary": "auto"}