From 8bcb8b8e8754486272f0a36fd56db5ade307caaa Mon Sep 17 00:00:00 2001 From: Julien Talbot Date: Fri, 10 Apr 2026 12:51:30 +0400 Subject: [PATCH] feat(providers): add native xAI provider Adds xAI as a first-class provider: ProviderConfig in auth.py, HermesOverlay in providers.py, 11 curated Grok models, URL mapping in model_metadata.py, aliases (x-ai, x.ai), and env var tests. Uses standard OpenAI-compatible chat completions. Closes #7050 --- agent/model_metadata.py | 1 + hermes_cli/auth.py | 8 ++++++++ hermes_cli/models.py | 13 +++++++++++++ hermes_cli/providers.py | 10 ++++++++++ tests/hermes_cli/test_api_key_providers.py | 7 +++++++ 5 files changed, 39 insertions(+) diff --git a/agent/model_metadata.py b/agent/model_metadata.py index 0fdf1a5245..2d1c02ac94 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -213,6 +213,7 @@ _URL_TO_PROVIDER: Dict[str, str] = { "models.github.ai": "copilot", "api.fireworks.ai": "fireworks", "opencode.ai": "opencode-go", + "api.x.ai": "xai", } diff --git a/hermes_cli/auth.py b/hermes_cli/auth.py index befa97d098..021e9c0cae 100644 --- a/hermes_cli/auth.py +++ b/hermes_cli/auth.py @@ -198,6 +198,14 @@ PROVIDER_REGISTRY: Dict[str, ProviderConfig] = { api_key_env_vars=("DEEPSEEK_API_KEY",), base_url_env_var="DEEPSEEK_BASE_URL", ), + "xai": ProviderConfig( + id="xai", + name="xAI", + auth_type="api_key", + inference_base_url="https://api.x.ai/v1", + api_key_env_vars=("XAI_API_KEY",), + base_url_env_var="XAI_BASE_URL", + ), "ai-gateway": ProviderConfig( id="ai-gateway", name="AI Gateway", diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 93b6ff9e05..0d99294864 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -129,6 +129,19 @@ _PROVIDER_MODELS: dict[str, list[str]] = { "glm-4.5", "glm-4.5-flash", ], + "xai": [ + "grok-4.20-0309-reasoning", + "grok-4.20-0309-non-reasoning", + "grok-4.20-multi-agent-0309", + "grok-4-1-fast-reasoning", + "grok-4-1-fast-non-reasoning", + "grok-4-fast-reasoning", + "grok-4-fast-non-reasoning", + "grok-4-0709", + "grok-code-fast-1", + "grok-3", + "grok-3-mini", + ], "kimi-coding": [ "kimi-for-coding", "kimi-k2.5", diff --git a/hermes_cli/providers.py b/hermes_cli/providers.py index 2210ab00ab..899c358746 100644 --- a/hermes_cli/providers.py +++ b/hermes_cli/providers.py @@ -127,6 +127,11 @@ HERMES_OVERLAYS: Dict[str, HermesOverlay] = { is_aggregator=True, base_url_env_var="HF_BASE_URL", ), + "xai": HermesOverlay( + transport="openai_chat", + base_url_override="https://api.x.ai/v1", + base_url_env_var="XAI_BASE_URL", + ), } @@ -163,6 +168,10 @@ ALIASES: Dict[str, str] = { "z.ai": "zai", "zhipu": "zai", + # xai + "x-ai": "xai", + "x.ai": "xai", + # kimi-for-coding (models.dev ID) "kimi": "kimi-for-coding", "kimi-coding": "kimi-for-coding", @@ -341,6 +350,7 @@ def get_label(provider_id: str) -> str: + def is_aggregator(provider: str) -> bool: """Return True when the provider is a multi-model aggregator.""" pdef = get_provider(provider) diff --git a/tests/hermes_cli/test_api_key_providers.py b/tests/hermes_cli/test_api_key_providers.py index 5bb7d07065..039799d427 100644 --- a/tests/hermes_cli/test_api_key_providers.py +++ b/tests/hermes_cli/test_api_key_providers.py @@ -40,6 +40,7 @@ class TestProviderRegistry: ("copilot", "GitHub Copilot", "api_key"), ("huggingface", "Hugging Face", "api_key"), ("zai", "Z.AI / GLM", "api_key"), + ("xai", "xAI", "api_key"), ("kimi-coding", "Kimi / Moonshot", "api_key"), ("minimax", "MiniMax", "api_key"), ("minimax-cn", "MiniMax (China)", "api_key"), @@ -58,6 +59,12 @@ class TestProviderRegistry: assert pconfig.api_key_env_vars == ("GLM_API_KEY", "ZAI_API_KEY", "Z_AI_API_KEY") assert pconfig.base_url_env_var == "GLM_BASE_URL" + def test_xai_env_vars(self): + pconfig = PROVIDER_REGISTRY["xai"] + assert pconfig.api_key_env_vars == ("XAI_API_KEY",) + assert pconfig.base_url_env_var == "XAI_BASE_URL" + assert pconfig.inference_base_url == "https://api.x.ai/v1" + def test_copilot_env_vars(self): pconfig = PROVIDER_REGISTRY["copilot"] assert pconfig.api_key_env_vars == ("COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN")