Создание плагина модели провайдера

Плагины провайдера модели объявляют бэкенд-инференса — совместимы с конечной точкой OpenAI, серверными антропными сообщениями, ответами API в стиле Codex или собственным интерфейсом Bedrock, — через который Hermes может маршрутизировать вызовы AIAgent. Каждый встроенный провайдер (OpenRouter, Anthropic, GMI, DeepSeek, Nvidia и т.д.) подключается как один из таких плагинов. Сторонние разработчики могут добавить свой собственный, поместив каталог в $HERMES_HOME/plugins/model-providers/ без каких-либо изменений в репозитории.:::совет Плагины моделей провайдеров — это третий вид провайдерских плагинов. Другие: Плагины провайдера памяти (межсессионные знания) и Плагины контекстного движка (стратегии сжатия контекста). Все три последующих шаблона: «Помести каталог, объяви профиль, никаких правок репозитория».

Как обнаружено работание

providers/__init__.py._discover_providers() показывает результат при первом вызове get_provider_profile() или list_providers(). Порядок поиска:

  1. Встроенные плагины<repo>/plugins/model-providers/<name>/ — по именися с Hermes
  2. Пользовательские плагины$HERMES_HOME/plugins/model-providers/<name>/ — размещаются в любом каталоге; перезапуск для проведения сессий не требуется
  3. Устаревшие однофайловые<repo>/providers/<name>.py — обратная настройка для независимо редактируемых настроек.

Пользовательские плагины переопределяют встроенные с тем же именем, потому что register_provider() работает по принципу «последний записывающий побеждает». Поместите каталог $HERMES_HOME/plugins/model-providers/gmi/, чтобы заменить встроенный профиль GMI без изменений репозитория.

Структура каталога

plugins/model-providers/my-provider/
├── __init__.py       # Вызывает register_provider(profile) на уровне модуля
├── plugin.yaml       # kind: model-provider + метаданные (рекомендуется, но необязательно)
└── README.md         # Инструкции по настройке (необязательно)

Единственный обязательный файл — __init__.py. plugin.yaml использует несколько hermes плагинов для интроспекции и общего PluginManager для маршрутизации плагина к правильному загрузчику; без него общий загрузчик использует эвристику по исходному тексту.

Минимальный пример — простой провайдер с API-ключом

# plugins/model-providers/acme-inference/__init__.py
from providers import register_provider
from providers.base import ProviderProfile

acme = ProviderProfile(
    name="acme-inference",
    aliases=("acme",),
    display_name="Acme Inference",
    description="Acme — прямой API, совместимый с OpenAI",
    signup_url="https://acme.example.com/keys",
    env_vars=("ACME_API_KEY", "ACME_BASE_URL"),
    base_url="https://api.acme.example.com/v1",
    auth_type="api_key",
    default_aux_model="acme-small-fast",
    fallback_models=(
        "acme-large-v3",
        "acme-medium-v3",
        "acme-small-fast",
    ),
)

register_provider(acme)
# plugins/model-providers/acme-inference/plugin.yaml
name: acme-inference
kind: model-provider
version: 1.0.0
description: Acme Inference — прямой API, совместимый с OpenAI
author: Your Name

Всё. После помещения этих двух файлов следующие компоненты автоматически подключаются без каких-либо других правок:

Интеграция Где Что получает
разрешение учётных данных hermes_cli/auth.py PROVIDER_REGISTRY["acme-inference"] сотрудничает из профиля
Флаг --provider CLI hermes_cli/main.py Принимает acme-inference
Средство выбора модель Гермеса hermes_cli/models.py Имеется в CANONICAL_PROVIDERS, список моделей загружается из {base_url}/models
доктор Гермес hermes_cli/doctor.py Проверка работоспособности ACME_API_KEY + запрос к {base_url}/models
настройка Гермеса hermes_cli/config.py ACME_API_KEY появляется в OPTIONAL_ENV_VARS и в мастере настроек
Обратное собрание URL агент/model_metadata.py Имя хоста → имя провайдера для автоматического определения
Вспомогательная модель агент/auxiliary_client.py Использует default_aux_model для сжатия/увеличения
разрешение во время выполнения hermes_cli/runtime_provider.py Возвращает base_url, api_key, api_mode
Транспорт agent/transports/chat_completions.py Путь профиль потока kwargs через prepare_messages/build_extra_body/build_api_kwargs_extras

Поля Профиль Провайдера

Полное определение в providers/base.py. Наиболее полезно:

Поле Тип Назначение
имя ул Канонический идентификатор — соответствует протоколам --provider и HERMES_INFERENCE_PROVIDER
псевдонимы кортеж[строка,...] Альтернативные имена, разрешенные get_provider_profile() (например, grokxai)
api_mode ул chat_completions | codex_responses | антропные_сообщения | bedrock_converse
отображаемое_имя ул Человекочитаемая метка, отображаемая в размерах выбора модель Гермеса
описание ул Подзаголовок в выборе товаров
signup_url ул Показывает при первой настройке («получить API-ключ здесь»)
env_vars кортеж[строка,...] Переменные окружения для API-переключателей в порядке приоритета; последняя запись *_BASE_URL используется как пользовательское переопределение базового URL
base_url ул Конечная точка вывода по умолчанию
model_url ул Явный URL каталог моделей (по умолчанию {base_url}/models)
auth_type ул api_key | oauth_device_code | oauth_external | второй пилот | aws_sdk | внешний_процесс
резервные_модели кортеж[строка,...] Подобранный список, прогноз, когда загрузка живого каталога не удалась
default_headers dict[str, str] Отправляется с каждым запросом (например, Editor-Version от Copilot)
фиксированная_температура Любой None = использовать значение вызывающего; Sentinel OMIT_TEMPERATURE = не оставлять температуру вообще (Кими)
default_max_tokens интервал \| Нет Ограничение max_tokens на уровне провайдера (Nvidia: 16384)
default_aux_model ул Дешёвая модель для вспомогательных задач (сжатие, взгляд, удлиненизация)

Переопределяемые хуки

Унаследуйте от ProviderProfile для нетривиальных сторон:

from typing import Any
from providers.base import ProviderProfile

class AcmeProfile(ProviderProfile):
    def prepare_messages(self, messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
        """Специфичная для провайдера предобработка сообщений. Выполняется после
        санитизации codex, до замены роли разработчика. По умолчанию: сквозной проход."""
        # Пример: Qwen нормализует простой текст в массив частей
        # и внедряет cache_control; Kimi переписывает JSON вызова инструмента
        return messages

    def build_extra_body(self, *, session_id=None, **context) -> dict:
        """Специфичные для провайдера поля extra_body, объединяемые с вызовом API.
        Контекст включает: session_id, provider_preferences, model, base_url,
        reasoning_config. По умолчанию: пустой словарь."""
        # Пример: блок provider-preferences от OpenRouter,
        # перевод thinking_config от Gemini.
        return {}

    def build_api_kwargs_extras(self, *, reasoning_config=None, **context):
        """Возвращает (extra_body_additions, top_level_kwargs). Нужно, когда некоторые
        поля идут на верхний уровень (reasoning_effort от Kimi), а некоторые — в extra_body
        (словарь reasoning от OpenRouter). По умолчанию: ({}, {})."""
        return {}, {}

    def fetch_models(self, *, api_key=None, timeout=8.0) -> list[str] | None:
        """Загрузка живого каталога. По умолчанию обращается к {models_url или base_url}/models
        с Bearer-аутентификацией. Переопределите для: собственной аутентификации (Anthropic),
        отсутствия REST endpoint (Bedrock → None) или публичных/неаутентифицированных каталогов (OpenRouter)."""
        return super().fetch_models(api_key=api_key, timeout=timeout)

Примеры использования хуков

Посмотрите на эти встроенные заглушки для идиомы:

Плагин Зачем смотреть
plugins/model-providers/openrouter/ Агрегатор с предпочтениями провайдера, публичный каталог моделей
плагины/поставщики моделей/gemini/ Перевод thinking_config (родные + вложенные формы, совместимые с OpenAI)
плагины/поставщики моделей/кими-кодирование/ OMIT_TEMPERATURE, extra_body.thinking, reasoning_effort на верхнем уровне
plugins/model-providers/qwen-oauth/ Нормализация сообщений, реализации cache_control, VL высокое разрешение
плагины/поставщики моделей/nous/ Теги атрибуции, «опускать рассуждения, когда отключено»
плагины/поставщики моделей/пользовательские/ Особенности Олламы: num_ctx + think: false
плагины/поставщики моделей/основание/ api_mode="bedrock_converse", fetch_models получает None (нет конечной точки REST) ​​

Пользовательские переопределения — замена встроенного без правки репозитория

Допустим, вы хотите написать gmi на вашей частной промежуточной конечной точке для тестирования. Создайте ~/.hermes/plugins/model-providers/gmi/__init__.py:

from providers import register_provider
from providers.base import ProviderProfile

register_provider(ProviderProfile(
    name="gmi",
    aliases=("gmi-cloud", "gmicloud"),
    env_vars=("GMI_API_KEY",),
    base_url="https://gmi-staging.internal.example.com/v1",
    auth_type="api_key",
    default_aux_model="google/gemini-3.1-flash-lite-preview",
))

В следующей сессии get_provider_profile("gmi").base_url возвращает промежуточный URL-адрес. Никакой патчей репозитория, никакой пересборки. Поскольку пользовательские плагины обнаруживаются после встроенных, пользователь побеждает вызов register_provider().

Выбор api_mode

Распознаются значения четырех. Гермес выбирает одно на основе:

  1. Явного переопределения пользователя (config.yaml, model.api_mode, если задано)
  2. Диспетчеризация моделей в OpenCode (opencode_model_api_mode для Zen и Go)
  3. Автоопределение URL — суффикс /anthropicanthropic_messages, api.openai.comcodex_responses, api.x.aicodex_responses, /coding на доменах Kimi → chat_completions
  4. api_mode профиль как запасной вариант, при определении URL-адреса ничего не находит
  5. По умолчанию chat_completions

Установите profile.api_mode в соответствии с тем, что указывает ваш провайдер по умолчанию — это действует как подсказка. Пользовательские переопределения URL всё равно имеют приоритет.

Типы аутентификации

auth_type Значение Кто использует
api_key Одна переменная среда содержит статический API-ключ Большинство провайдеров
oauth_device_code Поток OAuth с кодом устройства
oauth_external Пользователь в системе в другом месте входит, токены используются в auth.json Anthropic OAuth, MiniMax OAuth, Gemini Cloud Code, Qwen Portal, Nous Portal
второй пилот Цикл обновлений токена GitHub Copilot только подключите второй пилот
aws_sdk Цепочка учётных данных AWS SDK (роль IAM, профиль, окружение) только подключите основание
внешний_процесс Аутентификация проходит дочерним процессом, который запускает агент только подключите copilot-acp

auth_type определяет, какой код пути считает вашего провайдера «простым провайдером с API-ключом» — если это не api_key, PluginManager всё равно записывает манифест, но автоматизация на уровне CLI Hermes (проверки Doctor, флаг --provider, делегирование мастеру) может пропустить его.

Определение времени

Обнаружение провайдеров ленивое — запускается при первом вызове get_provider_profile() или list_providers() в процессе. В примере это происходит рано при запуске (загрузка модуля auth.py расширяет PROVIDER_REGISTRY жадно). Если вам нужно проверить, загрузился ли ваш штекер, выполните:

hermes doctor

— успешный профиль с auth_type="api_key" появится в разделе «Подключение провайдера» с запросом к /models.

Для программной проверки:

from providers import list_providers
for p in list_providers():
    print(p.name, p.base_url, p.api_mode)

Тестирование вашего плагина

Укажите HERMES_HOME во временном каталоге, чтобы не загрязнять вашу реальную конфигурацию:

export HERMES_HOME=/tmp/hermes-plugin-test
mkdir -p $HERMES_HOME/plugins/model-providers/my-provider
cat > $HERMES_HOME/plugins/model-providers/my-provider/__init__.py <<'EOF'
from providers import register_provider
from providers.base import ProviderProfile
register_provider(ProviderProfile(
    name="my-provider",
    env_vars=("MY_API_KEY",),
    base_url="https://api.my-provider.example.com/v1",
    auth_type="api_key",
))
EOF

export MY_API_KEY=your-test-key
hermes -z "hello" --provider my-provider -m some-model

Интеграция с общим PluginManager

Общий PluginManager (то, с чем работает hermes plugs) видит плагины моделей провайдера, но не импортирует их — providers/__init__.py управляет их жизненным циклом. Менеджер записывает манифест для интроспекции и классифицирует по типу: модель-поставщик. Когда вы помещаете немаркированный пользовательский плагин в $HERMES_HOME/plugins/, который вызывает register_provider с ProviderProfile, менеджер автоматически приводит его к kind: model-provider через эвристику по исходному тексту — так что плагин всё равно маршрутизируется правильно, даже без plugin.yaml.

Распространение через pip

Как и любой Hermes, провайдеры моделей могут распространяться как pip-пакет. Добавьте точку входа в ваш pyproject.toml:

[project.entry-points."hermes.plugins"]
acme-inference = "acme_hermes_plugin:register"

…где acme_hermes_plugin:register — это функция, которая вызывает register_provider(profile). Общий PluginManager подхватывает плагины для входа во время discover_and_load(). Для pip-плагинов с kind: model-provider вам всё равно нужно объявить вид в манифесте (или опираться на эвристику по исходному тексту).

См. Создание плагина Hermes для полной настройки точек входа.

Связанные страницы