mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-10-20 05:15:31 +00:00
feat: add Hugging Face as a first-class inference provider
Register Hugging Face Inference Providers (router.huggingface.co/v1) as a named provider alongside existing ones. Users can now: - hermes chat --provider huggingface - Use hf:model-name syntax (e.g. hf:Qwen/Qwen3-235B-A22B-Thinking-2507) - Set HF_TOKEN in ~/.hermes/.env - Select from 18 curated models via hermes model picker OpenAI-compatible endpoint with automatic failover across providers (Groq, Together, SambaNova, etc.), free tier included. Files changed: - hermes_cli/auth.py: ProviderConfig + aliases (hf, hugging-face, huggingface-hub) - hermes_cli/models.py: _PROVIDER_MODELS, _PROVIDER_LABELS, _PROVIDER_ALIASES, _PROVIDER_ORDER - hermes_cli/main.py: provider_labels, providers list, --provider choices, dispatch - hermes_cli/setup.py: provider_choices, setup flow with token prompt - hermes_cli/config.py: HF_TOKEN + HF_BASE_URL in OPTIONAL_ENV_VARS - agent/model_metadata.py: context window entries for all curated HF models - .env.example: HF_TOKEN documentation Based on PR #1171 by @davanstrien. Salvaged onto current main with additional completeness: setup.py flow, config.py env vars, auth.py aliases, model_metadata context windows, .env.example.
This commit is contained in:
parent
aea39eeafb
commit
154fd88b76
7 changed files with 133 additions and 4 deletions
|
|
@ -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
|
||||
|
||||
# =============================================================================
|
||||
|
|
|
|||
|
|
@ -119,6 +119,25 @@ DEFAULT_CONTEXT_LENGTHS = {
|
|||
"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,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -195,6 +195,14 @@ PROVIDER_REGISTRY: Dict[str, 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",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -549,6 +549,21 @@ OPTIONAL_ENV_VARS = {
|
|||
"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": {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
@ -1444,6 +1446,27 @@ _PROVIDER_MODELS = {
|
|||
"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",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2642,7 +2665,7 @@ For more help on a command:
|
|||
)
|
||||
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)"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -155,6 +155,28 @@ _PROVIDER_MODELS: dict[str, list[str]] = {
|
|||
"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 = {
|
||||
|
|
@ -172,6 +194,7 @@ _PROVIDER_LABELS = {
|
|||
"ai-gateway": "AI Gateway",
|
||||
"kilocode": "Kilo Code",
|
||||
"alibaba": "Alibaba Cloud (DashScope)",
|
||||
"huggingface": "Hugging Face",
|
||||
"custom": "Custom endpoint",
|
||||
}
|
||||
|
||||
|
|
@ -201,6 +224,9 @@ _PROVIDER_ALIASES = {
|
|||
"aliyun": "alibaba",
|
||||
"qwen": "alibaba",
|
||||
"alibaba-cloud": "alibaba",
|
||||
"hf": "huggingface",
|
||||
"hugging-face": "huggingface",
|
||||
"huggingface-hub": "huggingface",
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@ _DEFAULT_PROVIDER_MODELS = {
|
|||
"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",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue