From 00516752dbf841732dee2292231899716b094918 Mon Sep 17 00:00:00 2001 From: hklee Date: Sun, 29 Mar 2026 22:57:55 +0900 Subject: [PATCH] fix: add image_config support in map_openai_params for Gemini image models --- .../vertex_and_google_ai_studio_gemini.py | 11 ++++++ .../vertex_ai/gemini/test_transformation.py | 34 ++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py index 36f51c5b2f5..d355875dca8 100644 --- a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py +++ b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py @@ -318,6 +318,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): "parallel_tool_calls", "web_search_options", "include_server_side_tool_invocations", + "image_config", ] # Add penalty parameters only for non-preview models @@ -1022,6 +1023,16 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): optional_params["candidate_count"] = value elif param == "audio" and isinstance(value, dict): optional_params["speechConfig"] = self._map_audio_params(value) + elif param == "image_config" and isinstance(value, dict): + image_config = {} + aspect_ratio = value.get("aspect_ratio") or value.get("aspectRatio") + image_size = value.get("image_size") or value.get("imageSize") + if aspect_ratio is not None: + image_config["aspectRatio"] = aspect_ratio + if image_size is not None: + image_config["imageSize"] = image_size + if image_config: + optional_params["imageConfig"] = image_config elif param == "stop": if isinstance(value, str): optional_params["stop_sequences"] = [value] diff --git a/tests/litellm/llms/vertex_ai/gemini/test_transformation.py b/tests/litellm/llms/vertex_ai/gemini/test_transformation.py index 20f48b6f393..b82c2f95247 100644 --- a/tests/litellm/llms/vertex_ai/gemini/test_transformation.py +++ b/tests/litellm/llms/vertex_ai/gemini/test_transformation.py @@ -287,4 +287,36 @@ def test_map_function_enterprise_web_search_snake_case(): result = config._map_function(tools, optional_params) assert len(result) == 1 - assert "enterpriseWebSearch" in result[0] \ No newline at end of file + assert "enterpriseWebSearch" in result[0] + + +def test_map_openai_params_image_config_snake_case(): + """Test that image_config with snake_case keys is mapped to camelCase imageConfig.""" + config = VertexGeminiConfig() + result = config.map_openai_params( + non_default_params={ + "image_config": {"image_size": "2K", "aspect_ratio": "16:9"} + }, + optional_params={}, + model="gemini-3.1-flash-image-preview", + drop_params=False, + ) + assert "imageConfig" in result + assert result["imageConfig"]["imageSize"] == "2K" + assert result["imageConfig"]["aspectRatio"] == "16:9" + + +def test_map_openai_params_image_config_camel_case(): + """Test that image_config with camelCase keys is passed through to imageConfig.""" + config = VertexGeminiConfig() + result = config.map_openai_params( + non_default_params={ + "image_config": {"imageSize": "4K", "aspectRatio": "9:16"} + }, + optional_params={}, + model="gemini-3.1-flash-image-preview", + drop_params=False, + ) + assert "imageConfig" in result + assert result["imageConfig"]["imageSize"] == "4K" + assert result["imageConfig"]["aspectRatio"] == "9:16" \ No newline at end of file