diff --git a/agent/anthropic_adapter.py b/agent/anthropic_adapter.py index 39edd74d00..52a86513fb 100644 --- a/agent/anthropic_adapter.py +++ b/agent/anthropic_adapter.py @@ -26,6 +26,13 @@ from typing import Any, Dict, List, Optional, Tuple from utils import base_url_host_matches, base_url_hostname, normalize_proxy_env_vars from agent.secret_scope import get_secret as _get_secret +try: + import hermes_cli as _hermes_cli + + _HERMES_VERSION = str(_hermes_cli.__version__) +except Exception: + _HERMES_VERSION = "0.0.0" + def _getenv(name: str, default: str = "") -> str: """Profile-scoped replacement for os.getenv on credential reads. @@ -857,12 +864,18 @@ def build_anthropic_client( ) if _is_kimi_coding_endpoint(base_url): - # Kimi's /coding endpoint requires User-Agent: claude-code/0.1.0 - # to be recognized as a valid Coding Agent. Without it, returns 403. - # Check this BEFORE _requires_bearer_auth since both match api.kimi.com/coding. + # Kimi's /coding endpoint requires a non-empty User-Agent to be + # recognized as a valid Coding Agent. Originally we sent + # ``claude-code/0.1.0`` (the minimum that avoided a 403), but the Kimi + # team asked us to identify ourselves properly so they can attribute + # traffic correctly. Send the same attribution header set we send to + # OpenRouter, Vercel AI Gateway, and Fireworks: + # HTTP-Referer + X-Title + HermesAgent User-Agent. kwargs["api_key"] = api_key kwargs["default_headers"] = { - "User-Agent": "claude-code/0.1.0", + "HTTP-Referer": "https://hermes-agent.nousresearch.com", + "X-Title": "Hermes Agent", + "User-Agent": f"HermesAgent/{_HERMES_VERSION}", **( {"anthropic-beta": ",".join(common_betas)} if common_betas else {} ) } elif _requires_bearer_auth(normalized_base_url): diff --git a/plugins/model-providers/kimi-coding/__init__.py b/plugins/model-providers/kimi-coding/__init__.py index fd0a24a714..4c1435003b 100644 --- a/plugins/model-providers/kimi-coding/__init__.py +++ b/plugins/model-providers/kimi-coding/__init__.py @@ -10,6 +10,7 @@ This module covers the chat_completions path (/v1 endpoint). from typing import Any from urllib.parse import urlparse +from hermes_cli import __version__ as _HERMES_VERSION from providers import register_provider from providers.base import OMIT_TEMPERATURE, ProviderProfile @@ -102,7 +103,11 @@ kimi = KimiProfile( base_url="https://api.moonshot.ai/v1", fixed_temperature=OMIT_TEMPERATURE, default_max_tokens=32000, - default_headers={"User-Agent": "hermes-agent/1.0"}, + default_headers={ + "HTTP-Referer": "https://hermes-agent.nousresearch.com", + "X-Title": "Hermes Agent", + "User-Agent": f"HermesAgent/{_HERMES_VERSION}", + }, default_aux_model="kimi-k2-turbo-preview", ) @@ -113,7 +118,11 @@ kimi_cn = KimiProfile( base_url="https://api.moonshot.cn/v1", fixed_temperature=OMIT_TEMPERATURE, default_max_tokens=32000, - default_headers={"User-Agent": "hermes-agent/1.0"}, + default_headers={ + "HTTP-Referer": "https://hermes-agent.nousresearch.com", + "X-Title": "Hermes Agent", + "User-Agent": f"HermesAgent/{_HERMES_VERSION}", + }, default_aux_model="kimi-k2-turbo-preview", ) diff --git a/run_agent.py b/run_agent.py index 64d3491a84..9589606745 100644 --- a/run_agent.py +++ b/run_agent.py @@ -5936,7 +5936,8 @@ class AIAgent: self._client_kwargs["default_headers"] = copilot_default_headers() elif base_url_host_matches(base_url, "api.kimi.com"): - self._client_kwargs["default_headers"] = {"User-Agent": "claude-code/0.1.0"} + from agent.auxiliary_client import _AI_GATEWAY_HEADERS + self._client_kwargs["default_headers"] = dict(_AI_GATEWAY_HEADERS) elif base_url_host_matches(base_url, "portal.qwen.ai"): self._client_kwargs["default_headers"] = _qwen_portal_headers() elif base_url_host_matches(base_url, "chatgpt.com"):