mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
feat(vercel_ai_gateway): convert reasoning_effort to Vercel AI Gateway extra_body format
Automatically maps reasoning_effort to extra_body.reasoning = {effort, enabled: true}
as required by Vercel AI Gateway. Adds reasoning_effort to supported params, 2 unit tests,
and provider documentation.
Fixes #25510
Signed-off-by: devarakondasrikanth <devarakondasrikanth@ymail.com>
This commit is contained in:
parent
5544803b35
commit
1e64174fce
3 changed files with 114 additions and 31 deletions
|
|
@ -246,6 +246,36 @@ curl http://localhost:4000/v1/chat/completions \
|
|||
|
||||
For more detailed information on using the LiteLLM Proxy, see the [LiteLLM Proxy documentation](../providers/litellm_proxy).
|
||||
|
||||
## Reasoning Effort
|
||||
|
||||
LiteLLM automatically converts the standard `reasoning_effort` parameter into the format required by Vercel AI Gateway.
|
||||
|
||||
**Input (standard LiteLLM format):**
|
||||
```python showLineNumbers title="reasoning_effort with Vercel AI Gateway"
|
||||
import litellm
|
||||
|
||||
response = litellm.completion(
|
||||
model="vercel_ai_gateway/anthropic/claude-sonnet-4-5",
|
||||
messages=[{"role": "user", "content": "Explain quantum entanglement"}],
|
||||
reasoning_effort="high"
|
||||
)
|
||||
```
|
||||
|
||||
LiteLLM converts this to the Vercel AI Gateway `extra_body` format automatically:
|
||||
|
||||
```json
|
||||
{
|
||||
"extra_body": {
|
||||
"reasoning": {
|
||||
"effort": "high",
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Supported values for `reasoning_effort`: `"low"`, `"medium"`, `"high"`.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [Vercel AI Gateway Documentation](https://vercel.com/docs/ai-gateway)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ class VercelAIGatewayConfig(OpenAIGPTConfig):
|
|||
base_params = super().get_supported_openai_params(model)
|
||||
if "extra_body" not in base_params:
|
||||
base_params.append("extra_body")
|
||||
if "reasoning_effort" not in base_params:
|
||||
base_params.append("reasoning_effort")
|
||||
return base_params
|
||||
|
||||
def _get_openai_compatible_provider_info(
|
||||
|
|
@ -56,16 +58,20 @@ class VercelAIGatewayConfig(OpenAIGPTConfig):
|
|||
non_default_params, optional_params, model, drop_params
|
||||
)
|
||||
|
||||
# Vercel AI Gateway-only parameters
|
||||
extra_body = {}
|
||||
# Vercel AI Gateway-only parameters — seed from any extra_body the super call already set
|
||||
extra_body = mapped_openai_params.pop("extra_body", {})
|
||||
provider_options = non_default_params.pop("providerOptions", None)
|
||||
|
||||
if provider_options is not None:
|
||||
extra_body["providerOptions"] = provider_options
|
||||
|
||||
mapped_openai_params[
|
||||
"extra_body"
|
||||
] = extra_body # openai client supports `extra_body` param
|
||||
reasoning_effort = mapped_openai_params.pop("reasoning_effort", None)
|
||||
if reasoning_effort is not None:
|
||||
extra_body["reasoning"] = {"effort": reasoning_effort, "enabled": True}
|
||||
|
||||
mapped_openai_params["extra_body"] = (
|
||||
extra_body # openai client supports `extra_body` param
|
||||
)
|
||||
return mapped_openai_params
|
||||
|
||||
def transform_request(
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ import os
|
|||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath("../../../../..")
|
||||
) # Adds the parent directory to the system path
|
||||
|
|
@ -21,16 +19,17 @@ def test_vercel_ai_gateway_extra_body_transformation():
|
|||
messages=[{"role": "user", "content": "Hello, world!"}],
|
||||
optional_params={
|
||||
"extra_body": {
|
||||
"providerOptions": {
|
||||
"gateway": {"order": ["azure", "openai"]}
|
||||
}
|
||||
"providerOptions": {"gateway": {"order": ["azure", "openai"]}}
|
||||
}
|
||||
},
|
||||
litellm_params={},
|
||||
headers={},
|
||||
)
|
||||
|
||||
assert transformed_request["extra_body"]["providerOptions"]["gateway"]["order"] == ["azure", "openai"]
|
||||
assert transformed_request["extra_body"]["providerOptions"]["gateway"]["order"] == [
|
||||
"azure",
|
||||
"openai",
|
||||
]
|
||||
assert transformed_request["messages"] == [
|
||||
{"role": "user", "content": "Hello, world!"}
|
||||
]
|
||||
|
|
@ -39,28 +38,31 @@ def test_vercel_ai_gateway_extra_body_transformation():
|
|||
def test_vercel_ai_gateway_provider_options_mapping():
|
||||
"""Test that providerOptions from non_default_params is moved to extra_body"""
|
||||
config = VercelAIGatewayConfig()
|
||||
|
||||
|
||||
non_default_params = {
|
||||
"providerOptions": {
|
||||
"gateway": {"order": ["azure", "openai"]}
|
||||
}
|
||||
"providerOptions": {"gateway": {"order": ["azure", "openai"]}}
|
||||
}
|
||||
optional_params = {}
|
||||
model = "vercel_ai_gateway/openai/gpt-4o"
|
||||
|
||||
|
||||
result = config.map_openai_params(
|
||||
non_default_params, optional_params, model, drop_params=False
|
||||
)
|
||||
|
||||
assert result["extra_body"]["providerOptions"]["gateway"]["order"] == ["azure", "openai"]
|
||||
|
||||
assert result["extra_body"]["providerOptions"]["gateway"]["order"] == [
|
||||
"azure",
|
||||
"openai",
|
||||
]
|
||||
assert "providerOptions" not in result
|
||||
|
||||
|
||||
def test_vercel_ai_gateway_get_supported_openai_params():
|
||||
"""Test that extra_body is included in supported params"""
|
||||
config = VercelAIGatewayConfig()
|
||||
supported_params = config.get_supported_openai_params("vercel_ai_gateway/openai/gpt-4o")
|
||||
|
||||
supported_params = config.get_supported_openai_params(
|
||||
"vercel_ai_gateway/openai/gpt-4o"
|
||||
)
|
||||
|
||||
assert "extra_body" in supported_params
|
||||
assert "temperature" in supported_params
|
||||
assert "max_tokens" in supported_params
|
||||
|
|
@ -70,7 +72,7 @@ def test_vercel_ai_gateway_get_supported_openai_params():
|
|||
def test_vercel_ai_gateway_get_openai_compatible_provider_info():
|
||||
"""Test provider info retrieval with environment variables"""
|
||||
config = VercelAIGatewayConfig()
|
||||
|
||||
|
||||
with patch.dict(
|
||||
"os.environ",
|
||||
{
|
||||
|
|
@ -86,27 +88,72 @@ def test_vercel_ai_gateway_get_openai_compatible_provider_info():
|
|||
def test_vercel_ai_gateway_error_class():
|
||||
"""Test error class creation"""
|
||||
config = VercelAIGatewayConfig()
|
||||
|
||||
|
||||
error_message = "Test error"
|
||||
status_code = 400
|
||||
headers = {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
error_class = config.get_error_class(error_message, status_code, headers)
|
||||
|
||||
|
||||
assert isinstance(error_class, VercelAIGatewayException)
|
||||
assert error_class.message == error_message
|
||||
assert error_class.status_code == status_code
|
||||
assert error_class.headers == headers
|
||||
|
||||
|
||||
def test_vercel_ai_gateway_reasoning_effort_mapping():
|
||||
"""Test that reasoning_effort is converted to extra_body.reasoning format"""
|
||||
config = VercelAIGatewayConfig()
|
||||
|
||||
non_default_params = {"reasoning_effort": "high"}
|
||||
optional_params = {}
|
||||
model = "vercel_ai_gateway/openai/gpt-4o"
|
||||
|
||||
result = config.map_openai_params(
|
||||
non_default_params, optional_params, model, drop_params=False
|
||||
)
|
||||
|
||||
assert result["extra_body"]["reasoning"] == {"effort": "high", "enabled": True}
|
||||
assert "reasoning_effort" not in result
|
||||
|
||||
|
||||
def test_vercel_ai_gateway_reasoning_effort_with_existing_extra_body():
|
||||
"""Test that reasoning_effort merges with existing extra_body instead of overwriting it"""
|
||||
config = VercelAIGatewayConfig()
|
||||
|
||||
non_default_params = {
|
||||
"reasoning_effort": "high",
|
||||
"providerOptions": {"gateway": {"order": ["azure", "openai"]}},
|
||||
}
|
||||
optional_params = {}
|
||||
model = "vercel_ai_gateway/openai/gpt-4o"
|
||||
|
||||
result = config.map_openai_params(
|
||||
non_default_params, optional_params, model, drop_params=False
|
||||
)
|
||||
|
||||
# Both keys must coexist in extra_body — neither should be silently dropped
|
||||
assert result["extra_body"]["reasoning"] == {"effort": "high", "enabled": True}
|
||||
assert result["extra_body"]["providerOptions"] == {
|
||||
"gateway": {"order": ["azure", "openai"]}
|
||||
}
|
||||
assert "reasoning_effort" not in result
|
||||
|
||||
|
||||
def test_vercel_ai_gateway_reasoning_effort_in_supported_params():
|
||||
"""Test that reasoning_effort is included in supported params"""
|
||||
config = VercelAIGatewayConfig()
|
||||
supported_params = config.get_supported_openai_params(
|
||||
"vercel_ai_gateway/openai/gpt-4o"
|
||||
)
|
||||
|
||||
assert "reasoning_effort" in supported_params
|
||||
|
||||
|
||||
def test_vercel_ai_gateway_exception_inheritance():
|
||||
"""Test that VercelAIGatewayException inherits from BaseLLMException"""
|
||||
from litellm.llms.base_llm.chat.transformation import BaseLLMException
|
||||
|
||||
exception = VercelAIGatewayException(
|
||||
message="test",
|
||||
status_code=500,
|
||||
headers={}
|
||||
)
|
||||
|
||||
|
||||
exception = VercelAIGatewayException(message="test", status_code=500, headers={})
|
||||
|
||||
assert isinstance(exception, BaseLLMException)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue