refactor(gmi): declare User-Agent attribution on provider profile (salvage #20907)#21701
Merged
kshitijk4poor merged 2 commits intoMay 8, 2026
Merged
Conversation
Collaborator
d515f11 to
96f3442
Compare
The previous revision of this PR added six GMI-specific branches
(`elif base_url_host_matches(..., 'api.gmi-serving.com')`) across
run_agent.py and agent/auxiliary_client.py, plus a _HERMES_UA_HEADERS
constant in auxiliary_client.py.
ProviderProfile already has a `default_headers: dict[str, str]` field
commented as 'Client-level quirks (set once at client construction)'.
Other plugins (ai-gateway, kimi-coding) already use it. Two of the four
auxiliary_client sites we previously patched already had a generic
`else: profile.default_headers` fallback that picked it up (so did
both run_agent sites).
This revision:
* Sets `default_headers={'User-Agent': 'HermesAgent/<ver>'}` on the
GMI profile in plugins/model-providers/gmi/__init__.py.
* Reverts all six GMI-specific branches in run_agent.py and
auxiliary_client.py.
* Adds the generic profile-fallback `else` block to the two
auxiliary_client sites (`_to_async_client`, `resolve_provider_client`)
that didn't have it yet. This benefits every provider whose profile
declares default_headers, not just GMI — e.g. Vercel AI Gateway's
HTTP-Referer/X-Title now flow through the async client path too.
* Replaces the GMI-specific URL-branch tests with a profile-level
assertion and keeps the run_agent integration test (with
`provider='gmi'` so the fallback picks up the profile).
Net diff vs main: +82/-0 across 5 files, touching only the GMI plugin,
two generic fallback blocks in auxiliary_client.py, AUTHOR_MAP, and
tests. No core files change.
Based on NousResearch#20907 by @isaachuangGMICLOUD.
96f3442 to
cfec180
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
User-Agent: HermesAgent/<version>header on all outbound requests toapi.gmi-serving.comso GMI can identify traffic from Hermes Agent.Based on #20907 by @isaachuangGMICLOUD.
Approach
Instead of sprinkling GMI-specific
elif base_url_host_matches(..., "api.gmi-serving.com"):branches acrossrun_agent.pyandagent/auxiliary_client.py, this revision uses the existingProviderProfile.default_headersfield (already commented as "Client-level quirks (set once at client construction)") and the generic profile-fallback plumbing that's already there for that purpose.GMI plugin owns the header:
Other plugins already use this pattern for the same purpose —
ai-gateway(HTTP-Referer + X-Title) andkimi-coding(User-Agent).Two of the four client-construction sites already had a profile-fallback
elsebranch that picks upprofile.default_headersafter the URL-specific hardcodes miss. Bothrun_agent.pysites (__init__client construction and_apply_client_headers_for_base_url) already had it. This PR adds the same generic fallback block to the remaining two sites inagent/auxiliary_client.py(_to_async_clientandresolve_provider_client's main path), closing the asymmetry.As a side benefit, this also fixes a pre-existing gap:
ai-gateway'sHTTP-Referer/X-Titleattribution headers now flow through_to_async_clienttoo.Files changed
plugins/model-providers/gmi/__init__.pydefault_headers={"User-Agent": ...}on GMI profileagent/auxiliary_client.pyelse: profile.default_headersfallback in two sites (applies to every provider that declaresdefault_headers, not just GMI)tests/hermes_cli/test_gmi_provider.pytests/run_agent/test_provider_attribution_headers.py_apply_client_headers_for_base_urlpicks up the profile's UAscripts/release.pyisaac.h@gmicloud.aiNet diff:
+83/-0across 5 files. No core files (run_agent.py,cli.py,gateway/) are modified.What data GMI receives
Only
User-Agent: HermesAgent/<version>. NoHTTP-Referer, noX-Title, noX-OpenRouter-Categories(those are OpenRouter-specific). The OpenAI Python SDK always sendsX-Stainless-*telemetry headers (OS, arch, runtime version) — SDK behavior, same for every provider.Testing
scripts/run_tests.sh tests/hermes_cli/test_gmi_provider.py tests/run_agent/test_provider_attribution_headers.py tests/agent/test_auxiliary_client.py tests/providers/— 253 passed.resolve_provider_client("gmi")main path applies UA ✓_to_async_clientapplies UA via hostname inference ✓AIAgent._apply_client_headers_for_base_urlapplies UA via profile fallback ✓Closes #20907