mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-21 00:21:49 +00:00
Add reasoning effort mapping
This commit is contained in:
parent
4d821c9011
commit
b4a41b6777
6 changed files with 118 additions and 28 deletions
|
|
@ -229,15 +229,15 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
|
|||
Gemini 3 models include:
|
||||
- gemini-3-pro-preview
|
||||
- gemini-3-flash
|
||||
- skyhawk (Gemini 3 Flash checkpoint)
|
||||
- fiercefalcon (Gemini 3 Flash checkpoint)
|
||||
- Any future Gemini 3.x models
|
||||
"""
|
||||
# Check for Gemini 3 models
|
||||
if "gemini-3" in model:
|
||||
return True
|
||||
|
||||
# Check for skyhawk (Gemini 3 Flash checkpoint)
|
||||
if "skyhawk" in model.lower():
|
||||
# Check for fiercefalcon (Gemini 3 Flash checkpoint) # TODO: remove
|
||||
if "fiercefalcon" in model.lower(): # TODO : Remove this once we have the official name of the model
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
@ -632,20 +632,20 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
|
|||
Returns:
|
||||
GeminiThinkingConfig with thinkingLevel and includeThoughts
|
||||
"""
|
||||
# Check if this is skyhawk/gemini-3-flash which supports MINIMAL thinking level
|
||||
is_skyhawk = model and (
|
||||
"skyhawk" in model.lower() or "gemini-3-flash" in model.lower()
|
||||
# Check if this is gemini-3-flash which supports MINIMAL thinking level
|
||||
is_fiercefalcon= model and (
|
||||
"fiercefalcon" in model.lower() or "gemini-3-flash" in model.lower()
|
||||
)
|
||||
if reasoning_effort == "minimal":
|
||||
if is_skyhawk:
|
||||
if is_fiercefalcon:
|
||||
return {"thinkingLevel": "minimal", "includeThoughts": True}
|
||||
else:
|
||||
return {"thinkingLevel": "low", "includeThoughts": True}
|
||||
elif reasoning_effort == "low":
|
||||
return {"thinkingLevel": "low", "includeThoughts": True}
|
||||
elif reasoning_effort == "medium":
|
||||
# For skyhawk, medium maps to "medium", otherwise "high"
|
||||
if is_skyhawk:
|
||||
# For fiercefalcon, medium maps to "medium", otherwise "high"
|
||||
if is_fiercefalcon:
|
||||
return {"thinkingLevel": "medium", "includeThoughts": True}
|
||||
else:
|
||||
return {
|
||||
|
|
@ -655,14 +655,14 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
|
|||
elif reasoning_effort == "high":
|
||||
return {"thinkingLevel": "high", "includeThoughts": True}
|
||||
elif reasoning_effort == "disable":
|
||||
# Gemini 3 cannot fully disable thinking, so we use "minimal" for skyhawk, "low" for others
|
||||
if is_skyhawk:
|
||||
# Gemini 3 cannot fully disable thinking, so we use "minimal" for fiercefalcon, "low" for others
|
||||
if is_fiercefalcon:
|
||||
return {"thinkingLevel": "minimal", "includeThoughts": False}
|
||||
else:
|
||||
return {"thinkingLevel": "low", "includeThoughts": False}
|
||||
elif reasoning_effort == "none":
|
||||
# For skyhawk, use "minimal" instead of "low"
|
||||
if is_skyhawk:
|
||||
# For fiercefalcon, use "minimal" instead of "low"
|
||||
if is_fiercefalcon:
|
||||
return {"thinkingLevel": "minimal", "includeThoughts": False}
|
||||
else:
|
||||
return {"thinkingLevel": "low", "includeThoughts": False}
|
||||
|
|
@ -935,10 +935,10 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
|
|||
"thinkingLevel" not in thinking_config
|
||||
and "thinkingBudget" not in thinking_config
|
||||
):
|
||||
# For skyhawk, default to "minimal" to match Gemini 2.5 Flash behavior
|
||||
# For fiercefalcon, default to "minimal" to match Gemini 2.5 Flash behavior
|
||||
# For other Gemini 3 models, default to "low"
|
||||
is_skyhawk = "skyhawk" in model.lower() or "gemini-3-flash" in model.lower()
|
||||
thinking_config["thinkingLevel"] = "minimal" if is_skyhawk else "low"
|
||||
is_fiercefalcon = "fiercefalcon" in model.lower() or "gemini-3-flash" in model.lower()
|
||||
thinking_config["thinkingLevel"] = "minimal" if is_fiercefalcon else "low"
|
||||
optional_params["thinkingConfig"] = thinking_config
|
||||
|
||||
return optional_params
|
||||
|
|
|
|||
|
|
@ -14416,7 +14416,7 @@
|
|||
"supports_web_search": true,
|
||||
"tpm": 800000
|
||||
},
|
||||
"gemini/skyhawk": {
|
||||
"gemini/fiercefalcon": {
|
||||
"cache_read_input_token_cost": 3e-08,
|
||||
"input_cost_per_audio_token": 1e-06,
|
||||
"input_cost_per_token": 3e-07,
|
||||
|
|
@ -14463,7 +14463,7 @@
|
|||
"supports_web_search": true,
|
||||
"tpm": 800000
|
||||
},
|
||||
"skyhawk": {
|
||||
"fiercefalcon": {
|
||||
"cache_read_input_token_cost": 3e-08,
|
||||
"input_cost_per_audio_token": 1e-06,
|
||||
"input_cost_per_token": 3e-07,
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ class LiteLLMCompletionResponsesConfig:
|
|||
"metadata",
|
||||
"parallel_tool_calls",
|
||||
"previous_response_id",
|
||||
"reasoning",
|
||||
"stream",
|
||||
"temperature",
|
||||
"text",
|
||||
|
|
@ -122,6 +123,17 @@ class LiteLLMCompletionResponsesConfig:
|
|||
text_param
|
||||
)
|
||||
|
||||
# Extract reasoning_effort from reasoning parameter
|
||||
reasoning_effort = None
|
||||
reasoning_param = responses_api_request.get("reasoning")
|
||||
if reasoning_param:
|
||||
if isinstance(reasoning_param, dict):
|
||||
# reasoning can be {"effort": "low|medium|high"}
|
||||
reasoning_effort = reasoning_param.get("effort")
|
||||
elif isinstance(reasoning_param, str):
|
||||
# reasoning could be a string directly
|
||||
reasoning_effort = reasoning_param
|
||||
|
||||
litellm_completion_request: dict = {
|
||||
"messages": LiteLLMCompletionResponsesConfig.transform_responses_api_input_to_messages(
|
||||
input=input,
|
||||
|
|
@ -140,6 +152,7 @@ class LiteLLMCompletionResponsesConfig:
|
|||
"service_tier": kwargs.get("service_tier"),
|
||||
"web_search_options": web_search_options,
|
||||
"response_format": response_format,
|
||||
"reasoning_effort": reasoning_effort,
|
||||
# litellm specific params
|
||||
"custom_llm_provider": custom_llm_provider,
|
||||
"extra_headers": extra_headers,
|
||||
|
|
@ -161,7 +174,6 @@ class LiteLLMCompletionResponsesConfig:
|
|||
litellm_completion_request = {
|
||||
k: v for k, v in litellm_completion_request.items() if v is not None
|
||||
}
|
||||
|
||||
return litellm_completion_request
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -14416,7 +14416,7 @@
|
|||
"supports_web_search": true,
|
||||
"tpm": 800000
|
||||
},
|
||||
"gemini/skyhawk": {
|
||||
"gemini/fiercefalcon": {
|
||||
"cache_read_input_token_cost": 3e-08,
|
||||
"input_cost_per_audio_token": 1e-06,
|
||||
"input_cost_per_token": 3e-07,
|
||||
|
|
@ -14463,7 +14463,7 @@
|
|||
"supports_web_search": true,
|
||||
"tpm": 800000
|
||||
},
|
||||
"skyhawk": {
|
||||
"fiercefalcon": {
|
||||
"cache_read_input_token_cost": 3e-08,
|
||||
"input_cost_per_audio_token": 1e-06,
|
||||
"input_cost_per_token": 3e-07,
|
||||
|
|
|
|||
|
|
@ -227,4 +227,4 @@ general_settings:
|
|||
# settings for using redis caching
|
||||
# REDIS_HOST: redis-16337.c322.us-east-1-2.ec2.cloud.redislabs.com
|
||||
# REDIS_PORT: "16337"
|
||||
# REDIS_PASSWORD:
|
||||
# REDIS_PASSWORD:
|
||||
|
|
@ -12,6 +12,9 @@ sys.path.insert(
|
|||
import pytest
|
||||
|
||||
from litellm.llms.gemini.google_genai.transformation import GoogleGenAIConfig
|
||||
from litellm.responses.litellm_completion_transformation.transformation import (
|
||||
LiteLLMCompletionResponsesConfig,
|
||||
)
|
||||
|
||||
|
||||
def test_map_generate_content_optional_params_response_json_schema_camelcase():
|
||||
|
|
@ -30,7 +33,7 @@ def test_map_generate_content_optional_params_response_json_schema_camelcase():
|
|||
|
||||
result = config.map_generate_content_optional_params(
|
||||
generate_content_config_dict=generate_content_config_dict,
|
||||
model="gemini/skyhawk"
|
||||
model="gemini/fiercefalcon"
|
||||
)
|
||||
|
||||
# responseJsonSchema should be in the result (camelCase format for Google GenAI API)
|
||||
|
|
@ -56,7 +59,7 @@ def test_map_generate_content_optional_params_response_schema_snakecase():
|
|||
|
||||
result = config.map_generate_content_optional_params(
|
||||
generate_content_config_dict=generate_content_config_dict,
|
||||
model="gemini/skyhawk"
|
||||
model="gemini/fiercefalcon"
|
||||
)
|
||||
|
||||
# response_schema should be converted to responseJsonSchema (camelCase)
|
||||
|
|
@ -79,7 +82,7 @@ def test_map_generate_content_optional_params_thinking_config_camelcase():
|
|||
|
||||
result = config.map_generate_content_optional_params(
|
||||
generate_content_config_dict=generate_content_config_dict,
|
||||
model="gemini/skyhawk"
|
||||
model="gemini/fiercefalcon"
|
||||
)
|
||||
|
||||
# thinkingConfig should be in the result (camelCase format for Google GenAI API)
|
||||
|
|
@ -103,7 +106,7 @@ def test_map_generate_content_optional_params_thinking_config_snakecase():
|
|||
|
||||
result = config.map_generate_content_optional_params(
|
||||
generate_content_config_dict=generate_content_config_dict,
|
||||
model="gemini/skyhawk"
|
||||
model="gemini/fiercefalcon"
|
||||
)
|
||||
|
||||
# thinking_config should be converted to thinkingConfig (camelCase)
|
||||
|
|
@ -135,7 +138,7 @@ def test_map_generate_content_optional_params_mixed_formats():
|
|||
|
||||
result = config.map_generate_content_optional_params(
|
||||
generate_content_config_dict=generate_content_config_dict,
|
||||
model="gemini/skyhawk"
|
||||
model="gemini/fiercefalcon"
|
||||
)
|
||||
|
||||
# All parameters should be converted to camelCase
|
||||
|
|
@ -162,10 +165,85 @@ def test_map_generate_content_optional_params_response_mime_type():
|
|||
|
||||
result = config.map_generate_content_optional_params(
|
||||
generate_content_config_dict=generate_content_config_dict,
|
||||
model="gemini/skyhawk"
|
||||
model="gemini/fiercefalcon"
|
||||
)
|
||||
|
||||
# responseMimeType should be passed through (it's already camelCase)
|
||||
assert "responseMimeType" in result or "response_mime_type" in result
|
||||
assert "responseJsonSchema" in result
|
||||
|
||||
|
||||
def test_responses_api_reasoning_dict_format():
|
||||
"""Test that reasoning parameter with dict format is mapped to reasoning_effort"""
|
||||
from litellm.types.llms.openai import ResponsesAPIOptionalRequestParams
|
||||
|
||||
responses_api_request: ResponsesAPIOptionalRequestParams = {
|
||||
"reasoning": {"effort": "high"},
|
||||
"temperature": 1.0,
|
||||
}
|
||||
|
||||
result = LiteLLMCompletionResponsesConfig.transform_responses_api_request_to_chat_completion_request(
|
||||
model="gemini/2.5-pro",
|
||||
input="Hello, what is the capital of France?",
|
||||
responses_api_request=responses_api_request,
|
||||
)
|
||||
|
||||
# reasoning_effort should be extracted from reasoning dict
|
||||
assert "reasoning_effort" in result
|
||||
assert result["reasoning_effort"] == "high"
|
||||
|
||||
|
||||
def test_responses_api_reasoning_string_format():
|
||||
"""Test that reasoning parameter with string format is mapped to reasoning_effort"""
|
||||
from litellm.types.llms.openai import ResponsesAPIOptionalRequestParams
|
||||
|
||||
responses_api_request: ResponsesAPIOptionalRequestParams = {
|
||||
"reasoning": "medium", # Could be a string directly
|
||||
"temperature": 1.0,
|
||||
}
|
||||
|
||||
result = LiteLLMCompletionResponsesConfig.transform_responses_api_request_to_chat_completion_request(
|
||||
model="gemini/2.5-pro",
|
||||
input="Hello, what is the capital of France?",
|
||||
responses_api_request=responses_api_request,
|
||||
)
|
||||
|
||||
# reasoning_effort should be extracted from reasoning string
|
||||
assert "reasoning_effort" in result
|
||||
assert result["reasoning_effort"] == "medium"
|
||||
|
||||
|
||||
def test_responses_api_reasoning_low_effort():
|
||||
"""Test that low reasoning effort is correctly mapped"""
|
||||
from litellm.types.llms.openai import ResponsesAPIOptionalRequestParams
|
||||
|
||||
responses_api_request: ResponsesAPIOptionalRequestParams = {
|
||||
"reasoning": {"effort": "low"},
|
||||
}
|
||||
|
||||
result = LiteLLMCompletionResponsesConfig.transform_responses_api_request_to_chat_completion_request(
|
||||
model="gemini/2.5-pro",
|
||||
input="Test",
|
||||
responses_api_request=responses_api_request,
|
||||
)
|
||||
|
||||
assert "reasoning_effort" in result
|
||||
assert result["reasoning_effort"] == "low"
|
||||
|
||||
|
||||
def test_responses_api_no_reasoning():
|
||||
"""Test that no reasoning_effort is included when reasoning is not provided"""
|
||||
from litellm.types.llms.openai import ResponsesAPIOptionalRequestParams
|
||||
|
||||
responses_api_request: ResponsesAPIOptionalRequestParams = {
|
||||
"temperature": 1.0,
|
||||
}
|
||||
|
||||
result = LiteLLMCompletionResponsesConfig.transform_responses_api_request_to_chat_completion_request(
|
||||
model="gemini/2.5-pro",
|
||||
input="Test",
|
||||
responses_api_request=responses_api_request,
|
||||
)
|
||||
|
||||
# reasoning_effort should not be in result if not provided (filtered out as None)
|
||||
assert "reasoning_effort" not in result or result.get("reasoning_effort") is None
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue