Common.AI.ImageGen — Генерация изображений
Автор: Art Yg Версия: 1.0
ImageGen — универсальный плагин для асинхронной генерации изображений через внешний Webhook Processor, с сохранением результата в lead (URL и/или base64) и поддержкой сценарного выхода (success/error/timeout).
Плагин спроектирован так, чтобы работать в двухфазном режиме, как LLMQuery:
- PHASE 1 — отправить запрос (async) и выйти из скрипта
- PHASE 2 — обработать callback-ответ от процессора и продолжить сценарий
Зачем он существует
В Metabot-сценариях нельзя считать генерацию изображения “быстрой синхронной функцией”:
- внешние провайдеры отвечают с задержкой
- ответ может потеряться
- пользователь может продолжать писать, пока мы ждём
- результат может прийти не в том формате (URL vs b64)
ImageGen стандартизирует этот поток:
- отправляет запрос через Webhook Processor (Remote transport)
- ждёт callback и распознаёт “это callback или пользовательский ввод”
- сохраняет результат в атрибуты lead
- может уйти в
successScript(опционально) - может уйти в
error.script/timeout(если настроены)
Что использует внутри
ImageGen — это обёртка, которая опирается на инфраструктурные компоненты:
-
Common.Remote.RemoteApiCall — транспорт, отправляет запрос в Webhook Processor и включает
asyncResponse - Webhook Processor — внешний слой, который делает реальный HTTP-запрос к провайдеру и возвращает callback
- Common.Platform.AsyncFallback — таймаут/фоллбек-оркестрация
- локальная таблица провайдеров внутри ImageGen — mapping baseUrl/endpoint/method
Важно: RemoteApiCall как транспорт в принципе поддерживает любых провайдеров, но текущая версия ImageGen по умолчанию заточена под OpenAI Images API.
Поддерживаемые провайдеры
На текущий момент поддержан:
-
openai
Если вам нужен другой провайдер (например, Replicate, Stability, Midjourney proxy, внутренний сервис) — свяжитесь с командой, и мы расширим плагин.
Примечание для архитектуры: сейчас таблица провайдеров (
PROVIDERS) находится внутри плагина. При необходимости её можно вынести наружу (в конфиг бота/таблицу/отдельный реестр), чтобы вы могли подключать свои провайдеры без изменения кода плагина.
Что сохраняет
В зависимости от настроек:
-
save.urlAttr— URL изображения (если провайдер вернул url) -
save.b64Attr— base64 JSON (b64_json), если пришёл именно он -
save.rawJsonAttr— сырой payload ответа (для диагностики)
Двухфазный протокол выполнения
PHASE 1 — отправка запроса
Когда isFirstImmediateCall = true:
-
Берёт токен из атрибута бота (по
auth.tokenKey) -
Собирает prompt:
- приоритет
image.promptAttr(из lead) - иначе
image.prompt(строкой)
- приоритет
-
Формирует request body под
/images/generations -
Отправляет запрос через
RemoteApiCall.send(..., asyncResponse: true) -
(опционально) показывает
messages.wait -
Возвращает
false— сценарий “выходит” и ждёт callback
PHASE 2 — обработка callback
Когда isFirstImmediateCall = false:
-
Проверяет, что это действительно callback от процессора (
payload.is_async_response) -
Если это не callback (пользователь что-то написал):
- показывает
messages.processing - возвращает
false
- показывает
-
Если callback:
- парсит
payload.content - извлекает
urlилиb64_json - сохраняет в lead
- если включён
requireUrlи url нет → ошибка
- парсит
-
Если задан
successScript— делаетbot.run(successScript), иначе возвращаетсяtrue
Конфигурация
Ниже описаны ключевые блоки ImageGen.run().
Provider + Auth
provider: "openai",
auth: { tokenKey: "OPENAI_API_KEY" }
-
provider— ключ провайдера (сейчас актуальноopenai) -
auth.tokenKey— имя атрибута бота, где лежит API ключ
Image параметры
image: {
model: "dall-e-3",
prompt: "...",
promptAttr: null,
n: 1,
size: "1024x1792",
quality: "standard",
style: "natural",
background: null,
response_format: "url"
}
Практика по форматам результата:
-
dall-e-2 / dall-e-3: можно запросить
response_format: "url"и получить временный URL -
gpt-image- модели*: часто отдают
b64_json(URL может не прийти)
requireUrl
requireUrl: true
Если true, то отсутствие URL считается ошибкой (даже если пришёл b64).
Это удобно для MVP-потока “дай URL, а скачивание/сохранение сделаю потом”.
UX сообщения
messages: {
wait: "🜁 Генерируем…",
processing: "⏳ Подожди, ещё формируется…",
error: "⚠️ Не удалось получить изображение"
}
-
wait— показываем при отправке (PHASE 1) -
processing— показываем если пользователь пишет во время ожидания (PHASE 2, но это не callback) -
error— сообщение по умолчанию при ошибке (если нет error.script)
Ошибки
error: {
script: "Orion_Image_Error",
flagAttr: "orion_image_error",
reasonAttr: "orion_image_error_reason"
}
-
flagAttr/reasonAttr— сохранить состояние ошибки в lead -
script— куда перейти при ошибке (опционально)
Успешный выход
successScript: "Orion_Image_Ready"
Если не указан — ImageGen.run() возвращается в ту же точку (return true), без перехода.
Рекомендуемый паттерн с таймаутом
Сам ImageGen может быть использован вместе с Common.Platform.AsyncFallback.
Почему это важно: без timeout у тебя будет “вечное ожидание”, если callback потеряется.
Типовой паттерн:
- PHASE 1:
AsyncFallback.configure(...).schedule() - PHASE 2 (callback):
fb.unschedule() - если результат плохой:
fb.fail(reason, details)+ редирект в error script
Примеры использования
Пример 1 — таймаут + error + successScript (всё внутри вызова ImageGen)
/**
* orion_profiling_reflection_image
*
* Назначение:
* - асинхронно сгенерировать вертикальный "Operator Reflection" образ
* - сохранить URL в лид (скачивание/сохранение — отдельным шагом)
* - при успехе уйти в successScript
* - при ошибке или таймауте уйти в сценарии обработки
*/
const ImageGen = require("Common.AI.ImageGen");
return ImageGen.run({
lead,
isFirstImmediateCall,
code: "OrionActorImage",
// Provider/Auth
provider: "openai",
auth: { tokenKey: "OPENAI_API_KEY" },
// Таймаут (fallback)
timeout: {
seconds: 120,
script: "Orion_Image_Timeout"
},
// Ошибки (унифицировано как в LLMQuery)
error: {
script: "Orion_Image_Error",
flagAttr: "orion_image_error",
reasonAttr: "orion_image_error_reason"
},
// Режим MVP: хотим именно URL (а не b64)
requireUrl: true,
image: {
model: "dall-e-3",
size: "1024x1792",
quality: "standard",
style: "natural",
response_format: "url",
// PROMPT — компактнее, без потери смысла (и без “портрета человека”)
prompt: `Create a vertical codex-grade mythotech icon representing an abstract Operator entity from the NetWorld universe.
Aurum Void aesthetic: cold matte gold schematic lines (axes, nodes, ritual UI glyphs) over deep graphite void. No glossy sci-fi, no neon, no superhero vibe, no humor.
The figure must be faceless (hood/shadow/mask/void), calm and stable, centered on a strong vertical axis. Heavy materials: carbon composite armor/robe, matte metal, dense fabric, worn realistic textures. Subtle fog/particles for depth.
This is NOT a human portrait. It is an operational manifestation of a system entrepreneur / product leader at the scaling stage. Show structural responsibility and architect-like gravity.
Output: a visual artifact, not an illustration.`
},
messages: {
wait: "🜁 ORION формирует визуальное отражение…",
processing: "⏳ Подожди, образ ещё куется…",
error: "⚠️ Не удалось получить изображение"
},
save: {
urlAttr: "orion_actor_image_url",
b64Attr: "orion_actor_image_b64",
rawJsonAttr: "orion_actor_image_payload"
},
// Опциональный выход: если не задан — вернёмся в ту же точку
successScript: "Orion_Image_Ready"
});
Пример 2 — “простая вставка”: без successScript, но с таймаутом и error-обработчиком
Подходит, когда вы хотите вернуться в тот же сценарий и уже там решить, что делать дальше (например, проверить orion_actor_image_url и показать кнопку “скачать”).
const ImageGen = require("Common.AI.ImageGen");
return ImageGen.run({
lead,
isFirstImmediateCall,
code: "OrionActorImage",
provider: "openai",
auth: { tokenKey: "OPENAI_API_KEY" },
timeout: {
seconds: 120,
script: "Orion_Image_Timeout"
},
error: {
script: "Orion_Image_Error",
flagAttr: "orion_image_error",
reasonAttr: "orion_image_error_reason"
},
requireUrl: true,
image: {
model: "dall-e-3",
size: "1024x1792",
response_format: "url",
prompt: "Create a vertical codex-grade mythotech Operator icon in Aurum Void aesthetic..."
},
save: {
urlAttr: "orion_actor_image_url",
rawJsonAttr: "orion_actor_image_payload"
}
});
Пример 3 — когда таймаут управляется снаружи (через AsyncFallback)
Этот способ полезен, если у вас одна общая политика таймаутов или вы хотите управлять фоллбеками централизованно (например, для нескольких разных async-операций в одном шаге).
const ImageGen = require("Common.AI.ImageGen");
const AsyncFallback = require("Common.Platform.AsyncFallback");
// PHASE 1: конфиг + планирование таймаута
if (isFirstImmediateCall) {
AsyncFallback.configure({
lead,
namespace: "orion_image_reflection",
timeout: { seconds: 120, script: "Orion_Image_Timeout" },
error: { flagAttr: "orion_image_error", reasonAttr: "orion_image_error_reason" }
}).schedule();
}
// Сам вызов генерации (без встроенного timeout)
const res = ImageGen.run({
lead,
isFirstImmediateCall,
code: "OrionActorImage",
provider: "openai",
auth: { tokenKey: "OPENAI_API_KEY" },
requireUrl: true,
image: {
model: "dall-e-3",
size: "1024x1792",
response_format: "url",
prompt: "Create a vertical codex-grade mythotech Operator icon in Aurum Void aesthetic..."
},
save: {
urlAttr: "orion_actor_image_url",
rawJsonAttr: "orion_actor_image_payload"
},
error: {
script: "Orion_Image_Error",
flagAttr: "orion_image_error",
reasonAttr: "orion_image_error_reason"
},
successScript: "Orion_Image_Ready"
});
// PHASE 2: если callback пришёл и операция завершилась — снимаем таймаут
if (!isFirstImmediateCall && res === true) {
AsyncFallback.configure({
lead,
namespace: "orion_image_reflection"
}).unschedule();
}
return res;
Частые сценарии отказов и как реагировать
-
send_failed — процессор не принял запрос / RemoteApiCall не отработал →
error.scriptили retry -
url_missing при
requireUrl=true→ либо переключиться наb64_json, либо поменять модель/response_format -
no_result — ответ пришёл, но данных нет → сохранить
rawJsonAttrи смотреть, что вернул провайдер/процессор -
timeout (через AsyncFallback) → отдельный timeout-script может предложить повторить генерацию или вернуться в меню
Итог
Common.AI.ImageGen — это стандартизированный способ подключить генерацию изображений в сценарии Metabot:
- двухфазный async flow
- сохранение результата в lead
- UX на случай “пользователь пишет во время ожидания”
- расширяемая система провайдеров (сейчас OpenAI)
- легко стыкуется с
Common.Platform.AsyncFallbackдля таймаутов