Перейти к основному содержимому

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

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

подсказка

Генерация изображений — один из нескольких бэкенд-плагинов, которые поддерживает VibeOS. Другие (с более специализированными ABC) — это Плагины провайдеров памяти, Плагины контекстных движков и Плагины провайдеров моделей. Общие плагины инструментов/хуков/CLI описаны в разделе Создание плагина VibeOS.

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

VibeOS сканирует бэкенды генерации изображений в трёх местах:

  1. Встроенные — <repo>/plugins/image_gen/<name>/ (автозагрузка с kind: backend, всегда доступны)
  2. Пользовательские — ~/.vibeos/plugins/image_gen/<name>/ (включаются через plugins.enabled)
  3. Pip — пакеты, объявляющие точку входа vibeos_agent.plugins

Функция register(ctx) каждого плагина вызывает ctx.register_image_gen_provider(...) — это помещает его в реестр в agent/image_gen_registry.py. Активный провайдер выбирается параметром image_gen.provider в config.yaml; vibeos tools проводит пользователя через процесс выбора.

Обёртка инструмента image_generate запрашивает у реестра активный провайдер и направляет запрос туда. Если провайдер не зарегистрирован, инструмент выводит понятную ошибку со ссылкой на vibeos tools.

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

plugins/image_gen/my-backend/
├── __init__.py # Подкласс ImageGenProvider + register()
└── plugin.yaml # Манифест с kind: backend

Встроенный плагин на этом этапе готов. Пользовательские плагины в ~/.vibeos/plugins/image_gen/&lt;name&gt;/ необходимо добавить в plugins.enabled в config.yaml (или выполнить vibeos 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:
# Человеческое название, отображаемое в `vibeos 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]]:
# Каталог, отображаемый в выборе моделей `vibeos tools`.
return [
{
"id": "my-model-fast",
"display": "My Model (Fast)",
"speed": "~5s",
"strengths": "Быстрая итерация",
"price": "$0.01/image",
},
{
"id": "my-model-hq",
"display": "My Model (HQ)",
"speed": "~30s",
"strengths": "Максимальная точность",
"price": "$0.04/image",
},
]

def default_model(self) -> Optional[str]:
return "my-model-fast"

def get_setup_schema(self) -> Dict[str, Any]:
# Метаданные для выбора в `vibeos tools` — ключи для запроса при настройке.
return {
"name": "My Backend",
"badge": "paid", # необязательно; отображается как короткий тег в списке выбора
"tag": "Однострочное описание под названием",
"env_vars": [
{
"key": "MY_BACKEND_API_KEY",
"prompt": "API-ключ My Backend",
"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",
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: сохранить в $VIBEOS_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: Мой бэкенд изображений — text-to-image через My Backend SDK
author: Ваше Имя
kind: backend
requires_env:
- MY_BACKEND_API_KEY

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

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

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

ЧленОбязательныйПо умолчаниюНазначение
name✅—Стабильный идентификатор, используемый в конфиге image_gen.provider
display_name—name.title()Название, отображаемое в vibeos tools
is_available()—TrueШлюз для отсутствующих учётных данных/зависимостей
list_models()—[]Каталог для выбора модели в vibeos 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-или-абсолютный-путь>,
model=<id-модели>,
prompt=<повторённый-prompt>,
aspect_ratio="landscape" | "square" | "portrait",
provider=<имя-вашего-провайдера>,
extra={...}, # необязательные поля, специфичные для бэкенда
)

Ошибка:

error_response(
error="читаемое сообщение",
error_type="provider_error" | "invalid_input" | "<имя класса исключения>",
provider=<имя-вашего-провайдера>,
model=<id-модели>,
prompt=<prompt>,
aspect_ratio=<разрешённый aspect>,
)

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

Обработка вывода base64 и URL​

Некоторые бэкенды возвращают URL изображений (fal, Replicate); другие — полезную нагрузку base64 (OpenAI gpt-image-2). Для случая base64 используйте save_b64_image() — она записывает в $VIBEOS_HOME/cache/images/&lt;префикс&gt;_&lt;временная_метка&gt;_&lt;uuid&gt;.&lt;расширение&gt; и возвращает абсолютный Path. Передайте этот путь (как str) в качестве image=вsuccess_response()`. Доставка через шлюз (пузырь Telegram, вложение Discord) распознаёт как URL, так и абсолютные пути.

Пользовательские переопределения​

Поместите пользовательский плагин в ~/.vibeos/plugins/image_gen/&lt;name&gt;/ с тем же свойством name, что и у встроенного, и включите его через vibeos plugins enable &lt;name&gt; — реестр работает по принципу «последний записывающий побеждает», поэтому ваша версия заменяет встроенную. Полезно для направления плагина openai` на частный прокси или замены пользовательского каталога моделей.

Тестирование​

export VIBEOS_HOME=/tmp/vibeos-imggen-test
mkdir -p $VIBEOS_HOME/plugins/image_gen/my-backend
# …скопируйте __init__.py + plugin.yaml в эту директорию…

export MY_BACKEND_API_KEY=your-test-key
vibeos plugins enable my-backend

# Выберите его как активный провайдер
echo "image_gen:" >> $VIBEOS_HOME/config.yaml
echo " provider: my-backend" >> $VIBEOS_HOME/config.yaml

# Проверьте
vibeos -z "Сгенерируй изображение корги в скафандре"

Или интерактивно: vibeos tools → «Image Generation» → выберите my-backend → введите API-ключ, если будет предложено.

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

  • plugins/image_gen/openai/__init__.py — gpt-image-2 на низком/среднем/высоком уровнях как три виртуальных ID модели, использующих одну модель API с разными параметрами quality. Хороший пример многоуровневых моделей в одном бэкенде + цепочка приоритетов config.yaml.
  • plugins/image_gen/xai/__init__.py — Grok Imagine через xAI. Другая форма (вывод URL, более простой каталог).
  • plugins/image_gen/openai-codex/__init__.py — Вариант API Responses в стиле Codex, повторно использующий OpenAI SDK с другим базовым URL маршрутизации.

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

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

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

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