Add support for structured output thinkingConfig param

This commit is contained in:
Sameer Kankute
2025-12-17 18:02:15 +05:30
parent 9e3f946a2e
commit ed6c66c20c
2 changed files with 195 additions and 2 deletions
@@ -75,6 +75,7 @@ class GoogleGenAIConfig(BaseGoogleGenAIGenerateContentConfig, VertexLLM):
"seed",
"response_mime_type",
"response_schema",
"response_json_schema",
"routing_config",
"model_selection_config",
"safety_settings",
@@ -105,13 +106,34 @@ class GoogleGenAIConfig(BaseGoogleGenAIGenerateContentConfig, VertexLLM):
Returns:
Mapped parameters for the provider
"""
from litellm.llms.vertex_ai.gemini.transformation import _snake_to_camel, _camel_to_snake
_generate_content_config_dict: Dict[str, Any] = {}
supported_google_genai_params = (
self.get_supported_generate_content_optional_params(model)
)
# Create a set with both camelCase and snake_case versions for faster lookup
supported_params_set = set(supported_google_genai_params)
supported_params_set.update(_snake_to_camel(p) for p in supported_google_genai_params)
supported_params_set.update(_camel_to_snake(p) for p in supported_google_genai_params if "_" not in p)
for param, value in generate_content_config_dict.items():
if param in supported_google_genai_params:
_generate_content_config_dict[param] = value
# Google GenAI API expects camelCase, so we'll always output in camelCase
# Check if param (or its variants) is supported
param_snake = _camel_to_snake(param)
param_camel = _snake_to_camel(param)
# Check if param is supported in any format
is_supported = (
param in supported_google_genai_params or
param_snake in supported_google_genai_params or
param_camel in supported_google_genai_params
)
if is_supported:
# Always output in camelCase for Google GenAI API
output_key = param_camel if param != param_camel else param
_generate_content_config_dict[output_key] = value
return _generate_content_config_dict
def validate_environment(
@@ -0,0 +1,171 @@
#!/usr/bin/env python3
"""
Test to verify the Google GenAI transformation logic for generateContent parameters
"""
import os
import sys
sys.path.insert(
0, os.path.abspath("../../..")
) # Adds the parent directory to the system path
import pytest
from litellm.llms.gemini.google_genai.transformation import GoogleGenAIConfig
def test_map_generate_content_optional_params_response_json_schema_camelcase():
"""Test that responseJsonSchema (camelCase) is passed through correctly"""
config = GoogleGenAIConfig()
generate_content_config_dict = {
"responseJsonSchema": {
"type": "object",
"properties": {
"recipe_name": {"type": "string"}
}
},
"temperature": 1.0
}
result = config.map_generate_content_optional_params(
generate_content_config_dict=generate_content_config_dict,
model="gemini/skyhawk"
)
# responseJsonSchema should be in the result (camelCase format for Google GenAI API)
assert "responseJsonSchema" in result
assert result["responseJsonSchema"] == generate_content_config_dict["responseJsonSchema"]
assert "temperature" in result
assert result["temperature"] == 1.0
def test_map_generate_content_optional_params_response_schema_snakecase():
"""Test that response_schema (snake_case) is converted to responseJsonSchema (camelCase)"""
config = GoogleGenAIConfig()
generate_content_config_dict = {
"response_json_schema": {
"type": "object",
"properties": {
"recipe_name": {"type": "string"}
}
},
"temperature": 1.0
}
result = config.map_generate_content_optional_params(
generate_content_config_dict=generate_content_config_dict,
model="gemini/skyhawk"
)
# response_schema should be converted to responseJsonSchema (camelCase)
assert "responseJsonSchema" in result
assert result["responseJsonSchema"] == generate_content_config_dict["response_json_schema"]
assert "temperature" in result
def test_map_generate_content_optional_params_thinking_config_camelcase():
"""Test that thinkingConfig (camelCase) is passed through correctly"""
config = GoogleGenAIConfig()
generate_content_config_dict = {
"thinkingConfig": {
"thinkingLevel": "minimal",
"includeThoughts": True
},
"temperature": 1.0
}
result = config.map_generate_content_optional_params(
generate_content_config_dict=generate_content_config_dict,
model="gemini/skyhawk"
)
# thinkingConfig should be in the result (camelCase format for Google GenAI API)
assert "thinkingConfig" in result
assert result["thinkingConfig"]["thinkingLevel"] == "minimal"
assert result["thinkingConfig"]["includeThoughts"] is True
assert "temperature" in result
def test_map_generate_content_optional_params_thinking_config_snakecase():
"""Test that thinking_config (snake_case) is converted to thinkingConfig (camelCase)"""
config = GoogleGenAIConfig()
generate_content_config_dict = {
"thinking_config": {
"thinkingLevel": "medium",
"includeThoughts": True
},
"temperature": 1.0
}
result = config.map_generate_content_optional_params(
generate_content_config_dict=generate_content_config_dict,
model="gemini/skyhawk"
)
# thinking_config should be converted to thinkingConfig (camelCase)
assert "thinkingConfig" in result
assert result["thinkingConfig"]["thinkingLevel"] == "medium"
assert result["thinkingConfig"]["includeThoughts"] is True
assert "thinking_config" not in result # Should not be in snake_case format
assert "temperature" in result
def test_map_generate_content_optional_params_mixed_formats():
"""Test that both camelCase and snake_case parameters work together"""
config = GoogleGenAIConfig()
generate_content_config_dict = {
"responseJsonSchema": {
"type": "object",
"properties": {
"recipe_name": {"type": "string"}
}
},
"thinking_config": {
"thinkingLevel": "low",
"includeThoughts": True
},
"temperature": 1.0,
"max_output_tokens": 100
}
result = config.map_generate_content_optional_params(
generate_content_config_dict=generate_content_config_dict,
model="gemini/skyhawk"
)
# All parameters should be converted to camelCase
assert "responseJsonSchema" in result
assert "thinkingConfig" in result
assert result["thinkingConfig"]["thinkingLevel"] == "low"
assert "temperature" in result
assert "maxOutputTokens" in result # This one stays as-is if it's in supported list
def test_map_generate_content_optional_params_response_mime_type():
"""Test that responseMimeType is handled correctly"""
config = GoogleGenAIConfig()
generate_content_config_dict = {
"responseMimeType": "application/json",
"responseJsonSchema": {
"type": "object",
"properties": {
"recipe_name": {"type": "string"}
}
}
}
result = config.map_generate_content_optional_params(
generate_content_config_dict=generate_content_config_dict,
model="gemini/skyhawk"
)
# responseMimeType should be passed through (it's already camelCase)
assert "responseMimeType" in result or "response_mime_type" in result
assert "responseJsonSchema" in result