diff --git a/homeassistant/components/google_generative_ai_conversation/entity.py b/homeassistant/components/google_generative_ai_conversation/entity.py index 08f60d0cb8fd..547b3d41ab3e 100644 --- a/homeassistant/components/google_generative_ai_conversation/entity.py +++ b/homeassistant/components/google_generative_ai_conversation/entity.py @@ -110,6 +110,12 @@ def _is_gemini_3_model(model: str) -> bool: return name.startswith("gemini-3") +def _is_gemma_4_model(model: str) -> bool: + """Check if the model is a Gemma 4 series model.""" + name = model.removeprefix("models/") + return name.startswith("gemma-4") + + def _create_thinking_config( model: str, thinking_budget: int, @@ -123,10 +129,21 @@ def _create_thinking_config( -1 = automatic (default behavior), 0 = disable thinking, >0 = custom token budget (Gemini 2.5 only). - thinking_level: The user-configured thinking level for Gemini 3 models: - "auto" = automatic (default), "minimal", "low", "medium", "high". + thinking_level: The user-configured thinking level for Gemini 3 and + Gemma 4 models: "auto" = automatic (default), "minimal", "low", + "medium", "high". """ + if _is_gemma_4_model(model): + # Gemma 4 only supports the minimal and high thinking levels + gemma_level_map: dict[str, ThinkingLevel] = { + "minimal": ThinkingLevel.MINIMAL, + "high": ThinkingLevel.HIGH, + } + if thinking_level and thinking_level in gemma_level_map: + return ThinkingConfig(thinking_level=gemma_level_map[thinking_level]) + return None + if not _is_thinking_model(model): return None diff --git a/tests/components/google_generative_ai_conversation/test_conversation.py b/tests/components/google_generative_ai_conversation/test_conversation.py index 046387d3772b..e25f1a68fbe2 100644 --- a/tests/components/google_generative_ai_conversation/test_conversation.py +++ b/tests/components/google_generative_ai_conversation/test_conversation.py @@ -4,7 +4,7 @@ import datetime from unittest.mock import AsyncMock, patch from freezegun import freeze_time -from google.genai.types import GenerateContentResponse, ThinkingLevel +from google.genai.types import GenerateContentResponse, ThinkingConfig, ThinkingLevel import pytest from syrupy.assertion import SnapshotAssertion @@ -881,6 +881,37 @@ def test_create_thinking_config_gemini3_auto( assert result.thinking_level is None +@pytest.mark.parametrize( + ("model", "thinking_level", "expected"), + [ + pytest.param( + "models/gemma-4-26b-a4b-it", + "minimal", + ThinkingConfig(thinking_level=ThinkingLevel.MINIMAL), + id="minimal", + ), + pytest.param( + "gemma-4-31b-it", + "high", + ThinkingConfig(thinking_level=ThinkingLevel.HIGH), + id="high", + ), + # The API rejects any other thinking level for Gemma 4 + pytest.param("models/gemma-4-31b-it", "low", None, id="low"), + pytest.param("models/gemma-4-31b-it", "medium", None, id="medium"), + pytest.param("models/gemma-4-31b-it", "auto", None, id="auto"), + pytest.param("models/gemma-4-31b-it", None, None, id="unset"), + ], +) +def test_create_thinking_config_gemma4( + model: str, + thinking_level: str | None, + expected: ThinkingConfig | None, +) -> None: + """Test Gemma 4 models only send the supported thinking levels.""" + assert _create_thinking_config(model, 0, thinking_level) == expected + + @pytest.mark.parametrize( ("model", "thinking_budget", "expected_budget"), [