Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,15 @@ OPENCODE_ZEN_API_KEY=
# OpenCode Go provides access to open models (GLM-5, Kimi K2.5, MiniMax M2.5)
# $10/month subscription. Get your key at: https://opencode.ai/auth
OPENCODE_GO_API_KEY=

# =============================================================================
# LLM PROVIDER (Hugging Face Inference Providers)
# =============================================================================
# Hugging Face routes to 20+ open models via unified OpenAI-compatible endpoint.
# Free tier included ($0.10/month), no markup on provider rates.
# Get your token at: https://huggingface.co/settings/tokens
# Required permission: "Make calls to Inference Providers"
HF_TOKEN=
# OPENCODE_GO_BASE_URL=https://opencode.ai/zen/go/v1 # Override default base URL

# =============================================================================
Expand Down
19 changes: 19 additions & 0 deletions agent/model_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,25 @@
"qwen-plus-latest": 131072,
"qwen3.5-flash": 131072,
"qwen-vl-max": 32768,
# Hugging Face Inference Providers — model IDs use org/name format
"Qwen/Qwen3.5-397B-A17B": 131072,
"Qwen/Qwen3-235B-A22B-Thinking-2507": 131072,
"Qwen/Qwen3-Coder-480B-A35B-Instruct": 131072,
"Qwen/Qwen3-Coder-Next": 131072,
"Qwen/Qwen3-Next-80B-A3B-Instruct": 131072,
"Qwen/Qwen3-Next-80B-A3B-Thinking": 131072,
"deepseek-ai/DeepSeek-R1-0528": 65536,
"deepseek-ai/DeepSeek-V3.2": 65536,
"moonshotai/Kimi-K2-Instruct": 262144,
"moonshotai/Kimi-K2-Instruct-0905": 262144,
"moonshotai/Kimi-K2.5": 262144,
"moonshotai/Kimi-K2-Thinking": 262144,
"MiniMaxAI/MiniMax-M2.5": 204800,
"MiniMaxAI/MiniMax-M2.1": 204800,
"XiaomiMiMo/MiMo-V2-Flash": 32768,
"zai-org/GLM-5": 202752,
"zai-org/GLM-4.7": 202752,
"zai-org/GLM-4.7-Flash": 202752,
}


Expand Down
9 changes: 9 additions & 0 deletions hermes_cli/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ class ProviderConfig:
api_key_env_vars=("KILOCODE_API_KEY",),
base_url_env_var="KILOCODE_BASE_URL",
),
"huggingface": ProviderConfig(
id="huggingface",
name="Hugging Face",
auth_type="api_key",
inference_base_url="https://router.huggingface.co/v1",
api_key_env_vars=("HF_TOKEN",),
base_url_env_var="HF_BASE_URL",
),
}


Expand Down Expand Up @@ -574,6 +582,7 @@ def resolve_provider(
"claude": "anthropic", "claude-code": "anthropic",
"aigateway": "ai-gateway", "vercel": "ai-gateway", "vercel-ai-gateway": "ai-gateway",
"opencode": "opencode-zen", "zen": "opencode-zen",
"hf": "huggingface", "hugging-face": "huggingface", "huggingface-hub": "huggingface",
"go": "opencode-go", "opencode-go-sub": "opencode-go",
"kilo": "kilocode", "kilo-code": "kilocode", "kilo-gateway": "kilocode",
}
Expand Down
15 changes: 15 additions & 0 deletions hermes_cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,21 @@ def ensure_hermes_home():
"category": "provider",
"advanced": True,
},
"HF_TOKEN": {
"description": "Hugging Face token for Inference Providers (20+ open models via router.huggingface.co)",
"prompt": "Hugging Face Token",
"url": "https://huggingface.co/settings/tokens",
"password": True,
"category": "provider",
},
"HF_BASE_URL": {
"description": "Hugging Face Inference Providers base URL override",
"prompt": "HF base URL (leave empty for default)",
"url": None,
"password": False,
"category": "provider",
"advanced": True,
},

