Разрешение провайдера во время выполнения
Hermes использует общий механизм разрешения провайдера во время выполнения, применяемый в:
-
CLI
-
gateway
-
cron-заданиях
-
ACP
-
вспомогательных вызовах моделей
Основные реализации:
-
hermes_cli/runtime_provider.py— разрешение учётных данных,_resolve_custom_runtime() -
hermes_cli/auth.py— реестр провайдеров,resolve_provider() -
hermes_cli/model_switch.py— общий конвейер переключения/model(CLI + gateway) -
agent/auxiliary_client.py— маршрутизация вспомогательных моделей -
providers/— ABC + точки входа реестра (ProviderProfile,register_provider,get_provider_profile,list_providers) -
plugins/model-providers/<name>/— плагины отдельных провайдеров (встроенные), которые объявляютapi_mode,base_url,env_vars,fallback_modelsи регистрируются в реестре при первом обращении. Пользовательские плагины в$HERMES_HOME/plugins/model-providers/<name>/переопределяют встроенные с тем же именем.
get_provider_profile() в providers/ возвращает 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 подхватил его — ветвление в самом резолвере не требуется.
Если вы пытаетесь добавить нового первоклассного инференс-провайдера, прочтите Adding Providers и Model Provider Plugin guide вместе с этой страницей.
Приоритет разрешения
На высоком уровне разрешение провайдера использует:
-
явный запрос из CLI/среды выполнения
-
конфигурацию модели/провайдера в
config.yaml -
переменные окружения
-
значения по умолчанию для конкретного провайдера или авторазрешение
Этот порядок важен, поскольку Hermes использует сохранённый выбор модели/провайдера как источник истины для обычных запусков. Это предотвращает случайное переопределение конечной точки, выбранной пользователем в hermes model, устаревшим экспортом из shell.
Провайдеры
Текущие семейства провайдеров включают (см. plugins/model-providers/ для полного набора встроенных):
-
AI Gateway (Vercel)
-
OpenRouter
-
Nous Portal
-
OpenAI Codex
-
Copilot / Copilot ACP
-
Anthropic (нативный)
-
Google / Gemini (
gemini,google-gemini-cli) -
Alibaba / DashScope (
alibaba,alibaba-coding-plan) -
DeepSeek
-
Z.AI
-
Kimi / Moonshot (
kimi-coding,kimi-coding-cn) -
MiniMax (
minimax,minimax-cn,minimax-oauth) -
Kilo Code
-
Hugging Face
-
OpenCode Zen / OpenCode Go
-
AWS Bedrock
-
Azure Foundry
-
NVIDIA NIM
-
xAI (Grok)
-
Arcee
-
GMI Cloud
-
StepFun
-
Qwen OAuth
-
Xiaomi
-
Ollama Cloud
-
LM Studio
-
Tencent TokenHub
-
Custom (
provider: custom) — первоклассный провайдер для любой OpenAI-совместимой конечной точки -
Именованные пользовательские провайдеры (список
custom_providersв config.yaml)
Результат разрешения во время выполнения
Резолвер времени выполнения возвращает такие данные, как:
-
provider -
api_mode -
base_url -
api_key -
source -
метаданные, специфичные для провайдера, такие как информация об истечении срока/обновлении
Почему это важно
Этот резолвер — основная причина, по которой Hermes может разделять логику аутентификации/выполнения между:
-
hermes chat -
обработкой сообщений gateway
-
cron-заданиями, запускаемыми в новых сессиях
-
сессиями редактора ACP
-
задачами вспомогательных моделей
AI Gateway
Установите AI_GATEWAY_API_KEY в ~/.hermes/.env и запускайте с флагом --provider ai-gateway. Hermes получает доступные модели из эндпоинта /models gateway, фильтруя языковые модели с поддержкой tool-use.
OpenRouter, AI Gateway и пользовательские OpenAI-совместимые base URLs
Hermes содержит логику для предотвращения утечки неверного API-ключа на пользовательскую конечную точку, когда существует несколько ключей провайдеров (например, OPENROUTER_API_KEY, AI_GATEWAY_API_KEY и OPENAI_API_KEY).
API-ключ каждого провайдера привязан к своему собственному base URL:
-
OPENROUTER_API_KEYотправляется только на эндпоинтыopenrouter.ai -
AI_GATEWAY_API_KEYотправляется только на эндпоинтыai-gateway.vercel.sh -
OPENAI_API_KEYиспользуется для пользовательских эндпоинтов и как запасной вариант
Hermes также различает:
-
реальную пользовательскую конечную точку, выбранную пользователем
-
путь отката OpenRouter, используемый, когда не настроена пользовательская конечная точка
Это различие особенно важно для:
-
локальных серверов моделей
-
OpenAI-совместимых API, отличных от OpenRouter/AI Gateway
-
переключения провайдеров без повторного запуска настройки
-
сохранённых в конфигурации пользовательских эндпоинтов, которые должны продолжать работать, даже если
OPENAI_BASE_URLне экспортирован в текущем shell
Нативный путь Anthropic
Anthropic — это больше не просто «через OpenRouter».
Когда разрешение провайдера выбирает anthropic, Hermes использует:
-
api_mode = anthropic_messages -
нативный API Anthropic Messages
-
agent/anthropic_adapter.pyдля трансляции
Разрешение учётных данных для нативного Anthropic теперь предпочитает обновляемые учётные данные Claude Code скопированным токенам окружения, когда оба присутствуют. На практике это означает:
-
файлы учётных данных Claude Code рассматриваются как предпочтительный источник, когда они включают обновляемую аутентификацию
-
ручные значения
ANTHROPIC_TOKEN/CLAUDE_CODE_OAUTH_TOKENпо-прежнему работают как явные переопределения -
Hermes предварительно проверяет обновление учётных данных Anthropic перед нативными вызовами Messages API
-
Hermes по-прежнему выполняет одну повторную попытку при 401 после пересоздания клиента Anthropic, как путь отката
Путь OpenAI Codex
Codex использует отдельный путь Responses API:
-
api_mode = codex_responses -
выделенное разрешение учётных данных и поддержка хранилища аутентификации
Маршрутизация вспомогательных моделей
Вспомогательные задачи, такие как:
-
vision
-
суммаризация извлечения из веба
-
суммаризация сжатия контекста
-
суммаризация поиска по сессиям
-
операции skills hub
-
вспомогательные операции MCP
-
сбросы памяти
могут использовать собственную маршрутизацию провайдера/модели, а не основную диалоговую модель.
Когда вспомогательная задача настроена с провайдером main, Hermes разрешает её через тот же общий путь выполнения, что и обычный чат. На практике это означает:
-
пользовательские эндпоинты, управляемые окружением, по-прежнему работают
-
пользовательские эндпоинты, сохранённые через
hermes model/config.yaml, также работают -
вспомогательная маршрутизация может отличить реальный сохранённый пользовательский эндпоинт от отката OpenRouter
Резервные модели
Hermes поддерживает настроенную цепочку резервных провайдеров — список пар (provider, model), перебираемых по порядку при возникновении ошибок у основной модели. Устаревший словарь fallback_model с одной парой по-прежнему принимается для обратной совместимости (и мигрируется при первой записи).
Как это работает внутри
-
Хранение:
AIAgent.__init__сохраняет словарьfallback_modelи устанавливает_fallback_activated = False. -
Точки срабатывания:
_try_activate_fallback()вызывается из трёх мест в основном цикле повторных попыток вrun_agent.py: - После исчерпания попыток при невалидных ответах API (None choices, отсутствующий контент)
- При неповторяемых клиентских ошибках (HTTP 401, 403, 404)
-
После исчерпания попыток при временных ошибках (HTTP 429, 500, 502, 503)
-
Процесс активации (
_try_activate_fallback): - Немедленно возвращает
False, если уже активирован или не настроен - Вызывает
resolve_provider_client()изauxiliary_client.pyдля создания нового клиента с правильной аутентификацией - Определяет
api_mode:codex_responsesдля openai-codex,anthropic_messagesдля anthropic,chat_completionsдля всего остального - Заменяет на месте:
self.model,self.provider,self.base_url,self.api_mode,self.client,self._client_kwargs - Для отката anthropic: создаёт нативный клиент Anthropic вместо OpenAI-совместимого
- Переоценивает кэширование промптов (включено для моделей Claude на OpenRouter)
- Устанавливает
_fallback_activated = True— предотвращает повторное срабатывание -
Сбрасывает счётчик попыток в 0 и продолжает цикл
-
Процесс конфигурации:
- CLI:
cli.pyчитаетCLI_CONFIG["fallback_model"]→ передаёт вAIAgent(fallback_model=...) - Gateway:
gateway/run.py._load_fallback_model()читаетconfig.yaml→ передаёт вAIAgent - Валидация: оба ключа
providerиmodelдолжны быть непустыми, иначе резервный режим отключается
Что НЕ поддерживает резервный режим
-
Делегирование сабагентам (
tools/delegate_tool.py): сабагенты наследуют провайдера родителя, но не конфигурацию резервного режима -
Вспомогательные задачи: используют собственную независимую цепочку автоопределения провайдера (см. раздел «Маршрутизация вспомогательных моделей» выше)
Cron-задания поддерживают резервный режим: run_job() читает fallback_providers (или устаревший fallback_model) из config.yaml и передаёт его в AIAgent(fallback_model=...), следуя шаблону _load_fallback_model() из gateway. См. Cron Internals.
Тестовое покрытие
См. tests/test_fallback_model.py для всесторонних тестов, охватывающих всех поддерживаемых провайдеров, семантику одноразового срабатывания и граничные случаи.