← Về thư mục
📄 / / usr / local / lib / hermes-agent / website / docs / developer-guide / provider-runtime.md

sidebar_position: 4 title: "Provider Runtime Resolution" description: "How Hermes resolves providers, credentials, API modes, and auxiliary models at runtime"


Provider Runtime Resolution

Hermes has a shared provider runtime resolver used across:

Primary implementation:

get_provider_profile() in providers/ returns a ProviderProfile for a given provider id. runtime_provider.py calls this at resolution time to get the canonical base_url, env_vars priority list, api_mode, and fallback_models without needing to duplicate that data in multiple files. Adding a new plugin under plugins/model-providers/<your-provider>/ (or $HERMES_HOME/plugins/model-providers/<your-provider>/) that calls register_provider() is enough for runtime_provider.py to pick it up — no branch needed in the resolver itself.

If you are trying to add a new first-class inference provider, read Adding Providers and the Model Provider Plugin guide alongside this page.

Resolution precedence

At a high level, provider resolution uses:

  1. explicit CLI/runtime request
  2. config.yaml model/provider config
  3. environment variables
  4. provider-specific defaults or auto resolution

That ordering matters because Hermes treats the saved model/provider choice as the source of truth for normal runs. This prevents a stale shell export from silently overriding the endpoint a user last selected in hermes model.

Providers

Current provider families include (see plugins/model-providers/ for the complete bundled set):

Output of runtime resolution

The runtime resolver returns data such as:

Why this matters

This resolver is the main reason Hermes can share auth/runtime logic between:

AI Gateway

Set AI_GATEWAY_API_KEY in ~/.hermes/.env and run with --provider ai-gateway. Hermes fetches available models from the gateway's /models endpoint, filtering to language models with tool-use support.

OpenRouter, AI Gateway, and custom OpenAI-compatible base URLs

Hermes contains logic to avoid leaking the wrong API key to a custom endpoint when multiple provider keys exist (e.g. OPENROUTER_API_KEY, AI_GATEWAY_API_KEY, and OPENAI_API_KEY).

Each provider's API key is scoped to its own base URL:

Hermes also distinguishes between:

That distinction is especially important for:

Native Anthropic path

Anthropic is not just "via OpenRouter" anymore.

When provider resolution selects anthropic, Hermes uses:

Credential resolution for native Anthropic now prefers refreshable Claude Code credentials over copied env tokens when both are present. In practice that means:

OpenAI Codex path

Codex uses a separate Responses API path:

Auxiliary model routing

Auxiliary tasks such as:

can use their own provider/model routing rather than the main conversational model.

When an auxiliary task is configured with provider main, Hermes resolves that through the same shared runtime path as normal chat. In practice that means:

Fallback models

Hermes supports a configured fallback provider chain — a list of (provider, model) entries tried in order when the primary model encounters errors. The legacy single-pair fallback_model dict is still accepted for back-compat (and migrated on first write).

How it works internally

  1. Storage: AIAgent.__init__ stores the fallback_model dict and sets _fallback_activated = False.

  2. Trigger points: _try_activate_fallback() is called from three places in the main retry loop in run_agent.py:

  3. After max retries on invalid API responses (None choices, missing content)
  4. On non-retryable client errors (HTTP 401, 403, 404)
  5. After max retries on transient errors (HTTP 429, 500, 502, 503)

  6. Activation flow (_try_activate_fallback):

  7. Returns False immediately if already activated or not configured
  8. Calls resolve_provider_client() from auxiliary_client.py to build a new client with proper auth
  9. Determines api_mode: codex_responses for openai-codex, anthropic_messages for anthropic, chat_completions for everything else
  10. Swaps in-place: self.model, self.provider, self.base_url, self.api_mode, self.client, self._client_kwargs
  11. For anthropic fallback: builds a native Anthropic client instead of OpenAI-compatible
  12. Re-evaluates prompt caching (enabled for Claude models on OpenRouter)
  13. Sets _fallback_activated = True — prevents firing again
  14. Resets retry count to 0 and continues the loop

  15. Config flow:

  16. CLI: reads the fallback chain via hermes_cli/fallback_config.get_fallback_chain() → passes to AIAgent(fallback_model=...)
  17. Gateway: gateway/run.py._load_fallback_model() reads config.yaml → passes to AIAgent
  18. Validation: both provider and model keys must be non-empty, or fallback is disabled

What does NOT support fallback

Cron jobs do support fallback: run_job() reads fallback_providers (or legacy fallback_model) from config.yaml and passes it to AIAgent(fallback_model=...), matching the gateway's _load_fallback_model() pattern. See Cron Internals.

Test coverage

Fallback behavior is exercised across several suites: