Создание плагина провайдера генерации изображений
Плагины провайдеров регистрируют бэкенд, который обрабатывает каждый вызов инструмента image_generate — DALL·E, gpt-image, Grok, Flux, Imagen, Stable Diffusion, fal, Replicate, локальный ComfyUI или что угодно ещё. Встроенные провайдеры (OpenAI, OpenAI-Codex, xAI) — это тоже плагины. Чтобы добавить новый или переопределить встроенный, достаточно положить каталог в plugins/image_gen/<name>/.
Генерация изображений — один из нескольких типов бэкенд-плагинов Hermes Agent. Остальные (со своими специализированными ABC) описаны здесь: Плагины провайдеров памяти, Плагины движка контекста и Плагины провайдеров моделей. Плагины для инструментов, хуков и CLI — в разделе Создание плагина для Hermes.
Механизм обнаружения
Hermes Agent ищет бэкенды генерации изображений в трёх местах:
- Встроенные —
<repo>/plugins/image_gen/<name>/(загружаются автоматически приkind: backend, всегда доступны) - Пользовательские —
~/.hermes/plugins/image_gen/<name>/(подключаются черезplugins.enabled) - Pip — пакеты, объявляющие точку входа
hermes_agent.plugins
Функция register(ctx) каждого плагина вызывает ctx.register_image_gen_provider(...), добавляя провайдер в реестр agent/image_gen_registry.py. Активный провайдер задаётся параметром image_gen.provider в config.yaml; команда hermes tools поможет выбрать нужный вариант.
Обёртка инструмента image_generate запрашивает у реестра активный провайдер и передаёт ему вызов. Если ни один провайдер не зарегистрирован, инструмент вернёт понятную ошибку с отсылкой к hermes tools.
Структура каталога
plugins/image_gen/my-backend/
├── __init__.py # Подкласс ImageGenProvider + функция register()
└── plugin.yaml # Манифест с kind: backend
Для встроенного плагина этого достаточно. Пользовательские плагины из ~/.hermes/plugins/image_gen/<name>/ нужно дополнительно добавить в plugins.enabled файла config.yaml или выполнить hermes plugins enable <name>.
ABC ImageGenProvider
Наследуйтесь от agent.image_gen_provider.ImageGenProvider. Из всех членов класса обязательны только свойство name и метод generate() — у остальных есть разумные значения по умолчанию:
# plugins/image_gen/my-backend/__init__.py
from typing import Any, Dict, List, Optional
import os
from agent.image_gen_provider import (
DEFAULT_ASPECT_RATIO,
ImageGenProvider,
error_response,
normalize_reference_images,
resolve_aspect_ratio,
save_b64_image,
success_response,
)
class MyBackendImageGenProvider(ImageGenProvider):
@property
def name(self) -> str:
# Стабильный идентификатор, используется в конфиге image_gen.provider. Строчные, без пробелов.
return "my-backend"
@property
def display_name(self) -> str:
# Человекочитаемое название в `hermes tools`. По умолчанию — name.title().
return "My Backend"
def is_available(self) -> bool:
# Вернуть False, если не хватает учётных данных или зависимостей.
# Проверяется перед каждым вызовом инструмента.
if not os.environ.get("MY_BACKEND_API_KEY"):
return False
try:
import my_backend_sdk # noqa: F401
except ImportError:
return False
return True
def list_models(self) -> List[Dict[str, Any]]:
# Каталог моделей, отображаемый в `hermes tools`.
return [
{
"id": "my-model-fast",
"display": "My Model (Fast)",
"speed": "~5s",
"strengths": "Quick iteration",
"price": "$0.01/image",
},
{
"id": "my-model-hq",
"display": "My Model (HQ)",
"speed": "~30s",
"strengths": "Highest fidelity",
"price": "$0.04/image",
},
]
def default_model(self) -> Optional[str]:
return "my-model-fast"
def get_setup_schema(self) -> Dict[str, Any]:
# Метаданные для выборщика `hermes tools` — ключи, запрашиваемые при настройке.
return {
"name": "My Backend",
"badge": "paid", # опционально; краткая метка в выборщике
"tag": "One-line description shown under the name",
"env_vars": [
{
"key": "MY_BACKEND_API_KEY",
"prompt": "My Backend API key",
"url": "https://my-backend.example.com/api-keys",
},
],
}
def capabilities(self) -> Dict[str, Any]:
# Объявляет, поддерживает ли бэкенд image-to-image / редактирование.
# Слой инструмента отражает это в динамической схеме, чтобы модель
# знала, когда учитывается `image_url`. По умолчанию (если метод не задан)
# — только текст: {"modalities": ["text"], "max_reference_images": 0}.
return {"modalities": ["text", "image"], "max_reference_images": 4}
def generate(
self,
prompt: str,
aspect_ratio: str = DEFAULT_ASPECT_RATIO,
*,
image_url: Optional[str] = None,
reference_image_urls: Optional[List[str]] = None,
**kwargs: Any,
) -> Dict[str, Any]:
prompt = (prompt or "").strip()
aspect_ratio = resolve_aspect_ratio(aspect_ratio)
if not prompt:
return error_response(
error="Prompt is required",
error_type="invalid_input",
provider=self.name,
prompt="",
aspect_ratio=aspect_ratio,
)
# Маршрутизация: если задан image_url (или reference_image_urls), это запрос
# image-to-image / редактирования, иначе — text-to-image. Выбранный путь
# сообщайте через поле `modality` в success_response.
sources = []
if image_url:
sources.append(image_url)
sources.extend(normalize_reference_images(reference_image_urls) or [])
modality = "image" if sources else "text"
# Приоритет выбора модели: переменная окружения → конфиг → значение по умолчанию.
# Хороший пример — _resolve_model() во встроенном плагине openai.
model_id = kwargs.get("model") or self.default_model() or "my-model-fast"
try:
import my_backend_sdk
client = my_backend_sdk.Client(api_key=os.environ["MY_BACKEND_API_KEY"])
if modality == "image":
result = client.edit(
prompt=prompt,
model=model_id,
image_urls=sources,
)
else:
result = client.generate(
prompt=prompt,
model=model_id,
aspect_ratio=aspect_ratio,
)
# Поддерживаются два формата ответа:
# - строка URL: передаётся как `image`
# - данные base64: сохраняются в $HERMES_HOME/cache/images/ через save_b64_image()
if result.get("image_b64"):
path = save_b64_image(
result["image_b64"],
prefix=self.name,
extension="png",
)
image = str(path)
else:
image = result["image_url"]
return success_response(
image=image,
model=model_id,
prompt=prompt,
aspect_ratio=aspect_ratio,
provider=self.name,
modality=modality,
)
except Exception as exc:
return error_response(
error=str(exc),
error_type=type(exc).__name__,
provider=self.name,
model=model_id,
prompt=prompt,
aspect_ratio=aspect_ratio,
)
def register(ctx) -> None:
"""Точка входа плагина — вызывается один раз при загрузке."""
ctx.register_image_gen_provider(MyBackendImageGenProvider())
plugin.yaml
name: my-backend
version: 1.0.0
description: My image backend — text-to-image via My Backend SDK
author: Your Name
kind: backend
requires_env:
- MY_BACKEND_API_KEY
Именно kind: backend направляет плагин на путь регистрации генерации изображений. requires_env запрашивается при выполнении hermes plugins install.
Справочник ABC
Полный контракт — в agent/image_gen_provider.py. Методы, которые чаще всего переопределяют:
| Член | Обязателен | По умолчанию | Назначение |
|---|---|---|---|
name | ✅ | — | Стабильный идентификатор для конфига image_gen.provider |
display_name | — | name.title() | Метка в hermes tools |
is_available() | — | True | Проверка наличия учётных данных и зависимостей |
list_models() | — | [] | Каталог моделей для выборщика hermes tools |
default_model() | — | первая из list_models() | Резервный выбор, если модель не задана в конфиге |
get_setup_schema() | — | минимальный | Метаданные выборщика и промпты для переменных окружения |
generate(prompt, aspect_ratio, **kwargs) | ✅ | — | Основной вызов |
Формат ответа
generate() должен возвращать словарь, построенный через success_response() или error_response(). Обе функции находятся в agent/image_gen_provider.py.
Успех:
success_response(
image=<url-or-absolute-path>,
model=<model-id>,
prompt=<echoed-prompt>,
aspect_ratio="landscape" | "square" | "portrait",
provider=<your-provider-name>,
extra={...}, # опционально: специфичные для бэкенда поля
)
Ошибка:
error_response(
error="human-readable message",
error_type="provider_error" | "invalid_input" | "<exception class name>",
provider=<your-provider-name>,
model=<model-id>,
prompt=<prompt>,
aspect_ratio=<resolved aspect>,
)
Обёртка инструмента сериализует словарь в JSON и передаёт его языковой модели. Ошибки приходят как результат вызова инструмента — LLM сама решает, как объяснить их пользователю.
Base64 или URL: как обрабатывать ответ
Одни бэкенды возвращают URL изображений (fal, Replicate), другие — данные в кодировке base64 (OpenAI gpt-image-2). Для base64 используйте save_b64_image(): функция пишет файл в $HERMES_HOME/cache/images/<prefix>_<timestamp>_<uuid>.<ext> и возвращает абсолютный Path. Этот путь (как str) передайте в параметр image= функции success_response(). Доставка через шлюз (фото в Telegram, вложение в Discord) понимает и URL, и абсолютные пути.
Пользовательские переопределения
Создайте пользовательский плагин в ~/.hermes/plugins/image_gen/<name>/ с тем же значением свойства name, что у встроенного, и подключите его через hermes plugins enable <name>. Реестр работает по принципу «последний пишет — побеждает», так что ваша версия заменит встроенную. Удобно, например, чтобы направить плагин openai на приватный прокси или подставить собственный каталог моделей.
Тестирование
export HERMES_HOME=/tmp/hermes-imggen-test
mkdir -p $HERMES_HOME/plugins/image_gen/my-backend
# …скопируйте __init__.py и plugin.yaml в этот каталог…
export MY_BACKEND_API_KEY=your-test-key
hermes plugins enable my-backend
# Выбрать как активный провайдер
echo "image_gen:" >> $HERMES_HOME/config.yaml
echo " provider: my-backend" >> $HERMES_HOME/config.yaml
# Проверить работу
hermes -z "Generate an image of a corgi in a spacesuit"
Или в интерактивном режиме: hermes tools → «Image Generation» → выбрать my-backend → при необходимости ввести API-ключ.
Примеры реализаций
plugins/image_gen/openai/__init__.py— gpt-image-2 с тремя уровнями качества (low/medium/high), реализованными как три виртуальных идентификатора модели на одном API-бэкенде с разными параметрамиquality. Хорошая иллюстрация многоуровневых моделей на едином бэкенде и приоритетов конфига в config.yaml.plugins/image_gen/xai/__init__.py— Grok Imagine через xAI. Другая форма ответа (URL-вывод, более простой каталог).plugins/image_gen/openai-codex/__init__.py— вариант через Responses API в стиле Codex, переиспользующий OpenAI SDK с другим базовым URL маршрутизации.
Распространение через pip
# pyproject.toml
[project.entry-points."hermes_agent.plugins"]
my-backend-imggen = "my_backend_imggen_package"
my_backend_imggen_package должен предоставлять функцию register на верхнем уровне. Полная инструкция по настройке — в разделе Распространение через pip общего руководства по плагинам.
Связанные страницы
- Генерация изображений — документация функции для пользователей
- Обзор плагинов — все типы плагинов в одном месте
- Создание плагина для Hermes — руководство по инструментам, хукам и slash-командам