diff --git a/astrbot/core/provider/sources/gemini_source.py b/astrbot/core/provider/sources/gemini_source.py index f4cba79805..4cbb40fcec 100644 --- a/astrbot/core/provider/sources/gemini_source.py +++ b/astrbot/core/provider/sources/gemini_source.py @@ -251,17 +251,22 @@ async def _prepare_query_config( ) if thinking_level and isinstance(thinking_level, str): thinking_level = thinking_level.upper() - if thinking_level not in ["MINIMAL", "LOW", "MEDIUM", "HIGH"]: + allowed_levels = {"MINIMAL", "LOW", "MEDIUM", "HIGH"} + fallback_level = "HIGH" + if model_name.startswith("gemini-3.7"): + allowed_levels = {"LOW", "MEDIUM", "HIGH"} + fallback_level = "MEDIUM" + if thinking_level not in allowed_levels: logger.warning( - f"Invalid thinking level: {thinking_level}, using HIGH" + "Invalid thinking level %s for %s, using %s", + thinking_level, + model_name, + fallback_level, ) - thinking_level = "HIGH" - level = types.ThinkingLevel(thinking_level) - thinking_config = types.ThinkingConfig() - if not hasattr(types.ThinkingConfig, "thinking_level"): - setattr(types.ThinkingConfig, "thinking_level", level) - else: - thinking_config.thinking_level = level + thinking_level = fallback_level + thinking_config = types.ThinkingConfig( + thinking_level=types.ThinkingLevel(thinking_level) + ) return types.GenerateContentConfig( system_instruction=system_instruction, diff --git a/tests/test_gemini_source.py b/tests/test_gemini_source.py index 4c33de6bca..b5464e8047 100644 --- a/tests/test_gemini_source.py +++ b/tests/test_gemini_source.py @@ -10,6 +10,45 @@ from astrbot.core.provider.sources.gemini_source import ProviderGoogleGenAI +@pytest.mark.asyncio +async def test_gemini_thinking_level_is_serialized_on_every_request(): + model = "gemini-3.7-flash" + provider = ProviderGoogleGenAI.__new__(ProviderGoogleGenAI) + provider.provider_config = {"gm_thinking_config": {"level": "HIGH"}} + provider.provider_settings = {} + provider.model_name = model + provider.safety_settings = [] + + first_config = await provider._prepare_query_config({"model": model}) + second_config = await provider._prepare_query_config({"model": model}) + + assert first_config.thinking_config is not None + assert second_config.thinking_config is not None + assert first_config.thinking_config.model_dump(exclude_none=True) == { + "thinking_level": types.ThinkingLevel.HIGH, + } + assert second_config.thinking_config.model_dump(exclude_none=True) == { + "thinking_level": types.ThinkingLevel.HIGH, + } + + +@pytest.mark.asyncio +async def test_gemini_37_minimal_thinking_level_falls_back_to_medium(): + model = "gemini-3.7-flash" + provider = ProviderGoogleGenAI.__new__(ProviderGoogleGenAI) + provider.provider_config = {"gm_thinking_config": {"level": "MINIMAL"}} + provider.provider_settings = {} + provider.model_name = model + provider.safety_settings = [] + + config = await provider._prepare_query_config({"model": model}) + + assert config.thinking_config is not None + assert config.thinking_config.model_dump(exclude_none=True) == { + "thinking_level": types.ThinkingLevel.MEDIUM, + } + + @pytest.mark.asyncio async def test_gemini_prepare_conversation_removes_leading_model_content(): provider = ProviderGoogleGenAI.__new__(ProviderGoogleGenAI)