diff --git a/agent/auxiliary_client.py b/agent/auxiliary_client.py index 1e3d39c7ba5e..91ca81ea06f9 100644 --- a/agent/auxiliary_client.py +++ b/agent/auxiliary_client.py @@ -379,6 +379,10 @@ def build_or_headers(or_config: dict | None = None) -> dict: "User-Agent": f"HermesAgent/{_HERMES_VERSION}", } +_HERMES_UA_HEADERS = { + "User-Agent": f"HermesAgent/{_HERMES_VERSION}", +} + # Nous Portal extra_body for product attribution. # Callers should pass this as extra_body in chat.completions.create() # when the auxiliary client is backed by Nous Portal. @@ -1219,6 +1223,8 @@ 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 base_url_host_matches(base_url, "api.gmi-serving.com"): + extra["default_headers"] = dict(_HERMES_UA_HEADERS) else: try: from providers import get_provider_profile as _gpf_aux @@ -1254,6 +1260,8 @@ 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 base_url_host_matches(base_url, "api.gmi-serving.com"): + extra["default_headers"] = dict(_HERMES_UA_HEADERS) else: try: from providers import get_provider_profile as _gpf_aux2 @@ -2077,6 +2085,8 @@ def _to_async_client(sync_client, model: str, is_vision: bool = False): ) elif base_url_host_matches(sync_base_url, "api.kimi.com"): async_kwargs["default_headers"] = {"User-Agent": "claude-code/0.1.0"} + elif base_url_host_matches(sync_base_url, "api.gmi-serving.com"): + async_kwargs["default_headers"] = dict(_HERMES_UA_HEADERS) return AsyncOpenAI(**async_kwargs), model @@ -2492,6 +2502,8 @@ def _wrap_if_needed(client_obj, final_model_str: str, base_url_str: str = "", headers.update(copilot_request_headers( is_agent_turn=True, is_vision=is_vision )) + elif base_url_host_matches(base_url, "api.gmi-serving.com"): + headers.update(_HERMES_UA_HEADERS) 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 919a5875b65a..2883d885140b 100644 --- a/run_agent.py +++ b/run_agent.py @@ -1450,6 +1450,8 @@ def __init__( client_kwargs["default_headers"] = build_or_headers() elif base_url_host_matches(effective_base, "api.routermint.com"): client_kwargs["default_headers"] = _routermint_headers() + elif base_url_host_matches(effective_base, "api.gmi-serving.com"): + client_kwargs["default_headers"] = _routermint_headers() elif base_url_host_matches(effective_base, "api.githubcopilot.com"): from hermes_cli.models import copilot_default_headers @@ -6269,6 +6271,8 @@ def _apply_client_headers_for_base_url(self, base_url: str) -> None: self._client_kwargs["default_headers"] = dict(_AI_GATEWAY_HEADERS) elif base_url_host_matches(base_url, "api.routermint.com"): self._client_kwargs["default_headers"] = _routermint_headers() + elif base_url_host_matches(base_url, "api.gmi-serving.com"): + self._client_kwargs["default_headers"] = _routermint_headers() elif base_url_host_matches(base_url, "api.githubcopilot.com"): from hermes_cli.models import copilot_default_headers diff --git a/scripts/release.py b/scripts/release.py index 8249484e446f..71b51ac863ee 100755 --- a/scripts/release.py +++ b/scripts/release.py @@ -661,6 +661,7 @@ "mike@mikewaters.net": "mikewaters", "65117428+WadydX@users.noreply.github.com": "WadydX", "216480837+isaachuangGMICLOUD@users.noreply.github.com": "isaachuangGMICLOUD", + "isaac.h@gmicloud.ai": "isaachuangGMICLOUD", "nukuom976228@gmail.com": "hsy5571616", "11462216+Nan93@users.noreply.github.com": "Nan93", "l973401489@126.com": "zhouxiaoya12", diff --git a/tests/hermes_cli/test_gmi_provider.py b/tests/hermes_cli/test_gmi_provider.py index 0b9363e67530..5ed9dbe59996 100644 --- a/tests/hermes_cli/test_gmi_provider.py +++ b/tests/hermes_cli/test_gmi_provider.py @@ -284,6 +284,8 @@ def test_resolve_provider_client_uses_gmi_aux_default(self, monkeypatch): assert model == "google/gemini-3.1-flash-lite-preview" assert mock_openai.call_args.kwargs["api_key"] == "gmi-test-key" assert mock_openai.call_args.kwargs["base_url"] == "https://api.gmi-serving.com/v1" + headers = mock_openai.call_args.kwargs["default_headers"] + assert headers["User-Agent"].startswith("HermesAgent/") def test_resolve_provider_client_accepts_gmi_alias(self, monkeypatch): monkeypatch.setenv("GMI_API_KEY", "gmi-test-key") diff --git a/tests/run_agent/test_provider_attribution_headers.py b/tests/run_agent/test_provider_attribution_headers.py index 673a906cfbc3..4612f18802c3 100644 --- a/tests/run_agent/test_provider_attribution_headers.py +++ b/tests/run_agent/test_provider_attribution_headers.py @@ -65,6 +65,24 @@ def test_routermint_base_url_applies_user_agent_header(mock_openai): assert headers["User-Agent"].startswith("HermesAgent/") +@patch("run_agent.OpenAI") +def test_gmi_base_url_applies_user_agent_header(mock_openai): + mock_openai.return_value = MagicMock() + agent = AIAgent( + api_key="test-key", + base_url="https://api.gmi-serving.com/v1", + model="test/model", + quiet_mode=True, + skip_context_files=True, + skip_memory=True, + ) + + agent._apply_client_headers_for_base_url("https://api.gmi-serving.com/v1") + + headers = agent._client_kwargs["default_headers"] + assert headers["User-Agent"].startswith("HermesAgent/") + + @patch("run_agent.OpenAI") def test_unknown_base_url_clears_default_headers(mock_openai): mock_openai.return_value = MagicMock()