Создание плагина провайдера генерации изображений

Плагины провайдеров генерации изображений регистрируют бэкенд, который обслуживает каждый вызов инструмента image_generate — DALL·E, gpt-image, Grok, Flux, Imagen, Stable Diffusion, fal, Replication, локальная установка ComfyUI и т.д. Встроенные провайдеры (OpenAI, OpenAI-Codex, xAI) размещаются как плагины. Вы можете добавить новый или переопределить встроенный, поместив каталог в plugins/image_gen/<name>/.:::совет Генерация изображений — один из нескольких бэкенд-плагинов, которые поддерживают Hermes. Другие (с более специализированными ABC) — Плагины провайдеров памяти, Плагины контекстных движков и Плагины провайдеров моделей. Общие плагины инструментов/хуков/CLI находятся в Создание плагина Hermes.

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

Гермес сканирует резервные копии изображений в трех точках:

  1. Встроенные<repo>/plugins/image_gen/<name>/ (автоматически загружаются с kind: backend, всегда доступны)
  2. Пользовательские~/.hermes/plugins/image_gen/<name>/ (включаются через plugins.enabled)
  3. 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» запрашивает реестр активного провайдера и направляет туда запрос. Если провайдер не зарегистрирован, инструмент выдает полезную ошибку, указывающую на hermestools.

Структура директории

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>).

начало базового класса 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,
    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 generate(
        self,
        prompt: str,
        aspect_ratio: str = DEFAULT_ASPECT_RATIO,
        **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,
            )

        # Приоритет выбора модели: переменная окружения → конфиг → значение по умолчанию. Вспомогательный метод
        # _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"])
            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,
            )
        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())

плагин.yaml

name: my-backend
version: 1.0.0
description: Мой бэкенд изображений — текст-в-изображение через My Backend SDK
author: Ваше имя
kind: backend
requires_env:
  - MY_BACKEND_API_KEY

kind: backend направляет плагин на путь регистрации изображений генерации. requires_env запрашивается во время установки плагинов Hermes.

Справочник по ABC

Полный контракт в agent/image_gen_provider.py. Методы, которые вы обычно переопределяете:

Член Обязательный По умолчанию Назначение
имя Стабильный идентификатор, прогноз в конфигурации image_gen.provider
отображаемое_имя name.title() Метка, отображаемая в hermestools
is_available() Правда Шлюз для отсутствующих учётных данных/зависимостей
list_models() [] Каталог для выбора моделей в hermes Tools
default_model() первый из list_models() Запасной вариант, если модель не в настроении
get_setup_schema() действие Метаданный выбор + запросы окружения
генерировать(подсказка, аспектное соотношение, **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" | "<имя класса исключения>",
    provider=<your-provider-name>,
    model=<model-id>,
    prompt=<prompt>,
    aspect_ratio=<resolved aspect>,
)

Обёртка инструмента сериализует словарь в формате JSON и передаёт ему LLM. Ошибки приведены как результат инструмента; LLM решает, как объяснить их клиенту.

Обработка результатов base64 и URL

Некоторые бэкенды возвращают URL-изображения (fal, Replication); Другие возвращают полезную нагрузку 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 → «Генерация изображений» → выберите «my-backend» → введите API-ключ, если будет предложено.

Эталонные реализации

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

# pyproject.toml
[project.entry-points."hermes_agent.plugins"]
my-backend-imggen = "my_backend_imggen_package"

my_backend_imggen_package должен включать функцию register верхнего уровня. См. Распространение через pip в общем руководстве по плагинам для полных настроек.

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