You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Cycle 2 of the provider infrastructure refactor. Cycle 1 (#13473, PRs 1-7 merged) extracted format conversion into agent/transports/. This cycle consolidates per-provider quirks from 5+ files into single-file provider modules.
Problem: Adding or modifying a provider touches auth.py + runtime_provider.py + models.py + auxiliary_client.py + run_agent.py + transport. Kimi and Copilot each touch 7 files despite having ~130-200 lines of quirk code. ChatCompletionsTransport takes 20+ boolean flag params because every provider's quirks are passed individually.
Solution:providers/<name>.py modules — each declares auth, endpoints, headers, temperature, max_tokens, message preprocessing, and extra_body in one place. Transport receives a provider object instead of flags.
Workstreams
WS1: Transport Cleanup (from Cycle 1 gaps)
Prerequisite work before provider modules can absorb the adapters.
Beyond the original WS2/WS3 scope, PR #14424 also delivered full auto-wiring
infrastructure — adding providers/<name>.py now zero-touches every integration
point:
Integration point
File
Mechanism
PROVIDER_REGISTRY
hermes_cli/auth.py
Loop over list_providers() at import; skips names already declared
CANONICAL_PROVIDERS
hermes_cli/models.py
Appends ProviderEntry for api_key providers at import
--provider CLI choices
hermes_cli/main.py
_build_provider_choices() derives from CANONICAL_PROVIDERS
api_key provider catch-all
hermes_cli/main.py
_is_profile_api_key_provider() routes new providers to model flow without new elif
provider_model_ids()
hermes_cli/models.py
Calls profile.fetch_models() then profile.fallback_models for api_key profiles
Doctor health checks
hermes_cli/doctor.py
Auto-appends to _apikey_providers_static at runtime
OPTIONAL_ENV_VARS
hermes_cli/config.py
_inject_profile_env_vars() populates from env_vars at import
_URL_TO_PROVIDER domain map
agent/model_metadata.py
profile.get_hostname() auto-derived from base_url
Transport kwargs
agent/transports/chat_completions.py
_build_kwargs_from_profile() — hooks handle all quirks
api_mode resolution
hermes_cli/runtime_provider.py
profile.api_mode read directly
Aux model selection
agent/auxiliary_client.py
profile.default_aux_model read first
Request routing
run_agent.py
get_provider_profile() — all 30 registered providers go through profile path
New profile fields added beyond the original spec:
display_name — human label for picker / setup wizard
description — picker subtitle
signup_url — shown during first-run setup
fallback_models — curated list for model picker when live fetch fails
hostname — base hostname for URL→provider reverse-mapping (auto-derived from base_url)
default_aux_model — cheap model for auxiliary tasks
Cycle 2 builds on top: provider modules feed INTO transports
Open items
auth.py PROVIDER_REGISTRY full replacement — complex OAuth flows
(Anthropic, Nous device-code, Copilot, OpenAI Codex, Bedrock) are still handled
by bespoke code in hermes_cli/auth.py. Auto-extend covers all simple api_key
providers, but OAuth flows need their own migration work.
Docs — website/docs/developer-guide/adding-providers.md exists but
needs a full pass; integration docs on the new auto-wiring contract.
Overview
Cycle 2 of the provider infrastructure refactor. Cycle 1 (#13473, PRs 1-7 merged) extracted format conversion into
agent/transports/. This cycle consolidates per-provider quirks from 5+ files into single-file provider modules.Problem: Adding or modifying a provider touches auth.py + runtime_provider.py + models.py + auxiliary_client.py + run_agent.py + transport. Kimi and Copilot each touch 7 files despite having ~130-200 lines of quirk code. ChatCompletionsTransport takes 20+ boolean flag params because every provider's quirks are passed individually.
Solution:
providers/<name>.pymodules — each declares auth, endpoints, headers, temperature, max_tokens, message preprocessing, and extra_body in one place. Transport receives a provider object instead of flags.Workstreams
WS1: Transport Cleanup (from Cycle 1 gaps)
Prerequisite work before provider modules can absorb the adapters.
NormalizedResponsedirectly (eliminates 2-layertransport → v1 → NR mappingchain) — PR refactor: complete WS1 transport cleanup (all 4 items) #14459auxiliary_client.pyAnthropic path (L575-612) to use transport instead of callingbuild_anthropic_kwargs/normalize_anthropic_responsedirectly — PR refactor: complete WS1 transport cleanup (all 4 items) #14459normalize_converse_response()— flush_memories guard changed to api_mode check — PR refactor: complete WS1 transport cleanup (all 4 items) #14459 (dispatch-site itself stays: validate_response reads response.choices)_nr_to_assistant_message()shim — ToolCall + NR have backward-compat properties — PR refactor: complete WS1 transport cleanup (all 4 items) #14459WS2: Provider Module ABC + Registry
Design the provider interface and migrate providers.
ProviderProfileABC inproviders/base.py— PR feat: provider modules — ProviderProfile ABC, 29 providers, fetch_models, transport single-path #14424providers/*.py) — PR feat: provider modules — ProviderProfile ABC, 29 providers, fetch_models, transport single-path #14424_build_kwargs_from_profile()single-path — PR feat: provider modules — ProviderProfile ABC, 29 providers, fetch_models, transport single-path #14424run_agent.py— all 30 providers live via profile path — PR feat: provider modules — ProviderProfile ABC, 29 providers, fetch_models, transport single-path #14424auth.pyPROVIDER_REGISTRYto auto-extend from provider modules — PR feat: provider modules — ProviderProfile ABC, 29 providers, fetch_models, transport single-path #14424runtime_provider.pyapi_mode resolution to readprofile.api_mode— PR feat: provider modules — ProviderProfile ABC, 29 providers, fetch_models, transport single-path #14424fetch_models(api_key, timeout)hook toProviderProfile— replaces ad-hoc fetch functions — PR feat: provider modules — ProviderProfile ABC, 29 providers, fetch_models, transport single-path #14424models_urlfield — explicit catalog URL separate frombase_url— PR feat: provider modules — ProviderProfile ABC, 29 providers, fetch_models, transport single-path #14424WS3: Provider Migrations (ordered by scatter/complexity)
Migrate actual providers, most-scattered first.
Additional work completed (PR #14424)
Beyond the original WS2/WS3 scope, PR #14424 also delivered full auto-wiring
infrastructure — adding
providers/<name>.pynow zero-touches every integrationpoint:
PROVIDER_REGISTRYhermes_cli/auth.pylist_providers()at import; skips names already declaredCANONICAL_PROVIDERShermes_cli/models.pyProviderEntryfor api_key providers at import--providerCLI choiceshermes_cli/main.py_build_provider_choices()derives fromCANONICAL_PROVIDERShermes_cli/main.py_is_profile_api_key_provider()routes new providers to model flow without new elifprovider_model_ids()hermes_cli/models.pyprofile.fetch_models()thenprofile.fallback_modelsfor api_key profileshermes_cli/doctor.py_apikey_providers_staticat runtimeOPTIONAL_ENV_VARShermes_cli/config.py_inject_profile_env_vars()populates fromenv_varsat import_URL_TO_PROVIDERdomain mapagent/model_metadata.pyprofile.get_hostname()auto-derived frombase_urlagent/transports/chat_completions.py_build_kwargs_from_profile()— hooks handle all quirksapi_moderesolutionhermes_cli/runtime_provider.pyprofile.api_moderead directlyagent/auxiliary_client.pyprofile.default_aux_modelread firstrun_agent.pyget_provider_profile()— all 30 registered providers go through profile pathNew profile fields added beyond the original spec:
display_name— human label for picker / setup wizarddescription— picker subtitlesignup_url— shown during first-run setupfallback_models— curated list for model picker when live fetch failshostname— base hostname for URL→provider reverse-mapping (auto-derived frombase_url)default_aux_model— cheap model for auxiliary tasksProvider Quirk Scatter (Current State)
What Stays on AIAgent (NOT moving to providers)
_build_assistant_messageRelationship to Cycle 1
agent/transports/with 4 transports, shared types, unified_get_transport()Open items
PROVIDER_REGISTRYfull replacement — complex OAuth flows(Anthropic, Nous device-code, Copilot, OpenAI Codex, Bedrock) are still handled
by bespoke code in
hermes_cli/auth.py. Auto-extend covers all simple api_keyproviders, but OAuth flows need their own migration work.
website/docs/developer-guide/adding-providers.mdexists butneeds a full pass; integration docs on the new auto-wiring contract.