website/docs/developer-guide/adding-providers.md
Hermes can already talk to any OpenAI-compatible endpoint through the custom provider path. Do not add a built-in provider unless you want first-class UX for that service:
hermes model menu entriesprovider:model syntaxIf the provider is just "another OpenAI-compatible base URL and API key", a named custom provider may be enough.
A built-in provider has to line up across a few layers:
hermes_cli/auth.py decides how credentials are found.hermes_cli/runtime_provider.py turns that into runtime data:
providerapi_modebase_urlapi_keysourcerun_agent.py uses api_mode to decide how requests are built and sent.hermes_cli/models.py and hermes_cli/main.py make the provider show up in the CLI. (hermes_cli/setup.py delegates to main.py automatically — no changes needed there.)agent/auxiliary_client.py and agent/model_metadata.py keep side tasks and token budgeting working.The important abstraction is api_mode.
chat_completions.codex_responses.anthropic_messages.api_mode branch.Use this when the provider accepts standard chat-completions style requests.
Typical work:
You usually do not need a new adapter or a new api_mode.
Use this when the provider does not behave like OpenAI chat completions.
Examples in-tree today:
codex_responsesanthropic_messagesThis path includes everything from Path A plus:
agent/run_agent.py branches for request building, dispatch, usage extraction, interrupt handling, and response normalizationhermes_cli/auth.pyhermes_cli/models.pyhermes_cli/runtime_provider.pyhermes_cli/main.pyagent/auxiliary_client.pyagent/model_metadata.pywebsite/docs/:::tip
hermes_cli/setup.py does not need changes. The setup wizard delegates provider/model selection to select_provider_and_model() in main.py — any provider added there is automatically available in hermes setup.
:::
agent/<provider>_adapter.pyrun_agent.pypyproject.toml if a provider SDK is requiredIf your provider is just an OpenAI-compatible endpoint that authenticates with a single API key, you do not need to touch auth.py, runtime_provider.py, main.py, or any of the other files in the full checklist below.
All you need is:
plugins/model-providers/<your-provider>/ containing:
__init__.py — calls register_provider(profile) at module-levelplugin.yaml — manifest (name, kind: model-provider, version, description)get_provider_profile() or list_providers() — bundled plugins (this repo) and user plugins at $HERMES_HOME/plugins/model-providers/ both get picked up.When you add a plugin and it calls register_provider(), the following wire up automatically:
PROVIDER_REGISTRY entry in auth.py (credential resolution, env-var lookup)api_mode set to chat_completionsbase_url sourced from the config or the declared env varenv_vars checked in priority order for the API keyfallback_models list registered for the provider--provider CLI flag accepts the provider idhermes model menu includes the providerhermes setup wizard delegates to main.py automaticallyprovider:model alias syntax worksbase_url and api_key--provider <name> CLI flag accepts the provider idUser plugins at $HERMES_HOME/plugins/model-providers/<name>/ override bundled plugins of the same name (last-writer-wins in register_provider()) — so third parties can monkey-patch or replace any built-in profile without editing the repo.
See plugins/model-providers/nvidia/ or plugins/model-providers/gmi/ as a template, and the full Model Provider Plugin guide for field reference, hook idioms, and end-to-end examples.
Use the full checklist below when your provider needs any of the following:
/models fetchhermes model menu entries with bespoke auth flowsChoose a single provider id and use it everywhere.
Examples from the repo:
openai-codexkimi-codingminimax-cnThat same id should appear in:
PROVIDER_REGISTRY in hermes_cli/auth.py_PROVIDER_LABELS in hermes_cli/models.py_PROVIDER_ALIASES in both hermes_cli/auth.py and hermes_cli/models.py--provider choices in hermes_cli/main.pyIf the id differs between those files, the provider will feel half-wired: auth may work while /model, setup, or runtime resolution silently misses it.
hermes_cli/auth.pyFor API-key providers, add a ProviderConfig entry to PROVIDER_REGISTRY with:
idnameauth_type="api_key"inference_base_urlapi_key_env_varsbase_url_env_varAlso add aliases to _PROVIDER_ALIASES.
Use the existing providers as templates:
Questions to answer here:
If the provider needs something more than "look up an API key", add a dedicated credential resolver instead of shoving logic into unrelated branches.
hermes_cli/models.pyUpdate the provider catalog so the provider works in menus and in provider:model syntax.
Typical edits:
_PROVIDER_MODELS_PROVIDER_LABELS_PROVIDER_ALIASESlist_available_providers()provider_model_ids() if the provider supports a live /models fetchIf the provider exposes a live model list, prefer that first and keep _PROVIDER_MODELS as the static fallback.
This file is also what makes inputs like these work:
anthropic:claude-sonnet-4-6
kimi:model-name
If aliases are missing here, the provider may authenticate correctly but still fail in /model parsing.
hermes_cli/runtime_provider.pyresolve_runtime_provider() is the shared path used by CLI, gateway, cron, ACP, and helper clients.
Add a branch that returns a dict with at least:
{
"provider": "your-provider",
"api_mode": "chat_completions", # or your native mode
"base_url": "https://...",
"api_key": "...",
"source": "env|portal|auth-store|explicit",
"requested_provider": requested_provider,
}
If the provider is OpenAI-compatible, api_mode should usually stay chat_completions.
Be careful with API-key precedence. Hermes already contains logic to avoid leaking an OpenRouter key to unrelated endpoints. A new provider should be equally explicit about which key goes to which base URL.
hermes_cli/main.pyA provider is not discoverable until it shows up in the interactive hermes model flow.
Update these in hermes_cli/main.py:
provider_labels dictproviders list in select_provider_and_model()if selected_provider == ...)--provider argument choices_model_flow_<provider>() function, or reuse _model_flow_api_key_provider() if it fits:::tip
hermes_cli/setup.py does not need changes — it calls select_provider_and_model() from main.py, so your new provider appears in both hermes model and hermes setup automatically.
:::
Two files matter here:
agent/auxiliary_client.pyAdd a cheap / fast default aux model to _API_KEY_PROVIDER_AUX_MODELS if this is a direct API-key provider.
Auxiliary tasks include things like:
If the provider has no sensible aux default, side tasks may fall back badly or use an expensive main model unexpectedly.
agent/model_metadata.pyAdd context lengths for the provider's models so token budgeting, compression thresholds, and limits stay sane.
run_agent.py supportIf the provider is not plain chat completions, isolate the provider-specific logic in agent/<provider>_adapter.py.
Keep run_agent.py focused on orchestration. It should call adapter helpers, not hand-build provider payloads inline all over the file.
A native provider usually needs work in these places:
Typical responsibilities:
run_agent.py expectsrun_agent.pySearch for api_mode and audit every switch point. At minimum, verify:
__init__ chooses the new api_mode_build_api_kwargs() knows how to format requests_interruptible_api_call() dispatches to the right client callAlso search run_agent.py for self.client.. Any code path that assumes the standard OpenAI client exists can break when a native provider uses a different client object or self.client = None.
Prompt caching and provider-specific knobs are easy to regress.
Examples already in-tree:
When you add a native provider, double-check that Hermes is only sending fields that provider actually understands.
At minimum, touch the tests that guard provider wiring.
Common places:
tests/hermes_cli/test_runtime_provider_resolution.pytests/cli/test_cli_provider_resolution.pytests/hermes_cli/test_model_switch_custom_providers.py (and adjacent tests/hermes_cli/test_model_switch_*.py)tests/hermes_cli/test_setup_model_provider.pytests/run_agent/test_provider_parity.pytests/run_agent/test_run_agent.pytests/test_<provider>_adapter.py for a native providerFor docs-only examples, the exact file set may differ. The point is to cover:
Run tests with xdist disabled:
source venv/bin/activate
python -m pytest tests/hermes_cli/test_runtime_provider_resolution.py tests/cli/test_cli_provider_resolution.py tests/hermes_cli/test_setup_model_provider.py tests/run_agent/test_provider_parity.py -n0 -q
For deeper changes, run the full suite before pushing:
source venv/bin/activate
python -m pytest tests/ -n0 -q
After tests, run a real smoke test.
source venv/bin/activate
python -m hermes_cli.main chat -q "Say hello" --provider your-provider --model your-model
Also test the interactive flows if you changed menus:
source venv/bin/activate
python -m hermes_cli.main model
python -m hermes_cli.main setup
For native providers, verify at least one tool call too, not just a plain text response.
If the provider is meant to ship as a first-class option, update the user docs too:
website/docs/getting-started/quickstart.mdwebsite/docs/user-guide/configuration.mdwebsite/docs/reference/environment-variables.mdA developer can wire the provider perfectly and still leave users unable to discover the required env vars or setup flow.
Use this if the provider is standard chat completions.
ProviderConfig added in hermes_cli/auth.pyhermes_cli/auth.py and hermes_cli/models.pyhermes_cli/models.pyhermes_cli/runtime_provider.pyhermes_cli/main.py (setup.py inherits automatically)agent/auxiliary_client.pyagent/model_metadata.pyUse this when the provider needs a new protocol path.
agent/<provider>_adapter.pyapi_mode supported in run_agent.pyThat makes credentials resolve correctly while /model and provider:model inputs fail.
config["model"] can be a string or a dictA lot of provider-selection code has to normalize both forms.
If the service is just OpenAI-compatible, a custom provider may already solve the user problem with less maintenance.
The main chat path can work while summarization, memory flushes, or vision helpers fail because aux routing was never updated.
run_agent.pySearch for api_mode and self.client.. Do not assume the obvious request path is the only one.
Fields like provider routing belong only on the providers that support them.
hermes model but not hermes setupBoth flows need to know about the provider.
If you are hunting for all the places a provider touches, search these symbols:
PROVIDER_REGISTRY_PROVIDER_ALIASES_PROVIDER_MODELSresolve_runtime_provider_model_flow_select_provider_and_modelapi_mode_API_KEY_PROVIDER_AUX_MODELSself.client.