{"repo":"NousResearch/hermes-agent","public_id":"16941","display_label":"Gemini reasoning config mapping and visibility bugs","summary":"These issues and fixes address how reasoning/thinking configuration is bridged and mapped for Gemini and Vertex routes, including thought summaries incorrectly appearing in user-visible content and reasoning effort being incorrectly coupled with thought-summary visibility.","subsystem":"agent","counts":{"members":6,"open_issues":1,"open_issues_distinct":1,"open_prs":2,"closed_issues":1,"merged_prs":2,"attempt_prs":2},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":16999},"resolution":{"state":"partially_resolved","by":"pr-16999"},"freshness":{"state":"verified","last_verified_at":"2026-04-29T19:10:41+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.29066666666666663},"fix_edges":[{"issue_number":16941,"pr_number":16999,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":16941,"pr_number":17605,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":16941,"title":"[Bug]: Gemini chat-completions route does not bridge reasoning_config into thinking_config, so Discord show_reasoning stays empty","url":null,"state":"closed"},{"kind":"issue","number":64035,"title":"[Bug]: Gemini/Vertex thought summaries leak into user-visible content with display.show_reasoning: false (effort and visibility coupled via include_thoughts)","url":null,"state":"open"},{"kind":"pr","number":16999,"title":"fix(gemini): bridge reasoning_config into thinking_config for chat-completions routes","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/transports/chat_completions.py","run_agent.py","tests/agent/transports/test_chat_completions.py"],"fix_hunks":[{"file":"agent/transports/chat_completions.py","hunks":["@@ -18,6 +18,58 @@\n from agent.transports.types import NormalizedResponse, ToolCall, Usage\n \n \n+def _build_gemini_thinking_config(model: str, reasoning_config: dict | None) -> dict | None:\n+    \"\"\"Translate Hermes/OpenRouter-style reasoning config to Gemini thinkingConfig.\n+\n+    Gemini native/cloud-code adapters do not read ``extra_body.reasoning``.\n+    They only inspect ``extra_body.thinking_config`` / ``thinkingConfig`` and\n+    then request thought parts with ``includeThoughts`` enabled.\n+    \"\"\"\n+    if reasoning_config is None or not isinstance(reasoning_config, dict):\n+        return None\n+\n+    if reasoning_config.get(\"enabled\") is False:\n+        # Gemini can hide thought parts even when internal thinking still\n+        # happens; omit thinkingLevel to avoid model-specific validation quirks.\n+        return {\"includeThoughts\": False}\n+\n+    effort = str(reasoning_config.get(\"effort\", \"medium\") or \"medium\").strip().lower()\n+    if effort == \"none\":\n+        return {\"includeThoughts\": False}\n+\n+    thinking_config: Dict[str, Any] = {\"includeThoughts\": True}\n+    normalized_model = (model or \"\").strip().lower()\n+    if normalized_model.startswith(\"google/\"):\n+        normalized_model = normalized_model.split(\"/\", 1)[1]\n+\n+    # Gemini 2.5 accepts thinkingBudget; don't guess a budget from Hermes'\n+    # coarse effort levels. ``includeThoughts`` alone is enough to surface\n+    # thought parts without risking request validation errors.\n+    if normalized_model.startswith(\"gemini-2.5-\"):\n+        return thinking_config\n+\n+    if effort not in {\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\"}:\n+        effort = \"medium\"\n+\n+    # Gemini 3 Flash documents low/medium/high thinking levels; Gemini 3 Pro\n+    # is stricter (low/high). Clamp Hermes' wider effort set to what each\n+    # family accepts so we never forward an undocumented level verbatim.\n+    if normalized_model.startswith((\"gemini-3\", \"gemini-3.1\")):\n+        if \"flash\" in normalized_model:\n+            if effort in {\"minimal\", \"low\"}:\n+                thinking_config[\"thinkingLevel\"] = \"low\"\n+            elif effort in {\"high\", \"xhigh\"}:\n+                thinking_config[\"thinkingLevel\"] = \"high\"\n+            else:\n+                thinking_config[\"thinkingLevel\"] = \"medium\"\n+        elif \"pro\" in normalized_model:\n+            thinking_config[\"thinkingLevel\"] = (\n+                \"high\" if effort in {\"high\", \"xhigh\"} else \"low\"\n+            )\n+\n+    return thinking_config\n+\n+\n class ChatCompletionsTransport(ProviderTransport):\n     \"\"\"Transport for api_mode='chat_completions'.\n ","@@ -241,6 +293,7 @@ def build_kwargs(\n         is_openrouter = params.get(\"is_openrouter\", False)\n         is_nous = params.get(\"is_nous\", False)\n         is_github_models = params.get(\"is_github_models\", False)\n+        provider_name = str(params.get(\"provider_name\") or \"\").strip().lower()\n \n         provider_prefs = params.get(\"provider_preferences\")\n         if provider_prefs and is_openrouter:","@@ -293,6 +346,11 @@ def build_kwargs(\n         if is_qwen:\n             extra_body[\"vl_high_resolution_images\"] = True\n \n+        if provider_name in {\"gemini\", \"google-gemini-cli\"}:\n+            thinking_config = _build_gemini_thinking_config(model, reasoning_config)\n+            if thinking_config:\n+                extra_body[\"thinking_config\"] = thinking_config\n+\n         # Merge any pre-built extra_body additions\n         additions = params.get(\"extra_body_additions\")\n         if additions:"]},{"file":"run_agent.py","hunks":["@@ -8094,6 +8094,7 @@ def _build_api_kwargs(self, api_messages: list) -> dict:\n             supports_reasoning=self._supports_reasoning_extra_body(),\n             github_reasoning_extra=self._github_models_reasoning_extra_body() if _is_gh else None,\n             anthropic_max_output=_ant_max,\n+            provider_name=self.provider,\n         )\n \n     def _supports_reasoning_extra_body(self) -> bool:"]},{"file":"tests/agent/transports/test_chat_completions.py","hunks":["@@ -4,7 +4,7 @@\n from types import SimpleNamespace\n \n from agent.transports import get_transport\n-from agent.transports.types import NormalizedResponse, ToolCall\n+from agent.transports.types import NormalizedResponse\n \n \n @pytest.fixture","@@ -122,6 +122,90 @@ def test_custom_think_false(self, transport):\n         )\n         assert kw[\"extra_body\"][\"think\"] is False\n \n+    def test_gemini_without_explicit_reasoning_config_keeps_existing_behavior(self, transport):\n+        msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n+        kw = transport.build_kwargs(\n+            model=\"gemini-3-flash-preview\",\n+            messages=msgs,\n+            provider_name=\"gemini\",\n+        )\n+        assert \"thinking_config\" not in kw.get(\"extra_body\", {})\n+\n+    def test_gemini_flash_reasoning_maps_to_thinking_config(self, transport):\n+        msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n+        kw = transport.build_kwargs(\n+            model=\"gemini-3-flash-preview\",\n+            messages=msgs,\n+            provider_name=\"gemini\",\n+            reasoning_config={\"enabled\": True, \"effort\": \"high\"},\n+        )\n+        assert kw[\"extra_body\"][\"thinking_config\"] == {\n+            \"includeThoughts\": True,\n+            \"thinkingLevel\": \"high\",\n+        }\n+\n+    def test_gemini_25_reasoning_only_enables_visible_thoughts(self, transport):\n+        msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n+        kw = transport.build_kwargs(\n+            model=\"gemini-2.5-flash\",\n+            messages=msgs,\n+            provider_name=\"gemini\",\n+            reasoning_config={\"enabled\": True, \"effort\": \"high\"},\n+        )\n+        assert kw[\"extra_body\"][\"thinking_config\"] == {\n+            \"includeThoughts\": True,\n+        }\n+\n+    def test_gemini_pro_reasoning_clamps_to_supported_levels(self, transport):\n+        msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n+        kw = transport.build_kwargs(\n+            model=\"google/gemini-3.1-pro-preview\",\n+            messages=msgs,\n+            provider_name=\"gemini\",\n+            reasoning_config={\"enabled\": True, \"effort\": \"medium\"},\n+        )\n+        assert kw[\"extra_body\"][\"thinking_config\"] == {\n+            \"includeThoughts\": True,\n+            \"thinkingLevel\": \"low\",\n+        }\n+\n+    def test_gemini_disabled_reasoning_hides_thoughts(self, transport):\n+        msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n+        kw = transport.build_kwargs(\n+            model=\"gemini-3-flash-preview\",\n+            messages=msgs,\n+            provider_name=\"gemini\",\n+            reasoning_config={\"enabled\": False},\n+        )\n+        assert kw[\"extra_body\"][\"thinking_config\"] == {\n+            \"includeThoughts\": False,\n+        }\n+\n+    def test_gemini_xhigh_clamps_to_high(self, transport):\n+        msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n+        kw = transport.build_kwargs(\n+            model=\"gemini-3-flash-preview\",\n+            messages=msgs,\n+            provider_name=\"gemini\",\n+            reasoning_config={\"enabled\": True, \"effort\": \"xhigh\"},\n+        )\n+        assert kw[\"extra_body\"][\"thinking_config\"][\"thinkingLevel\"] == \"high\"\n+\n+    def test_gemini_flash_minimal_clamps_to_low(self, transport):\n+        # Gemini 3 Flash documents low/medium/high; \"minimal\" isn't accepted,\n+        # so clamp it down to \"low\" rather than forwarding it verbatim.\n+        msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n+        kw = transport.build_kwargs(\n+            model=\"gemini-3-flash-preview\",\n+            messages=msgs,\n+            provider_name=\"gemini\",\n+            reasoning_config={\"enabled\": True, \"effort\": \"minimal\"},\n+        )\n+        assert kw[\"extra_body\"][\"thinking_config\"] == {\n+            \"includeThoughts\": True,\n+            \"thinkingLevel\": \"low\",\n+        }\n+\n     def test_max_tokens_with_fn(self, transport):\n         msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n         kw = transport.build_kwargs(\n"]}]},{"kind":"pr","number":17605,"title":"fix(gemini): nest OpenAI-compat thinking config under google (salvage #17389)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["agent/auxiliary_client.py","agent/transports/chat_completions.py","run_agent.py","scripts/release.py","tests/agent/transports/test_chat_completions.py","tests/run_agent/test_provider_parity.py"],"fix_hunks":[{"file":"agent/auxiliary_client.py","hunks":["@@ -101,6 +101,14 @@ def __repr__(self):\n logger = logging.getLogger(__name__)\n \n \n+def _safe_isinstance(obj: Any, maybe_type: Any) -> bool:\n+    \"\"\"Return False instead of raising when a patched symbol is not a type.\"\"\"\n+    try:\n+        return isinstance(obj, maybe_type)\n+    except TypeError:\n+        return False\n+\n+\n def _extract_url_query_params(url: str):\n     \"\"\"Extract query params from URL, return (clean_url, default_query dict or None).\"\"\"\n     parsed = urlparse(url)","@@ -861,20 +869,20 @@ def _maybe_wrap_anthropic(\n     - The ``anthropic`` SDK is not installed (falls back to OpenAI wire).\n     \"\"\"\n     # Already wrapped — don't double-wrap.\n-    if isinstance(client_obj, AnthropicAuxiliaryClient):\n+    if _safe_isinstance(client_obj, AnthropicAuxiliaryClient):\n         return client_obj\n     # Other specialized adapters we should never re-dispatch.\n-    if isinstance(client_obj, CodexAuxiliaryClient):\n+    if _safe_isinstance(client_obj, CodexAuxiliaryClient):\n         return client_obj\n     try:\n         from agent.gemini_native_adapter import GeminiNativeClient\n-        if isinstance(client_obj, GeminiNativeClient):\n+        if _safe_isinstance(client_obj, GeminiNativeClient):\n             return client_obj\n     except ImportError:\n         pass\n     try:\n         from agent.copilot_acp_client import CopilotACPClient\n-        if isinstance(client_obj, CopilotACPClient):\n+        if _safe_isinstance(client_obj, CopilotACPClient):\n             return client_obj\n     except ImportError:\n         pass"]},{"file":"agent/transports/chat_completions.py","hunks":["@@ -20,12 +20,7 @@\n \n \n def _build_gemini_thinking_config(model: str, reasoning_config: dict | None) -> dict | None:\n-    \"\"\"Translate Hermes/OpenRouter-style reasoning config to Gemini thinkingConfig.\n-\n-    Gemini native/cloud-code adapters do not read ``extra_body.reasoning``.\n-    They only inspect ``extra_body.thinking_config`` / ``thinkingConfig`` and\n-    then request thought parts with ``includeThoughts`` enabled.\n-    \"\"\"\n+    \"\"\"Translate Hermes/OpenRouter-style reasoning config to Gemini thinkingConfig.\"\"\"\n     if reasoning_config is None or not isinstance(reasoning_config, dict):\n         return None\n ","@@ -71,6 +66,30 @@ def _build_gemini_thinking_config(model: str, reasoning_config: dict | None) ->\n     return thinking_config\n \n \n+def _snake_case_gemini_thinking_config(config: dict | None) -> dict | None:\n+    \"\"\"Convert Gemini thinking config keys to the OpenAI-compat field names.\"\"\"\n+    if not isinstance(config, dict) or not config:\n+        return None\n+\n+    translated: Dict[str, Any] = {}\n+    if isinstance(config.get(\"includeThoughts\"), bool):\n+        translated[\"include_thoughts\"] = config[\"includeThoughts\"]\n+    if isinstance(config.get(\"thinkingLevel\"), str) and config[\"thinkingLevel\"].strip():\n+        translated[\"thinking_level\"] = config[\"thinkingLevel\"].strip().lower()\n+    if isinstance(config.get(\"thinkingBudget\"), (int, float)):\n+        translated[\"thinking_budget\"] = int(config[\"thinkingBudget\"])\n+    return translated or None\n+\n+\n+def _is_gemini_openai_compat_base_url(base_url: Any) -> bool:\n+    normalized = str(base_url or \"\").strip().rstrip(\"/\").lower()\n+    if not normalized:\n+        return False\n+    if \"generativelanguage.googleapis.com\" not in normalized:\n+        return False\n+    return normalized.endswith(\"/openai\")\n+\n+\n class ChatCompletionsTransport(ProviderTransport):\n     \"\"\"Transport for api_mode='chat_completions'.\n ","@@ -309,6 +328,7 @@ def build_kwargs(\n         is_nous = params.get(\"is_nous\", False)\n         is_github_models = params.get(\"is_github_models\", False)\n         provider_name = str(params.get(\"provider_name\") or \"\").strip().lower()\n+        base_url = params.get(\"base_url\")\n \n         provider_prefs = params.get(\"provider_preferences\")\n         if provider_prefs and is_openrouter:","@@ -362,7 +382,19 @@ def build_kwargs(\n         if is_qwen:\n             extra_body[\"vl_high_resolution_images\"] = True\n \n-        if provider_name in {\"gemini\", \"google-gemini-cli\"}:\n+        if provider_name == \"gemini\":\n+            raw_thinking_config = _build_gemini_thinking_config(model, reasoning_config)\n+            if _is_gemini_openai_compat_base_url(base_url):\n+                thinking_config = _snake_case_gemini_thinking_config(raw_thinking_config)\n+                if thinking_config:\n+                    openai_compat_extra = extra_body.get(\"extra_body\", {})\n+                    google_extra = openai_compat_extra.get(\"google\", {})\n+                    google_extra[\"thinking_config\"] = thinking_config\n+                    openai_compat_extra[\"google\"] = google_extra\n+                    extra_body[\"extra_body\"] = openai_compat_extra\n+            elif raw_thinking_config:\n+                extra_body[\"thinking_config\"] = raw_thinking_config\n+        elif provider_name == \"google-gemini-cli\":\n             thinking_config = _build_gemini_thinking_config(model, reasoning_config)\n             if thinking_config:\n                 extra_body[\"thinking_config\"] = thinking_config"]},{"file":"run_agent.py","hunks":["@@ -8266,6 +8266,7 @@ def _build_api_kwargs(self, api_messages: list) -> dict:\n             model=self.model,\n             messages=_msgs_for_chat,\n             tools=self.tools,\n+            base_url=self.base_url,\n             timeout=self._resolved_api_call_timeout(),\n             max_tokens=[redacted:secret-assignment]\n             ephemeral_max_output_tokens=[redacted:secret-assignment]"]},{"file":"scripts/release.py","hunks":["@@ -66,6 +66,7 @@\n     \"[redacted:email]\": \"dlkakbs\",\n     \"[redacted:email]\": \"BeliefanX\",\n     \"[redacted:email]\": \"Mind-Dragon\",\n+    \"[redacted:email]\": \"Nanako0129\",\n     \"[redacted:email]\": \"westers\",\n     \"[redacted:email]\": \"yeyitech\",\n     \"[redacted:email]\": \"beenherebefore\","]},{"file":"tests/agent/transports/test_chat_completions.py","hunks":["@@ -122,21 +122,25 @@ def test_custom_think_false(self, transport):\n         )\n         assert kw[\"extra_body\"][\"think\"] is False\n \n-    def test_gemini_without_explicit_reasoning_config_keeps_existing_behavior(self, transport):\n+    def test_gemini_native_without_explicit_reasoning_config_keeps_existing_behavior(self, transport):\n         msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n         kw = transport.build_kwargs(\n             model=\"gemini-3-flash-preview\",\n             messages=msgs,\n             provider_name=\"gemini\",\n+            base_url=\"https://generativelanguage.googleapis.com/v1beta\",\n         )\n         assert \"thinking_config\" not in kw.get(\"extra_body\", {})\n+        assert \"google\" not in kw.get(\"extra_body\", {})\n+        assert \"extra_body\" not in kw.get(\"extra_body\", {})\n \n-    def test_gemini_flash_reasoning_maps_to_thinking_config(self, transport):\n+    def test_gemini_native_flash_reasoning_maps_to_top_level_thinking_config(self, transport):\n         msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n         kw = transport.build_kwargs(\n             model=\"gemini-3-flash-preview\",\n             messages=msgs,\n             provider_name=\"gemini\",\n+            base_url=\"https://generativelanguage.googleapis.com/v1beta\",\n             reasoning_config={\"enabled\": True, \"effort\": \"high\"},\n         )\n         assert kw[\"extra_body\"][\"thinking_config\"] == {","@@ -144,52 +148,85 @@ def test_gemini_flash_reasoning_maps_to_thinking_config(self, transport):\n             \"thinkingLevel\": \"high\",\n         }\n \n-    def test_gemini_25_reasoning_only_enables_visible_thoughts(self, transport):\n+    def test_gemini_openai_compat_flash_reasoning_maps_to_nested_google_thinking_config(self, transport):\n+        msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n+        kw = transport.build_kwargs(\n+            model=\"gemini-3-flash-preview\",\n+            messages=msgs,\n+            provider_name=\"gemini\",\n+            base_url=\"https://generativelanguage.googleapis.com/v1beta/openai\",\n+            reasoning_config={\"enabled\": True, \"effort\": \"high\"},\n+        )\n+        assert \"thinking_config\" not in kw[\"extra_body\"]\n+        assert kw[\"extra_body\"][\"extra_body\"][\"google\"][\"thinking_config\"] == {\n+            \"include_thoughts\": True,\n+            \"thinking_level\": \"high\",\n+        }\n+\n+    def test_gemini_native_25_reasoning_only_enables_visible_thoughts(self, transport):\n         msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n         kw = transport.build_kwargs(\n             model=\"gemini-2.5-flash\",\n             messages=msgs,\n             provider_name=\"gemini\",\n+            base_url=\"https://generativelanguage.googleapis.com/v1beta\",\n             reasoning_config={\"enabled\": True, \"effort\": \"high\"},\n         )\n         assert kw[\"extra_body\"][\"thinking_config\"] == {\n             \"includeThoughts\": True,\n         }\n \n-    def test_gemini_pro_reasoning_clamps_to_supported_levels(self, transport):\n+    def test_gemini_openai_compat_pro_reasoning_clamps_to_supported_levels(self, transport):\n         msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n         kw = transport.build_kwargs(\n             model=\"google/gemini-3.1-pro-preview\",\n             messages=msgs,\n             provider_name=\"gemini\",\n+            base_url=\"https://generativelanguage.googleapis.com/v1beta/openai\",\n             reasoning_config={\"enabled\": True, \"effort\": \"medium\"},\n         )\n-        assert kw[\"extra_body\"][\"thinking_config\"] == {\n-            \"includeThoughts\": True,\n-            \"thinkingLevel\": \"low\",\n+        assert kw[\"extra_body\"][\"extra_body\"][\"google\"][\"thinking_config\"] == {\n+            \"include_thoughts\": True,\n+            \"thinking_level\": \"low\",\n         }\n \n-    def test_gemini_disabled_reasoning_hides_thoughts(self, transport):\n+    def test_gemini_native_disabled_reasoning_hides_thoughts(self, transport):\n         msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n         kw = transport.build_kwargs(\n             model=\"gemini-3-flash-preview\",\n             messages=msgs,\n             provider_name=\"gemini\",\n+            base_url=\"https://generativelanguage.googleapis.com/v1beta\",\n             reasoning_config={\"enabled\": False},\n         )\n         assert kw[\"extra_body\"][\"thinking_config\"] == {\n             \"includeThoughts\": False,\n         }\n \n-    def test_gemini_xhigh_clamps_to_high(self, transport):\n+    def test_gemini_openai_compat_xhigh_clamps_to_high(self, transport):\n         msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n         kw = transport.build_kwargs(\n             model=\"gemini-3-flash-preview\",\n             messages=msgs,\n             provider_name=\"gemini\",\n+            base_url=\"https://generativelanguage.googleapis.com/v1beta/openai\",\n             reasoning_config={\"enabled\": True, \"effort\": \"xhigh\"},\n         )\n-        assert kw[\"extra_body\"][\"thinking_config\"][\"thinkingLevel\"] == \"high\"\n+        assert kw[\"extra_body\"][\"extra_body\"][\"google\"][\"thinking_config\"][\"thinking_level\"] == \"high\"\n+\n+    def test_google_gemini_cli_keeps_top_level_thinking_config(self, transport):\n+        msgs = [{\"role\": \"user\", \"content\": \"Hi\"}]\n+        kw = transport.build_kwargs(\n+            model=\"gemini-3-flash-preview\",\n+            messages=msgs,\n+            provider_name=\"google-gemini-cli\",\n+            reasoning_config={\"enabled\": True, \"effort\": \"high\"},\n+        )\n+        assert kw[\"extra_body\"][\"thinking_config\"] == {\n+            \"includeThoughts\": True,\n+            \"thinkingLevel\": \"high\",\n+        }\n+        assert \"google\" not in kw[\"extra_body\"]\n \n     def test_gemini_flash_minimal_clamps_to_low(self, transport):\n         # Gemini 3 Flash documents low/medium/high; \"minimal\" isn't accepted,","@@ -199,11 +236,12 @@ def test_gemini_flash_minimal_clamps_to_low(self, transport):\n             model=\"gemini-3-flash-preview\",\n             messages=msgs,\n             provider_name=\"gemini\",\n+            base_url=\"https://generativelanguage.googleapis.com/v1beta/openai\",\n             reasoning_config={\"enabled\": True, \"effort\": \"minimal\"},\n         )\n-        assert kw[\"extra_body\"][\"thinking_config\"] == {\n-            \"includeThoughts\": True,\n-            \"thinkingLevel\": \"low\",\n+        assert kw[\"extra_body\"][\"extra_body\"][\"google\"][\"thinking_config\"] == {\n+            \"include_thoughts\": True,\n+            \"thinking_level\": \"low\",\n         }\n \n     def test_max_tokens_with_fn(self, transport):"]},{"file":"tests/run_agent/test_provider_parity.py","hunks":["@@ -144,6 +144,36 @@ def test_strips_codex_only_tool_call_fields_from_chat_messages(self, monkeypatch\n         assert messages[1][\"tool_calls\"][0][\"response_item_id\"] == \"fc_123\"\n         assert \"codex_reasoning_items\" in messages[1]\n \n+    def test_gemini_native_passes_base_url_for_top_level_thinking_config(self, monkeypatch):\n+        agent = _make_agent(\n+            monkeypatch,\n+            \"gemini\",\n+            base_url=\"https://generativelanguage.googleapis.com/v1beta\",\n+            model=\"gemini-3-flash-preview\",\n+        )\n+        agent.reasoning_config = {\"enabled\": True, \"effort\": \"high\"}\n+        kwargs = agent._build_api_kwargs([{\"role\": \"user\", \"content\": \"hi\"}])\n+        assert kwargs[\"extra_body\"][\"thinking_config\"] == {\n+            \"includeThoughts\": True,\n+            \"thinkingLevel\": \"high\",\n+        }\n+        assert \"extra_body\" not in kwargs[\"extra_body\"]\n+\n+    def test_gemini_openai_compat_passes_base_url_for_nested_google_thinking_config(self, monkeypatch):\n+        agent = _make_agent(\n+            monkeypatch,\n+            \"gemini\",\n+            base_url=\"https://generativelanguage.googleapis.com/v1beta/openai\",\n+            model=\"gemini-3.1-pro-preview\",\n+        )\n+        agent.reasoning_config = {\"enabled\": True, \"effort\": \"high\"}\n+        kwargs = agent._build_api_kwargs([{\"role\": \"user\", \"content\": \"hi\"}])\n+        assert \"thinking_config\" not in kwargs[\"extra_body\"]\n+        assert kwargs[\"extra_body\"][\"extra_body\"][\"google\"][\"thinking_config\"] == {\n+            \"include_thoughts\": True,\n+            \"thinking_level\": \"high\",\n+        }\n+\n     def test_should_sanitize_tool_calls_codex_vs_chat(self, monkeypatch):\n         \"\"\"Codex API should NOT sanitize, all other APIs should sanitize.\"\"\"\n         # Codex mode should NOT need sanitization\n"]}]},{"kind":"pr","number":64036,"title":"fix(gemini): decouple reasoning effort from thought-summary visibility","url":null,"state":"open","close_reason":{"refs":[{"kind":"commit","value":"1a1f7378bbf6","number":0},{"kind":"pr","value":"#64035","number":64035},{"kind":"pr","value":"#64036","number":64036},{"kind":"pr","value":"#64036","number":64036},{"kind":"pr","value":"#64036","number":64036},{"kind":"pr","value":"#374151","number":374151},{"kind":"pr","value":"#64035","number":64035},{"kind":"pr","value":"#64036","number":64036}],"category":"partial_fix","implication":"needs_llm"},"merged":false},{"kind":"pr","number":91008,"title":"fix(vertex): route Gemini thought deltas to the reasoning stream, not visible content","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}