# ── Tool API keys ──
"PARALLEL_API_KEY": {
Expand Down
27 changes: 25 additions & 2 deletions hermes_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,7 @@ def cmd_model(args):
"ai-gateway": "AI Gateway",
"kilocode": "Kilo Code",
"alibaba": "Alibaba Cloud (DashScope)",
"huggingface": "Hugging Face",
"custom": "Custom endpoint",
}
active_label = provider_labels.get(active, active)
Expand All @@ -809,6 +810,7 @@ def cmd_model(args):
("opencode-go", "OpenCode Go (open models, $10/month subscription)"),
("ai-gateway", "AI Gateway (Vercel — 200+ models, pay-per-use)"),
("alibaba", "Alibaba Cloud / DashScope (Qwen models, Anthropic-compatible)"),
("huggingface", "Hugging Face Inference Providers (20+ open models)"),
]

# Add user-defined custom providers from config.yaml
Expand Down Expand Up @@ -877,7 +879,7 @@ def cmd_model(args):
_model_flow_anthropic(config, current_model)
elif selected_provider == "kimi-coding":
_model_flow_kimi(config, current_model)
elif selected_provider in ("zai", "minimax", "minimax-cn", "kilocode", "opencode-zen", "opencode-go", "ai-gateway", "alibaba"):
elif selected_provider in ("zai", "minimax", "minimax-cn", "kilocode", "opencode-zen", "opencode-go", "ai-gateway", "alibaba", "huggingface"):
_model_flow_api_key_provider(config, selected_provider, current_model)


Expand Down Expand Up @@ -1444,6 +1446,27 @@ def _model_flow_named_custom(config, provider_info):
"google/gemini-3-pro-preview",
"google/gemini-3-flash-preview",
],
# Curated model list sourced from https://models.dev (huggingface provider)
"huggingface": [
"Qwen/Qwen3.5-397B-A17B",
"Qwen/Qwen3-235B-A22B-Thinking-2507",
"Qwen/Qwen3-Coder-480B-A35B-Instruct",
"Qwen/Qwen3-Coder-Next",
"Qwen/Qwen3-Next-80B-A3B-Instruct",
"Qwen/Qwen3-Next-80B-A3B-Thinking",
"deepseek-ai/DeepSeek-R1-0528",
"deepseek-ai/DeepSeek-V3.2",
"moonshotai/Kimi-K2-Instruct",
"moonshotai/Kimi-K2-Instruct-0905",
"moonshotai/Kimi-K2.5",
"moonshotai/Kimi-K2-Thinking",
"MiniMaxAI/MiniMax-M2.5",
"MiniMaxAI/MiniMax-M2.1",
"XiaomiMiMo/MiMo-V2-Flash",
"zai-org/GLM-5",
"zai-org/GLM-4.7",
"zai-org/GLM-4.7-Flash",
],
}


Expand Down Expand Up @@ -2642,7 +2665,7 @@ def main():
)
chat_parser.add_argument(
"--provider",
choices=["auto", "openrouter", "nous", "openai-codex", "anthropic", "zai", "kimi-coding", "minimax", "minimax-cn", "kilocode"],
choices=["auto", "openrouter", "nous", "openai-codex", "anthropic", "huggingface", "zai", "kimi-coding", "minimax", "minimax-cn", "kilocode"],
default=None,
help="Inference provider (default: auto)"
)
Expand Down
28 changes: 27 additions & 1 deletion hermes_cli/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,28 @@
"qwen3.5-flash",
"qwen-vl-max",
],
# Curated model list for Hugging Face Inference Providers
# sourced from https://models.dev (huggingface provider)
"huggingface": [
"Qwen/Qwen3.5-397B-A17B",
"Qwen/Qwen3-235B-A22B-Thinking-2507",
"Qwen/Qwen3-Coder-480B-A35B-Instruct",
"Qwen/Qwen3-Coder-Next",
"Qwen/Qwen3-Next-80B-A3B-Instruct",
"Qwen/Qwen3-Next-80B-A3B-Thinking",
"deepseek-ai/DeepSeek-R1-0528",
"deepseek-ai/DeepSeek-V3.2",
"moonshotai/Kimi-K2-Instruct",
"moonshotai/Kimi-K2-Instruct-0905",
"moonshotai/Kimi-K2.5",
"moonshotai/Kimi-K2-Thinking",
"MiniMaxAI/MiniMax-M2.5",
"MiniMaxAI/MiniMax-M2.1",
"XiaomiMiMo/MiMo-V2-Flash",
"zai-org/GLM-5",
"zai-org/GLM-4.7",
"zai-org/GLM-4.7-Flash",
],
}

