Hermes 拥有一个共享的 provider 运行时解析器用于以下场景CLIgatewaycron 任务ACP辅助模型调用主要实现hermes_cli/runtime_provider.py— 凭据解析_resolve_custom_runtime()hermes_cli/auth.py— provider 注册表resolve_provider()hermes_cli/model_switch.py— 共享/model切换流水线CLI gatewayagent/auxiliary_client.py— 辅助模型路由providers/— ABC 注册表入口点ProviderProfile、register_provider、get_provider_profile、list_providersplugins/model-providers/name/— 每个 provider 的插件内置声明api_mode、base_url、env_vars、fallback_models并在首次访问时将自身注册到注册表。用户插件位于$HERMES_HOME/plugins/model-providers/name/会覆盖同名的内置插件。providers/中的get_provider_profile()为给定 provider id 返回一个ProviderProfile。runtime_provider.py在解析时调用它以获取规范的base_url、env_vars优先级列表、api_mode和fallback_models无需在多个文件中重复这些数据。在plugins/model-providers/your-provider/或$HERMES_HOME/plugins/model-providers/your-provider/下添加一个调用register_provider()的新插件即可让runtime_provider.py自动识别它——无需在解析器本身中添加分支。如果你想添加一个新的一等推理 provider请结合本页阅读 添加 Provider 和 Model Provider 插件指南。解析优先级从高层来看provider 解析使用以下顺序显式 CLI/运行时请求config.yaml中的模型/provider 配置环境变量provider 特定的默认值或自动解析该顺序很重要因为 Hermes 将已保存的模型/provider 选择视为正常运行的真实来源。这可以防止过时的 shell 导出变量悄悄覆盖用户在hermes model中最后选择的端点。Provider当前 provider 系列包括完整内置集合见plugins/model-providers/OpenRouterNous PortalOpenAI CodexCopilot / Copilot ACPAnthropic原生Google / Geminigemini、google-gemini-cliAlibaba / DashScopealibaba、alibaba-coding-planDeepSeekZ.AIKimi / Moonshotkimi-coding、kimi-coding-cnMiniMaxminimax、minimax-cn、minimax-oauthKilo CodeHugging FaceOpenCode Zen / OpenCode GoAWS BedrockAzure FoundryNVIDIA NIMxAIGrokArceeGMI CloudStepFunQwen OAuthXiaomiOllama CloudLM StudioTencent TokenHubCustomprovider: custom— 适用于任何 OpenAI 兼容端点的一等 provider命名自定义 providerconfig.yaml中的custom_providers列表运行时解析的输出运行时解析器返回的数据包括providerapi_modebase_urlapi_keysourceprovider 特定的元数据如过期/刷新信息为什么这很重要该解析器是 Hermes 能够在以下场景之间共享认证/运行时逻辑的主要原因hermes chatgateway 消息处理在全新会话中运行的 cron 任务ACP 编辑器会话辅助模型任务OpenRouter 与自定义 OpenAI 兼容 base URLHermes 包含相关逻辑以避免在存在多个 provider 密钥时例如同时存在OPENROUTER_API_KEY和OPENAI_API_KEY将错误的 API key 泄露给自定义端点。每个 provider 的 API key 仅作用于其自身的 base URLOPENROUTER_API_KEY仅发送至openrouter.ai端点OPENAI_API_KEY用于自定义端点及作为回退Hermes 还区分以下两种情况用户主动选择的真实自定义端点未配置自定义端点时使用的 OpenRouter 回退路径这种区分对以下场景尤为重要本地模型服务器非 OpenRouter 的 OpenAI 兼容 API无需重新运行 setup 即可切换 provider通过 config 保存的自定义端点即使当前 shell 中未导出OPENAI_BASE_URL也应正常工作原生 Anthropic 路径Anthropic 不再仅限于通过 OpenRouter访问。当 provider 解析选择anthropic时Hermes 使用api_mode anthropic_messages原生 Anthropic Messages APIagent/anthropic_adapter.py进行转换原生 Anthropic 的凭据解析现在在两者同时存在时优先使用可刷新的 Claude Code 凭据而非复制的环境变量 token。实际效果为包含可刷新认证的 Claude Code 凭据文件被视为首选来源手动设置的ANTHROPIC_TOKEN/CLAUDE_CODE_OAUTH_TOKEN值仍可作为显式覆盖Hermes 在调用原生 Messages API 前会预检 Anthropic 凭据刷新Hermes 在重建 Anthropic 客户端后仍会在收到 401 时重试一次作为回退路径OpenAI Codex 路径Codex 使用独立的 Responses API 路径api_mode codex_responses专用的凭据解析和认证存储支持辅助模型路由辅助任务包括视觉网页提取摘要上下文压缩摘要skills hub 操作MCP 辅助操作记忆刷新这些任务可以使用各自独立的 provider/模型路由而非主对话模型。当辅助任务配置的 provider 为main时Hermes 通过与普通对话相同的共享运行时路径进行解析。实际效果为环境变量驱动的自定义端点仍然有效通过hermes model/config.yaml保存的自定义端点同样有效辅助路由能够区分真实保存的自定义端点与 OpenRouter 回退回退模型Hermes 支持配置回退 provider 链——一个按顺序尝试的(provider, model)条目列表当主模型遇到错误时依次尝试。旧版单对fallback_model字典仍被接受以保持向后兼容并在首次写入时迁移。内部工作原理存储AIAgent.__init__存储fallback_model字典并将_fallback_activated设为False。触发点_try_activate_fallback()在run_agent.py主重试循环的三处被调用在无效 API 响应None choices、缺少 content达到最大重试次数后在不可重试的客户端错误HTTP 401、403、404时在瞬时错误HTTP 429、500、502、503达到最大重试次数后激活流程_try_activate_fallback若已激活或未配置立即返回False调用auxiliary_client.py中的resolve_provider_client()构建带有正确认证的新客户端确定api_modeopenai-codex 使用codex_responsesanthropic 使用anthropic_messages其余使用chat_completions原地替换self.model、self.provider、self.base_url、self.api_mode、self.client、self._client_kwargs对于 anthropic 回退构建原生 Anthropic 客户端而非 OpenAI 兼容客户端重新评估 prompt 缓存对 OpenRouter 上的 Claude 模型启用将_fallback_activated设为True——防止再次触发将重试计数重置为 0 并继续循环配置流程CLIcli.py读取CLI_CONFIG[fallback_model]→ 传递给AIAgent(fallback_model...)Gatewaygateway/run.py._load_fallback_model()读取config.yaml→ 传递给AIAgent验证provider和model键均须非空否则回退被禁用不支持回退的场景子代理委托tools/delegate_tool.py子代理继承父代理的 provider但不继承回退配置辅助任务使用各自独立的 provider 自动检测链见上方辅助模型路由Cron 任务支持回退run_job()从config.yaml读取fallback_providers或旧版fallback_model并传递给AIAgent(fallback_model...)与 gateway 的_load_fallback_model()模式一致。参见 Cron 内部机制。怎么添加一个模型插件Each subdirectory is a self-contained provider profile plugin. The directory layout mirrors plugins/platforms/: plugins/model-providers/ ├── openrouter/ │ ├── __init__.py # registers the ProviderProfile │ └── plugin.yaml # manifest: name, kind, version, description ├── anthropic/ │ ├── __init__.py │ └── plugin.yaml └── ... ## How discovery works providers/__init__.py._discover_providers() scans this directory (and $HERMES_HOME/plugins/model-providers/) the first time anything calls get_provider_profile() or list_providers(). Each __init__.py is imported and expected to call providers.register_provider(profile). User plugins at $HERMES_HOME/plugins/model-providers/name/ override bundled plugins of the same name — last-writer-wins in register_provider(). Drop a file there to replace a built-in. ## Adding a new provider 1. Create plugins/model-providers/your_provider/__init__.py: python from providers import register_provider from providers.base import ProviderProfile my_provider ProviderProfile( nameyour-provider, aliases(alias1, alias2), display_nameYour Provider, descriptionOne-line description shown in the setup picker, signup_urlhttps://your-provider.example.com/keys, env_vars(YOUR_PROVIDER_API_KEY, YOUR_PROVIDER_BASE_URL), base_urlhttps://api.your-provider.example.com/v1, default_aux_modelyour-cheap-model, ) register_provider(my_provider) 2. Create plugins/model-providers/your_provider/plugin.yaml: yaml name: your-provider-profile kind: model-provider version: 1.0.0 description: Short sentence about the provider author: Your Name Nothing else needs to change. auth.py, config.py, models.py, doctor.py, model_metadata.py, runtime_provider.py, and the chat_completions transport all auto-wire from the registry. ## Non-trivial profiles Override the ProviderProfile hooks in a subclass for per-provider quirks — see plugins/model-providers/openrouter/__init__.py for build_extra_body and build_api_kwargs_extras examples, and plugins/model-providers/gemini/__init__.py for thinking_config translation.
hermes源码学习5-Provider 运行时解析
Hermes 拥有一个共享的 provider 运行时解析器用于以下场景CLIgatewaycron 任务ACP辅助模型调用主要实现hermes_cli/runtime_provider.py— 凭据解析_resolve_custom_runtime()hermes_cli/auth.py— provider 注册表resolve_provider()hermes_cli/model_switch.py— 共享/model切换流水线CLI gatewayagent/auxiliary_client.py— 辅助模型路由providers/— ABC 注册表入口点ProviderProfile、register_provider、get_provider_profile、list_providersplugins/model-providers/name/— 每个 provider 的插件内置声明api_mode、base_url、env_vars、fallback_models并在首次访问时将自身注册到注册表。用户插件位于$HERMES_HOME/plugins/model-providers/name/会覆盖同名的内置插件。providers/中的get_provider_profile()为给定 provider id 返回一个ProviderProfile。runtime_provider.py在解析时调用它以获取规范的base_url、env_vars优先级列表、api_mode和fallback_models无需在多个文件中重复这些数据。在plugins/model-providers/your-provider/或$HERMES_HOME/plugins/model-providers/your-provider/下添加一个调用register_provider()的新插件即可让runtime_provider.py自动识别它——无需在解析器本身中添加分支。如果你想添加一个新的一等推理 provider请结合本页阅读 添加 Provider 和 Model Provider 插件指南。解析优先级从高层来看provider 解析使用以下顺序显式 CLI/运行时请求config.yaml中的模型/provider 配置环境变量provider 特定的默认值或自动解析该顺序很重要因为 Hermes 将已保存的模型/provider 选择视为正常运行的真实来源。这可以防止过时的 shell 导出变量悄悄覆盖用户在hermes model中最后选择的端点。Provider当前 provider 系列包括完整内置集合见plugins/model-providers/OpenRouterNous PortalOpenAI CodexCopilot / Copilot ACPAnthropic原生Google / Geminigemini、google-gemini-cliAlibaba / DashScopealibaba、alibaba-coding-planDeepSeekZ.AIKimi / Moonshotkimi-coding、kimi-coding-cnMiniMaxminimax、minimax-cn、minimax-oauthKilo CodeHugging FaceOpenCode Zen / OpenCode GoAWS BedrockAzure FoundryNVIDIA NIMxAIGrokArceeGMI CloudStepFunQwen OAuthXiaomiOllama CloudLM StudioTencent TokenHubCustomprovider: custom— 适用于任何 OpenAI 兼容端点的一等 provider命名自定义 providerconfig.yaml中的custom_providers列表运行时解析的输出运行时解析器返回的数据包括providerapi_modebase_urlapi_keysourceprovider 特定的元数据如过期/刷新信息为什么这很重要该解析器是 Hermes 能够在以下场景之间共享认证/运行时逻辑的主要原因hermes chatgateway 消息处理在全新会话中运行的 cron 任务ACP 编辑器会话辅助模型任务OpenRouter 与自定义 OpenAI 兼容 base URLHermes 包含相关逻辑以避免在存在多个 provider 密钥时例如同时存在OPENROUTER_API_KEY和OPENAI_API_KEY将错误的 API key 泄露给自定义端点。每个 provider 的 API key 仅作用于其自身的 base URLOPENROUTER_API_KEY仅发送至openrouter.ai端点OPENAI_API_KEY用于自定义端点及作为回退Hermes 还区分以下两种情况用户主动选择的真实自定义端点未配置自定义端点时使用的 OpenRouter 回退路径这种区分对以下场景尤为重要本地模型服务器非 OpenRouter 的 OpenAI 兼容 API无需重新运行 setup 即可切换 provider通过 config 保存的自定义端点即使当前 shell 中未导出OPENAI_BASE_URL也应正常工作原生 Anthropic 路径Anthropic 不再仅限于通过 OpenRouter访问。当 provider 解析选择anthropic时Hermes 使用api_mode anthropic_messages原生 Anthropic Messages APIagent/anthropic_adapter.py进行转换原生 Anthropic 的凭据解析现在在两者同时存在时优先使用可刷新的 Claude Code 凭据而非复制的环境变量 token。实际效果为包含可刷新认证的 Claude Code 凭据文件被视为首选来源手动设置的ANTHROPIC_TOKEN/CLAUDE_CODE_OAUTH_TOKEN值仍可作为显式覆盖Hermes 在调用原生 Messages API 前会预检 Anthropic 凭据刷新Hermes 在重建 Anthropic 客户端后仍会在收到 401 时重试一次作为回退路径OpenAI Codex 路径Codex 使用独立的 Responses API 路径api_mode codex_responses专用的凭据解析和认证存储支持辅助模型路由辅助任务包括视觉网页提取摘要上下文压缩摘要skills hub 操作MCP 辅助操作记忆刷新这些任务可以使用各自独立的 provider/模型路由而非主对话模型。当辅助任务配置的 provider 为main时Hermes 通过与普通对话相同的共享运行时路径进行解析。实际效果为环境变量驱动的自定义端点仍然有效通过hermes model/config.yaml保存的自定义端点同样有效辅助路由能够区分真实保存的自定义端点与 OpenRouter 回退回退模型Hermes 支持配置回退 provider 链——一个按顺序尝试的(provider, model)条目列表当主模型遇到错误时依次尝试。旧版单对fallback_model字典仍被接受以保持向后兼容并在首次写入时迁移。内部工作原理存储AIAgent.__init__存储fallback_model字典并将_fallback_activated设为False。触发点_try_activate_fallback()在run_agent.py主重试循环的三处被调用在无效 API 响应None choices、缺少 content达到最大重试次数后在不可重试的客户端错误HTTP 401、403、404时在瞬时错误HTTP 429、500、502、503达到最大重试次数后激活流程_try_activate_fallback若已激活或未配置立即返回False调用auxiliary_client.py中的resolve_provider_client()构建带有正确认证的新客户端确定api_modeopenai-codex 使用codex_responsesanthropic 使用anthropic_messages其余使用chat_completions原地替换self.model、self.provider、self.base_url、self.api_mode、self.client、self._client_kwargs对于 anthropic 回退构建原生 Anthropic 客户端而非 OpenAI 兼容客户端重新评估 prompt 缓存对 OpenRouter 上的 Claude 模型启用将_fallback_activated设为True——防止再次触发将重试计数重置为 0 并继续循环配置流程CLIcli.py读取CLI_CONFIG[fallback_model]→ 传递给AIAgent(fallback_model...)Gatewaygateway/run.py._load_fallback_model()读取config.yaml→ 传递给AIAgent验证provider和model键均须非空否则回退被禁用不支持回退的场景子代理委托tools/delegate_tool.py子代理继承父代理的 provider但不继承回退配置辅助任务使用各自独立的 provider 自动检测链见上方辅助模型路由Cron 任务支持回退run_job()从config.yaml读取fallback_providers或旧版fallback_model并传递给AIAgent(fallback_model...)与 gateway 的_load_fallback_model()模式一致。参见 Cron 内部机制。怎么添加一个模型插件Each subdirectory is a self-contained provider profile plugin. The directory layout mirrors plugins/platforms/: plugins/model-providers/ ├── openrouter/ │ ├── __init__.py # registers the ProviderProfile │ └── plugin.yaml # manifest: name, kind, version, description ├── anthropic/ │ ├── __init__.py │ └── plugin.yaml └── ... ## How discovery works providers/__init__.py._discover_providers() scans this directory (and $HERMES_HOME/plugins/model-providers/) the first time anything calls get_provider_profile() or list_providers(). Each __init__.py is imported and expected to call providers.register_provider(profile). User plugins at $HERMES_HOME/plugins/model-providers/name/ override bundled plugins of the same name — last-writer-wins in register_provider(). Drop a file there to replace a built-in. ## Adding a new provider 1. Create plugins/model-providers/your_provider/__init__.py: python from providers import register_provider from providers.base import ProviderProfile my_provider ProviderProfile( nameyour-provider, aliases(alias1, alias2), display_nameYour Provider, descriptionOne-line description shown in the setup picker, signup_urlhttps://your-provider.example.com/keys, env_vars(YOUR_PROVIDER_API_KEY, YOUR_PROVIDER_BASE_URL), base_urlhttps://api.your-provider.example.com/v1, default_aux_modelyour-cheap-model, ) register_provider(my_provider) 2. Create plugins/model-providers/your_provider/plugin.yaml: yaml name: your-provider-profile kind: model-provider version: 1.0.0 description: Short sentence about the provider author: Your Name Nothing else needs to change. auth.py, config.py, models.py, doctor.py, model_metadata.py, runtime_provider.py, and the chat_completions transport all auto-wire from the registry. ## Non-trivial profiles Override the ProviderProfile hooks in a subclass for per-provider quirks — see plugins/model-providers/openrouter/__init__.py for build_extra_body and build_api_kwargs_extras examples, and plugins/model-providers/gemini/__init__.py for thinking_config translation.