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

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:

  1. Берёт токен из атрибута бота (по auth.tokenKey)

  2. Собирает prompt:

    • приоритет image.promptAttr (из lead)
    • иначе image.prompt (строкой)
  3. Формирует request body под /images/generations

  4. Отправляет запрос через RemoteApiCall.send(..., asyncResponse: true)

  5. (опционально) показывает messages.wait

  6. Возвращает false — сценарий “выходит” и ждёт callback

PHASE 2 — обработка callback

Когда isFirstImmediateCall = false:

  1. Проверяет, что это действительно callback от процессора (payload.is_async_response)

  2. Если это не callback (пользователь что-то написал):

    • показывает messages.processing
    • возвращает false
  3. Если callback:

    • парсит payload.content
    • извлекает url или b64_json
    • сохраняет в lead
    • если включён requireUrl и url нет → ошибка
  4. Если задан 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 для таймаутов