#!/usr/bin/env bash # VENDORED COPY — DO NOT EDIT THIS FILE BY HAND. # # Source of truth: # git.moleculesai.app/molecule-ai/molecule-ai-workspace-template-hermes # scripts/derive-provider.sh # # This snapshot is read by derive_provider_drift_test.go so the AST # drift gate stays hermetic (no network, no submodule, no path-walk). # When upstream changes, refresh via: # # cp ~/path/to/molecule-ai-workspace-template-hermes/scripts/derive-provider.sh \ # workspace-server/internal/handlers/testdata/derive-provider.sh # # (and re-add the VENDORED COPY header below.) The drift test will # fail loudly if upstream adds prefixes that deriveProviderFromModelSlug # doesn't handle — fix it by adding the missing case to the Go function, # not by silently widening acceptedDivergences. # # derive-provider.sh — map a hermes-agent model slug to its provider # name. Sourced by both install.sh (SaaS bare-host path) and start.sh # (Docker path) so the two entry-points stay consistent. # # Contract: # Reads: $HERMES_INFERENCE_PROVIDER (if already set, we respect it) # $HERMES_INFERENCE_MODEL (preferred — matches upstream env name) # $HERMES_DEFAULT_MODEL (legacy fallback — name we invented before # 2026-05; workspace-server still writes # it during the migration window) # $HERMES_API_KEY / $NOUS_API_KEY (affect the nousresearch/* branch) # Writes: $PROVIDER — the derived provider name, or "auto" if unknown. # # Upstream's actual env var is $HERMES_INFERENCE_MODEL (see # website/docs/reference/environment-variables.md in NousResearch/hermes-agent). # We accept both for one release cycle so workspaces booting under the legacy # control-plane don't break — drop $HERMES_DEFAULT_MODEL once workspace-server # is updated to write the upstream name. # # Why the per-template sub-script (vs doing this in CP): every runtime # has its own provider taxonomy. Keeping the logic inside the template # repo means CP stays runtime-agnostic and adding a new runtime with # different provider semantics doesn't require a CP edit. # # Hermes-specific quirks encoded here: # - `openai/...` routes through `openrouter` (hermes has no direct # openai provider; openai-codex is OAuth-only for Codex models) # - `nousresearch/...` prefers direct `nous` if HERMES_API_KEY is # set, else falls back to `openrouter` (which also serves Hermes 3) # - chinese-region variants (minimax-cn, kimi-coding-cn) keep their # full prefix as the provider name # # See molecule-controlplane/docs/canary-tenants.md and the hermes-agent # providers.md docs for the full taxonomy. # Honour an explicit override. if [ -n "${HERMES_INFERENCE_PROVIDER:-}" ]; then PROVIDER="${HERMES_INFERENCE_PROVIDER}" return 0 2>/dev/null || exit 0 fi # Resolve the model slug — prefer the upstream env name, fall back to legacy. _HERMES_MODEL="${HERMES_INFERENCE_MODEL:-${HERMES_DEFAULT_MODEL:-}}" if [ -z "${_HERMES_MODEL}" ]; then PROVIDER="auto" return 0 2>/dev/null || exit 0 fi case "${_HERMES_MODEL}" in # Keep full CN-suffix as provider so chinese-region keys route right minimax-cn/*) PROVIDER="minimax-cn" ;; kimi-coding-cn/*) PROVIDER="kimi-coding-cn" ;; # Direct-SDK providers (clean 1:1 prefix→provider mapping) minimax/*) PROVIDER="minimax" ;; anthropic/*) PROVIDER="anthropic" ;; gemini/*) PROVIDER="gemini" ;; deepseek/*) PROVIDER="deepseek" ;; zai/*) PROVIDER="zai" ;; kimi-coding/*) PROVIDER="kimi-coding" ;; alibaba/*|dashscope/*|qwen/*) PROVIDER="alibaba" ;; xiaomi/*|mimo/*) PROVIDER="xiaomi" ;; arcee/*|arcee-ai/*) PROVIDER="arcee" ;; nvidia/*|nim/*) PROVIDER="nvidia" ;; ollama-cloud/*) PROVIDER="ollama-cloud" ;; huggingface/*|hf/*) PROVIDER="huggingface" ;; ai-gateway/*|aigateway/*) PROVIDER="ai-gateway" ;; kilocode/*) PROVIDER="kilocode" ;; opencode-zen/*) PROVIDER="opencode-zen" ;; opencode-go/*) PROVIDER="opencode-go" ;; # Hermes-specific routing quirks. `openai/*` has two valid targets: # 1. hermes's "custom" provider pointed at api.openai.com — requires # OPENAI_API_KEY. install.sh sees this case and auto-populates # HERMES_CUSTOM_{BASE_URL,API_KEY} so the direct-OpenAI path works # without the user having to set HERMES_CUSTOM_* explicitly. # 2. OpenRouter (hermes's built-in path — requires OPENROUTER_API_KEY). # # Priority: prefer **custom** (direct OpenAI) when OPENAI_API_KEY is set. # The operator supplying OPENAI_API_KEY for an openai/* model is an # explicit intent signal to hit OpenAI directly. The previous "prefer # OR if any OR key exists" rule silently hijacked that intent whenever # a tenant-global OPENROUTER_API_KEY was present (even if stale/empty # enough to 401), which is exactly what bit the 2026-04-23 E2E (surfaced # as OpenRouter's `401 Missing Authentication header` in the agent reply). # # To explicitly route openai/* through OR, set HERMES_INFERENCE_PROVIDER=openrouter # (handled at the top of this file) or use an openrouter/* model slug. openai/*) if [ -n "${OPENAI_API_KEY:-}" ]; then PROVIDER="custom" elif [ -n "${OPENROUTER_API_KEY:-}" ]; then PROVIDER="openrouter" else PROVIDER="openrouter" # no-key fallback — hermes will error clearly fi ;; nousresearch/*) # Prefer direct Nous Portal if Nous credentials present, else OR. if [ -n "${HERMES_API_KEY:-}" ] || [ -n "${NOUS_API_KEY:-}" ]; then PROVIDER="nous" else PROVIDER="openrouter" fi ;; # Explicit catch-alls openrouter/*) PROVIDER="openrouter" ;; custom/*) PROVIDER="custom" ;; # Additional 1:1 prefix→provider mappings — kept aligned with upstream's # HERMES_INFERENCE_PROVIDER list (website/docs/reference/environment-variables.md # in NousResearch/hermes-agent, v0.12.0 / 2026-04-30). Place these BEFORE the # catch-all so they win. xai/*|grok/*) PROVIDER="xai" ;; bedrock/*|aws/*) PROVIDER="bedrock" ;; tencent/*|tencent-tokenhub/*) PROVIDER="tencent-tokenhub" ;; gmi/*) PROVIDER="gmi" ;; qwen-oauth/*) PROVIDER="qwen-oauth" ;; lmstudio/*|lm-studio/*) PROVIDER="lmstudio" ;; minimax-oauth/*) PROVIDER="minimax-oauth" ;; alibaba-coding-plan/*) PROVIDER="alibaba-coding-plan" ;; google-gemini-cli/*) PROVIDER="google-gemini-cli" ;; openai-codex/*) PROVIDER="openai-codex" ;; copilot-acp/*) PROVIDER="copilot-acp" ;; copilot/*) PROVIDER="copilot" ;; # Unknown prefix → let hermes auto-detect *) PROVIDER="auto" ;; esac