_PROVIDER_LABELS = {
Expand All @@ -172,6 +194,7 @@
"ai-gateway": "AI Gateway",
"kilocode": "Kilo Code",
"alibaba": "Alibaba Cloud (DashScope)",
"huggingface": "Hugging Face",
"custom": "Custom endpoint",
}

Expand Down Expand Up @@ -201,6 +224,9 @@
"aliyun": "alibaba",
"qwen": "alibaba",
"alibaba-cloud": "alibaba",
"hf": "huggingface",
"hugging-face": "huggingface",
"huggingface-hub": "huggingface",
}


Expand Down Expand Up @@ -234,7 +260,7 @@ def list_available_providers() -> list[dict[str, str]]:
# Canonical providers in display order
_PROVIDER_ORDER = [
"openrouter", "nous", "openai-codex",
"zai", "kimi-coding", "minimax", "minimax-cn", "kilocode", "anthropic", "alibaba",
"huggingface", "zai", "kimi-coding", "minimax", "minimax-cn", "kilocode", "anthropic", "alibaba",
"opencode-zen", "opencode-go",
"ai-gateway", "deepseek", "custom",
]
Expand Down
30 changes: 29 additions & 1 deletion hermes_cli/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ def _set_default_model(config: Dict[str, Any], model_name: str) -> None:
"minimax-cn": ["MiniMax-M2.5", "MiniMax-M2.5-highspeed", "MiniMax-M2.1"],
"ai-gateway": ["anthropic/claude-opus-4.6", "anthropic/claude-sonnet-4.6", "openai/gpt-5", "google/gemini-3-flash"],
"kilocode": ["anthropic/claude-opus-4.6", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4", "google/gemini-3-pro-preview", "google/gemini-3-flash-preview"],
"huggingface": [
"Qwen/Qwen3.5-397B-A17B", "Qwen/Qwen3-235B-A22B-Thinking-2507",
"Qwen/Qwen3-Coder-480B-A35B-Instruct", "deepseek-ai/DeepSeek-R1-0528",
"deepseek-ai/DeepSeek-V3.2", "moonshotai/Kimi-K2.5",
],
}


Expand Down Expand Up @@ -741,6 +746,7 @@ def setup_model_provider(config: dict):
"Alibaba Cloud / DashScope (Qwen models via Anthropic-compatible API)",
"OpenCode Zen (35+ curated models, pay-as-you-go)",
"OpenCode Go (open models, $10/month subscription)",
"Hugging Face Inference Providers (20+ open models)",
]
if keep_label:
provider_choices.append(keep_label)
Expand Down Expand Up @@ -1412,7 +1418,29 @@ def setup_model_provider(config: dict):
_set_model_provider(config, "opencode-go", pconfig.inference_base_url)
selected_base_url = pconfig.inference_base_url

# else: provider_idx == 14 (Keep current) — only shown when a provider already exists
elif provider_idx == 14: # Hugging Face Inference Providers
selected_provider = "huggingface"
print()
print_header("Hugging Face API Token")
pconfig = PROVIDER_REGISTRY["huggingface"]
print_info(f"Provider: {pconfig.name}")
print_info("Get your token at: https://huggingface.co/settings/tokens")
print_info("Required permission: 'Make calls to Inference Providers'")
print()

api_key = prompt_fn(
" HF_TOKEN: ",
is_password=True,
).strip()
if api_key:
save_env_value("HF_TOKEN", api_key)
# Clear OpenRouter env vars to prevent routing confusion
save_env_value("OPENAI_BASE_URL", "")
save_env_value("OPENAI_API_KEY", "")
_set_model_provider(config, "huggingface", pconfig.inference_base_url)
selected_base_url = pconfig.inference_base_url

