mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-05 02:23:42 +00:00
fix: Pass extra_body to provider in Responses API requests (#16320)
## Problem
The `extra_body` parameter in `litellm.responses()` and `litellm.aresponses()`
was being accepted but never passed to the HTTP request sent to the LLM provider.
This prevented users from sending custom/experimental parameters to provider APIs.
## Changes
- Added `data.update(extra_body)` in `async_response_api_handler` (line 2138)
- Added `data.update(extra_body)` in `response_api_handler` (line 2012)
- Added tests to `test_openai_responses_api.py` for extra_body functionality
## Testing
- Tests verify extra_body params are passed in both sync and async modes
- Existing Responses API tests continue to pass
- Manually verified with OpenAI API that custom params are sent correctly
## Impact
Users can now pass custom/experimental parameters via extra_body:
```python
litellm.aresponses(
model="gpt-4o",
input="hello",
extra_body={"custom_param": "value"} # Now works!
)
```
This aligns with the OpenAI SDK pattern and matches behavior in other
LiteLLM endpoints (completion, embedding, etc.) that already support extra_body.
This commit is contained in:
@@ -2009,6 +2009,9 @@ class BaseLLMHTTPHandler:
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
if extra_body:
|
||||
data.update(extra_body)
|
||||
|
||||
## LOGGING
|
||||
logging_obj.pre_call(
|
||||
input=input,
|
||||
@@ -2135,6 +2138,9 @@ class BaseLLMHTTPHandler:
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
if extra_body:
|
||||
data.update(extra_body)
|
||||
|
||||
## LOGGING
|
||||
logging_obj.pre_call(
|
||||
input=input,
|
||||
|
||||
@@ -1676,3 +1676,133 @@ async def test_openai_streaming_logging():
|
||||
|
||||
await asyncio.sleep(2)
|
||||
assert tcl.validate_usage, "Usage should be validated"
|
||||
|
||||
|
||||
# Tests for extra_body parameter passing
|
||||
class MockResponse:
|
||||
def __init__(self, json_data, status_code):
|
||||
self._json_data = json_data
|
||||
self.status_code = status_code
|
||||
self.text = str(json_data)
|
||||
self.headers = httpx.Headers({})
|
||||
|
||||
def json(self):
|
||||
return self._json_data
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def extra_body_mock_response_data():
|
||||
return {
|
||||
"id": "resp_test123",
|
||||
"object": "response",
|
||||
"created_at": 1234567890,
|
||||
"status": "completed",
|
||||
"model": "gpt-4o",
|
||||
"output": [
|
||||
{
|
||||
"type": "message",
|
||||
"id": "msg_123",
|
||||
"status": "completed",
|
||||
"role": "assistant",
|
||||
"content": [
|
||||
{"type": "output_text", "text": "Hello!", "annotations": []}
|
||||
],
|
||||
}
|
||||
],
|
||||
"usage": {"input_tokens": 10, "output_tokens": 5, "total_tokens": 15},
|
||||
"parallel_tool_calls": True,
|
||||
"text": {"format": {"type": "text"}},
|
||||
"error": None,
|
||||
"metadata": {},
|
||||
"temperature": 1.0,
|
||||
"reasoning": {"effort": None, "summary": None},
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_aresponses_extra_body_params_passed(extra_body_mock_response_data):
|
||||
"""Test that extra_body parameters are passed in async mode."""
|
||||
with patch(
|
||||
"litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post",
|
||||
new_callable=AsyncMock,
|
||||
) as mock_post:
|
||||
mock_post.return_value = MockResponse(extra_body_mock_response_data, 200)
|
||||
|
||||
response = await litellm.aresponses(
|
||||
model="gpt-4o",
|
||||
input="Test input",
|
||||
max_output_tokens=20,
|
||||
extra_body={
|
||||
"custom_param_1": "value1",
|
||||
"custom_param_2": {"nested": "value2"},
|
||||
"experimental_feature": True,
|
||||
},
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
assert response.id is not None
|
||||
|
||||
request_body = mock_post.call_args.kwargs["json"]
|
||||
|
||||
assert "custom_param_1" in request_body
|
||||
assert request_body["custom_param_1"] == "value1"
|
||||
assert "custom_param_2" in request_body
|
||||
assert request_body["custom_param_2"]["nested"] == "value2"
|
||||
assert "experimental_feature" in request_body
|
||||
assert request_body["experimental_feature"] is True
|
||||
assert request_body["model"] == "gpt-4o"
|
||||
assert request_body["input"] == "Test input"
|
||||
|
||||
|
||||
def test_responses_extra_body_params_passed_sync(extra_body_mock_response_data):
|
||||
"""Test that extra_body parameters are passed in sync mode."""
|
||||
with patch(
|
||||
"litellm.llms.custom_httpx.http_handler.HTTPHandler.post",
|
||||
return_value=MockResponse(extra_body_mock_response_data, 200),
|
||||
) as mock_post:
|
||||
response = litellm.responses(
|
||||
model="gpt-4o",
|
||||
input="Sync test",
|
||||
max_output_tokens=20,
|
||||
extra_body={
|
||||
"sync_custom_param": "sync_value",
|
||||
"another_param": 42,
|
||||
},
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
assert response.id is not None
|
||||
|
||||
request_body = mock_post.call_args.kwargs["json"]
|
||||
|
||||
assert "sync_custom_param" in request_body
|
||||
assert request_body["sync_custom_param"] == "sync_value"
|
||||
assert "another_param" in request_body
|
||||
assert request_body["another_param"] == 42
|
||||
assert request_body["model"] == "gpt-4o"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_extra_body_merges_with_request_data(extra_body_mock_response_data):
|
||||
"""Test that extra_body is merged into the request data."""
|
||||
with patch(
|
||||
"litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post",
|
||||
new_callable=AsyncMock,
|
||||
) as mock_post:
|
||||
mock_post.return_value = MockResponse(extra_body_mock_response_data, 200)
|
||||
|
||||
await litellm.aresponses(
|
||||
model="gpt-4o",
|
||||
input="Test",
|
||||
temperature=0.7,
|
||||
max_output_tokens=20,
|
||||
extra_body={
|
||||
"custom_field": "custom_value",
|
||||
},
|
||||
)
|
||||
|
||||
request_body = mock_post.call_args.kwargs["json"]
|
||||
|
||||
assert "temperature" in request_body
|
||||
assert "custom_field" in request_body
|
||||
assert request_body["custom_field"] == "custom_value"
|
||||
|
||||
Reference in New Issue
Block a user