diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index 126f4615d..19bde946e 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -782,15 +782,6 @@ def _resolve_api_key_provider() -> Tuple[Optional[OpenAI], Optional[str]]: from hermes_cli.models import copilot_default_headers extra["default_headers"] = copilot_default_headers() - elif "generativelanguage.googleapis.com" in base_url.lower(): - # Google's OpenAI-compatible endpoint only accepts x-goog-api-key. - # Passing api_key= causes the SDK to inject Authorization: Bearer, - # which Google rejects with HTTP 400 "Multiple authentication - # credentials received". Use a placeholder for api_key and pass - # the real key via x-goog-api-key header instead. - # Fixes: https://github.com/NousResearch/hermes-agent/issues/7893 - extra["default_headers"] = {"x-goog-api-key": api_key} - api_key = "not-used" return OpenAI(api_key=api_key, base_url=base_url, **extra), model creds = resolve_api_key_provider_credentials(provider_id) @@ -812,15 +803,6 @@ def _resolve_api_key_provider() -> Tuple[Optional[OpenAI], Optional[str]]: from hermes_cli.models import copilot_default_headers extra["default_headers"] = copilot_default_headers() - elif "generativelanguage.googleapis.com" in base_url.lower(): - # Google's OpenAI-compatible endpoint only accepts x-goog-api-key. - # Passing api_key= causes the SDK to inject Authorization: Bearer, - # which Google rejects with HTTP 400 "Multiple authentication - # credentials received". Use a placeholder for api_key and pass - # the real key via x-goog-api-key header instead. - # Fixes: https://github.com/NousResearch/hermes-agent/issues/7893 - extra["default_headers"] = {"x-goog-api-key": api_key} - api_key = "not-used" return OpenAI(api_key=api_key, base_url=base_url, **extra), model return None, None @@ -1666,16 +1648,6 @@ def resolve_provider_client( from hermes_cli.models import copilot_default_headers headers.update(copilot_default_headers()) - elif "generativelanguage.googleapis.com" in base_url.lower(): - # Google's OpenAI-compatible endpoint only accepts x-goog-api-key. - # Passing api_key= causes the OpenAI SDK to inject Authorization: Bearer, - # which Google rejects with HTTP 400 "Multiple authentication credentials - # received". Use a placeholder for api_key and pass the real key via - # x-goog-api-key header instead. - # Fixes: https://github.com/NousResearch/hermes-agent/issues/7893 - headers["x-goog-api-key"] = api_key - api_key = "not-used" - client = OpenAI(api_key=api_key, base_url=base_url, **({"default_headers": headers} if headers else {})) diff --git a/run_agent.py b/run_agent.py index a0f4db548..756bb62ed 100644 --- a/run_agent.py +++ b/run_agent.py @@ -1054,16 +1054,6 @@ class AIAgent: } elif "portal.qwen.ai" in effective_base.lower(): client_kwargs["default_headers"] = _qwen_portal_headers() - elif "generativelanguage.googleapis.com" in effective_base.lower(): - # Google's OpenAI-compatible endpoint only accepts x-goog-api-key. - # The OpenAI SDK auto-injects Authorization: Bearer when api_key= is - # set to a real value, causing HTTP 400 "Multiple authentication - # credentials received". Pass a placeholder so the SDK does not - # emit Bearer, and carry the real key via x-goog-api-key instead. - # Fixes: https://github.com/NousResearch/hermes-agent/issues/7893 - real_key = client_kwargs["api_key"] - client_kwargs["api_key"] = "not-used" - client_kwargs["default_headers"] = {"x-goog-api-key": real_key} else: # No explicit creds — use the centralized provider router from agent.auxiliary_client import resolve_provider_client @@ -5245,17 +5235,6 @@ class AIAgent: self._client_kwargs["default_headers"] = {"User-Agent": "KimiCLI/1.30.0"} elif "portal.qwen.ai" in normalized: self._client_kwargs["default_headers"] = _qwen_portal_headers() - elif "generativelanguage.googleapis.com" in normalized: - # Google's endpoint rejects Bearer tokens; use x-goog-api-key instead. - # Swap the real key out of api_key and into the header so the OpenAI - # SDK does not emit Authorization: Bearer. - # Fixes: https://github.com/NousResearch/hermes-agent/issues/7893 - real_key = self._client_kwargs.get("api_key", "") - if real_key and real_key != "not-used": - self._client_kwargs["api_key"] = "not-used" - self._client_kwargs["default_headers"] = { - "x-goog-api-key": real_key or self._client_kwargs.get("api_key", ""), - } else: self._client_kwargs.pop("default_headers", None) diff --git a/tests/hermes_cli/test_gemini_provider.py b/tests/hermes_cli/test_gemini_provider.py index 9c6ee70aa..dbb1111fc 100644 --- a/tests/hermes_cli/test_gemini_provider.py +++ b/tests/hermes_cli/test_gemini_provider.py @@ -207,14 +207,8 @@ class TestGeminiAgentInit: assert agent.api_mode == "chat_completions" assert agent.provider == "gemini" - def test_gemini_uses_x_goog_api_key_not_bearer(self, monkeypatch): - """Regression test for issue #7893. - - When provider=gemini, the OpenAI client must be constructed with - api_key='not-used' and default_headers={'x-goog-api-key': real_key}. - This prevents the SDK from injecting Authorization: Bearer, which - Google's endpoint rejects with HTTP 400. - """ + def test_gemini_uses_bearer_auth(self, monkeypatch): + """Gemini OpenAI-compatible endpoint should receive the real API key.""" monkeypatch.setenv("GOOGLE_API_KEY", "AIzaSy_REAL_KEY") real_key = "AIzaSy_REAL_KEY" with patch("run_agent.OpenAI") as mock_openai: @@ -227,37 +221,22 @@ class TestGeminiAgentInit: base_url="https://generativelanguage.googleapis.com/v1beta/openai", ) call_kwargs = mock_openai.call_args[1] - # The SDK must NOT receive the real key as api_key (which would emit Bearer) - assert call_kwargs.get("api_key") == "not-used", ( - "api_key must be 'not-used' to suppress Authorization: Bearer for Gemini" - ) - # The real key must be in x-goog-api-key header + assert call_kwargs.get("api_key") == real_key headers = call_kwargs.get("default_headers", {}) - assert headers.get("x-goog-api-key") == real_key, ( - "x-goog-api-key header must carry the real Gemini API key" - ) + assert "x-goog-api-key" not in headers def test_gemini_resolve_provider_client_auth(self, monkeypatch): - """Regression test for issue #7893 — resolve_provider_client path. - - When resolve_provider_client('gemini') is called, the returned OpenAI - client must use x-goog-api-key header, not Authorization: Bearer. - """ + """resolve_provider_client('gemini') should pass the real API key through.""" monkeypatch.setenv("GEMINI_API_KEY", "AIzaSy_TEST_KEY") real_key = "AIzaSy_TEST_KEY" with patch("agent.auxiliary_client.OpenAI") as mock_openai: mock_openai.return_value = MagicMock() - mock_openai.return_value.api_key = "not-used" from agent.auxiliary_client import resolve_provider_client resolve_provider_client("gemini") call_kwargs = mock_openai.call_args[1] - assert call_kwargs.get("api_key") == "not-used", ( - "api_key must be 'not-used' to prevent Bearer injection for Gemini" - ) + assert call_kwargs.get("api_key") == real_key headers = call_kwargs.get("default_headers", {}) - assert headers.get("x-goog-api-key") == real_key, ( - "x-goog-api-key header must carry the real Gemini API key" - ) + assert "x-goog-api-key" not in headers # ── models.dev Integration ──