refactor(gmi): move User-Agent to profile.default_headers

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 #20907 by @isaachuangGMICLOUD.
This commit is contained in:
kshitijk4poor 2026-05-08 12:42:56 +05:30 committed by kshitij
parent 5d1bdf11b6
commit 81928f03ab
4 changed files with 82 additions and 0 deletions

View file

@ -2141,6 +2141,20 @@ 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"}
else:
# Fall back to profile.default_headers for providers that declare
# client-level headers on their ProviderProfile (e.g. attribution
# User-Agent strings). Provider is inferred from the hostname.
try:
from agent.model_metadata import _infer_provider_from_url
from providers import get_provider_profile as _gpf_async
_inferred = _infer_provider_from_url(sync_base_url)
if _inferred:
_ph_async = _gpf_async(_inferred)
if _ph_async and _ph_async.default_headers:
async_kwargs["default_headers"] = dict(_ph_async.default_headers)
except Exception:
pass
return AsyncOpenAI(**async_kwargs), model
@ -2368,6 +2382,16 @@ def resolve_provider_client(
extra["default_headers"] = copilot_request_headers(
is_agent_turn=True, is_vision=is_vision
)
else:
# Fall back to profile.default_headers for providers that
# declare client-level attribution headers on their profile.
try:
from providers import get_provider_profile as _gpf_custom
_ph_custom = _gpf_custom(provider)
if _ph_custom and _ph_custom.default_headers:
extra["default_headers"] = dict(_ph_custom.default_headers)
except Exception:
pass
client = OpenAI(api_key=custom_key, base_url=_clean_base, **extra)
client = _wrap_if_needed(client, final_model, custom_base, custom_key)
return (_to_async_client(client, final_model, is_vision=is_vision) if async_mode
@ -2556,6 +2580,18 @@ def resolve_provider_client(
headers.update(copilot_request_headers(
is_agent_turn=True, is_vision=is_vision
))
else:
# Fall back to profile.default_headers for providers that declare
# client-level attribution headers on their profile (e.g. GMI
# User-Agent for traffic identification, Vercel AI Gateway
# Referer/Title for analytics).
try:
from providers import get_provider_profile as _gpf_main
_ph_main = _gpf_main(provider)
if _ph_main and _ph_main.default_headers:
headers.update(_ph_main.default_headers)
except Exception:
pass
client = OpenAI(api_key=api_key, base_url=base_url,
**({"default_headers": headers} if headers else {}))