# else: provider_idx == 15 (Keep current) — only shown when a provider already exists
# Normalize "keep current" to an explicit provider so downstream logic
# doesn't fall back to the generic OpenRouter/static-model path.
if selected_provider is None:
Expand Down
1 change: 1 addition & 0 deletions website/docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ hermes setup # Or configure everything at once
| **MiniMax** | International MiniMax endpoint | Set `MINIMAX_API_KEY` |
| **MiniMax China** | China-region MiniMax endpoint | Set `MINIMAX_CN_API_KEY` |
| **Alibaba Cloud** | Qwen models via DashScope | Set `DASHSCOPE_API_KEY` |
| **Hugging Face** | 20+ open models via unified router (Qwen, DeepSeek, Kimi, etc.) | Set `HF_TOKEN` |
| **Kilo Code** | KiloCode-hosted models | Set `KILOCODE_API_KEY` |
| **Vercel AI Gateway** | Vercel AI Gateway routing | Set `AI_GATEWAY_API_KEY` |
| **Custom Endpoint** | VLLM, SGLang, or any OpenAI-compatible API | Set base URL + API key |
Expand Down
4 changes: 3 additions & 1 deletion website/docs/reference/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ All variables go in `~/.hermes/.env`. You can also set them with `hermes config
| `MINIMAX_CN_BASE_URL` | Override MiniMax China base URL (default: `https://api.minimaxi.com/v1`) |
| `KILOCODE_API_KEY` | Kilo Code API key ([kilo.ai](https://kilo.ai)) |
| `KILOCODE_BASE_URL` | Override Kilo Code base URL (default: `https://api.kilo.ai/api/gateway`) |
| `HF_TOKEN` | Hugging Face token for Inference Providers ([huggingface.co/settings/tokens](https://huggingface.co/settings/tokens)) |
| `HF_BASE_URL` | Override Hugging Face base URL (default: `https://router.huggingface.co/v1`) |
| `ANTHROPIC_API_KEY` | Anthropic Console API key ([console.anthropic.com](https://console.anthropic.com/)) |
| `ANTHROPIC_TOKEN` | Manual or legacy Anthropic OAuth/setup-token override |
| `DASHSCOPE_API_KEY` | Alibaba Cloud DashScope API key for Qwen models ([modelstudio.console.alibabacloud.com](https://modelstudio.console.alibabacloud.com/)) |
Expand All @@ -48,7 +50,7 @@ For native Anthropic auth, Hermes prefers Claude Code's own credential files whe

| Variable | Description |
|----------|-------------|
| `HERMES_INFERENCE_PROVIDER` | Override provider selection: `auto`, `openrouter`, `nous`, `openai-codex`, `anthropic`, `zai`, `kimi-coding`, `minimax`, `minimax-cn`, `kilocode`, `alibaba` (default: `auto`) |
| `HERMES_INFERENCE_PROVIDER` | Override provider selection: `auto`, `openrouter`, `nous`, `openai-codex`, `anthropic`, `huggingface`, `zai`, `kimi-coding`, `minimax`, `minimax-cn`, `kilocode`, `alibaba` (default: `auto`) |
| `HERMES_PORTAL_BASE_URL` | Override Nous Portal URL (for development/testing) |
| `NOUS_INFERENCE_BASE_URL` | Override Nous inference API URL |
| `HERMES_NOUS_MIN_KEY_TTL_SECONDS` | Min agent key TTL before re-mint (default: 1800 = 30min) |
Expand Down
30 changes: 28 additions & 2 deletions website/docs/user-guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ You need at least one way to connect to an LLM. Use `hermes model` to switch pro
| **MiniMax China** | `MINIMAX_CN_API_KEY` in `~/.hermes/.env` (provider: `minimax-cn`) |
| **Alibaba Cloud** | `DASHSCOPE_API_KEY` in `~/.hermes/.env` (provider: `alibaba`, aliases: `dashscope`, `qwen`) |
| **Kilo Code** | `KILOCODE_API_KEY` in `~/.hermes/.env` (provider: `kilocode`) |
| **Alibaba Cloud** | `DASHSCOPE_API_KEY` in `~/.hermes/.env` (provider: `alibaba`) |
| **Hugging Face** | `HF_TOKEN` in `~/.hermes/.env` (provider: `huggingface`, aliases: `hf`) |
| **Custom Endpoint** | `hermes model` (saved in `config.yaml`) or `OPENAI_BASE_URL` + `OPENAI_API_KEY` in `~/.hermes/.env` |

:::info Codex Note
Expand Down Expand Up @@ -152,6 +152,32 @@ model:

Base URLs can be overridden with `GLM_BASE_URL`, `KIMI_BASE_URL`, `MINIMAX_BASE_URL`, `MINIMAX_CN_BASE_URL`, or `DASHSCOPE_BASE_URL` environment variables.

### Hugging Face Inference Providers

[Hugging Face Inference Providers](https://huggingface.co/docs/inference-providers) routes to 20+ open models through a unified OpenAI-compatible endpoint (`router.huggingface.co/v1`). Requests are automatically routed to the fastest available backend (Groq, Together, SambaNova, etc.) with automatic failover.

```bash
# Use any available model
hermes chat --provider huggingface --model Qwen/Qwen3-235B-A22B-Thinking-2507
# Requires: HF_TOKEN in ~/.hermes/.env

# Short alias
hermes chat --provider hf --model deepseek-ai/DeepSeek-V3.2
```

Or set it permanently in `config.yaml`:
```yaml
model:
provider: "huggingface"
default: "Qwen/Qwen3-235B-A22B-Thinking-2507"
```

Get your token at [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) — make sure to enable the "Make calls to Inference Providers" permission. Free tier included ($0.10/month credit, no markup on provider rates).

You can append routing suffixes to model names: `:fastest` (default), `:cheapest`, or `:provider_name` to force a specific backend.

The base URL can be overridden with `HF_BASE_URL`.

## Custom & Self-Hosted LLM Providers

Hermes Agent works with **any OpenAI-compatible API endpoint**. If a server implements `/v1/chat/completions`, you can point Hermes at it. This means you can use local models, GPU inference servers, multi-provider routers, or any third-party API.
Expand Down Expand Up @@ -443,7 +469,7 @@ fallback_model:

When activated, the fallback swaps the model and provider mid-session without losing your conversation. It fires **at most once** per session.

Supported providers: `openrouter`, `nous`, `openai-codex`, `anthropic`, `zai`, `kimi-coding`, `minimax`, `minimax-cn`, `custom`.
Supported providers: `openrouter`, `nous`, `openai-codex`, `anthropic`, `huggingface`, `zai`, `kimi-coding`, `minimax`, `minimax-cn`, `custom`.

:::tip
Fallback is configured exclusively through `config.yaml` — there are no environment variables for it. For full details on when it triggers, supported providers, and how it interacts with auxiliary tasks and delegation, see [Fallback Providers](/docs/user-guide/features/fallback-providers).
Expand Down
3 changes: 2 additions & 1 deletion website/docs/user-guide/features/fallback-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Both `provider` and `model` are **required**. If either is missing, the fallback
| MiniMax | `minimax` | `MINIMAX_API_KEY` |
| MiniMax (China) | `minimax-cn` | `MINIMAX_CN_API_KEY` |
| Kilo Code | `kilocode` | `KILOCODE_API_KEY` |
| Hugging Face | `huggingface` | `HF_TOKEN` |
| Custom endpoint | `custom` | `base_url` + `api_key_env` (see below) |

### Custom Endpoint Fallback
Expand Down Expand Up @@ -161,7 +162,7 @@ When a task's provider is set to `"auto"` (the default), Hermes tries providers

```text
OpenRouter → Nous Portal → Custom endpoint → Codex OAuth →
API-key providers (z.ai, Kimi, MiniMax, Anthropic) → give up
API-key providers (z.ai, Kimi, MiniMax, Hugging Face, Anthropic) → give up
```

**For vision tasks:**
Expand Down