ComfyUI принимает граф генерации по HTTP: один запрос ставит задачу в очередь, второй отдаёт её статус, третий возвращает готовый файл. Движок остаётся локальным, интерфейс живёт отдельно. В разбираемом сценарии интерфейс собирается в api2app без программирования, и, по словам автора урока, такую форму можно оперативно перестраивать под меняющиеся задачи, не трогая код (разбор на Хабре).
Схема раскладывается на три слоя.
| Слой | Что делает | Где живёт |
|---|---|---|
| Workflow ComfyUI | Описывает граф: модель, промпт, сэмплер, разрешение | Машина с GPU |
| HTTP-слой | Три конфигурации API: постановка задачи, статус, файл | api2app |
| Интерфейс | Текстовое поле, кнопка, прогрессбар, область вывода | api2app |
Запросов три, потому что генерация асинхронная. Первый вызов только ставит задачу в очередь и возвращает её идентификатор. Второй проверяет состояние. Третий отдаёт картинку по идентификатору контента. Одной конфигурацией не обойтись: ответ на постановку задачи изображения не содержит.
Что понадобится: рабочая установка ComfyUI на машине с GPU, аккаунт api2app и, если приложением будут пользоваться из интернета, пакет comfy-api-proxy. Без него стандартное API ComfyUI годится для локальных экспериментов.
Зачем превращать ComfyUI в API и что берёт на себя api2app
Разделение обязанностей простое. ComfyUI считает, api2app показывает. Платформа берёт на себя форму, кнопки, состояния загрузки и хранение настроек запросов, а вся тяжёлая работа остаётся на вашем железе. Правки интерфейса делаются в конструкторе, пересобирать приложение не нужно.
Плата за это - привязка к платформе и к формату ответов ComfyUI. Логика приложения живёт в конфигурациях API, а не в коде, и её возможности ограничены тем, что умеет конструктор.
Кому подходит схема, а кому проще написать код
Схема выгодна в одном случае: есть отлаженный workflow и нужно быстро отдать его людям через понятную форму. Переделать поле, подпись или набор параметров можно в конструкторе за несколько минут.
Ограничения начинаются там, где нужна логика посложнее. Своя авторизация пользователей, биллинг, приоритеты в очереди, история генераций, тонкая обработка ошибок на серверной стороне: всё это придётся либо обходить средствами платформы, либо переносить в код. Если таких требований много, FastAPI плюс небольшой фронтенд дадут больше контроля, хотя и потребуют разработчика. Конструктор без кода экономит время на старте и ограничивает свободу на дистанции.
Шаг 1. Готовим workflow ComfyUI и экспортируем его в JSON
В уроке за основу взят готовый шаблон ComfyUI «Flux.2 [Klein] 4B: текст в изображение». Порядок такой: открыть шаблон, прогнать генерацию в обычном режиме и убедиться, что результат устраивает. Отправлять в API неотлаженный граф смысла нет: ошибка в нодах превратится в задачу, которая упадёт уже на стороне приложения, и разбираться придётся в двух местах сразу.
После успешного прогона делается экспорт для API. Это отдельное действие: обычное сохранение workflow и экспорт для API дают разные файлы, и для отправки в очередь нужен второй. Полученный json-файл открывают и копируют содержимое в буфер обмена целиком.
Структура API-формата узнаваема: объект, где ключи - идентификаторы нод, а внутри каждого лежат class_type и inputs. Данных интерфейса вроде координат нод на канвасе в нём, как правило, нет.
Почему в теле запроса ключ prompt, а не workflow
Стандартное API ComfyUI ждёт JSON workflow под ключом prompt. Тело запроса выглядит примерно так:
{
"prompt": {
"3": { "class_type": "...", "inputs": { ... } }
}
}
При работе через comfy-api-proxy ключ меняется на workflow:
{
"workflow": {
"3": { "class_type": "...", "inputs": { ... } }
}
}
Это первое место, где ломаются запросы при переходе с локального режима на прокси. Ошибка выглядит как отказ принять тело запроса, хотя сам workflow рабочий. Держите два варианта тела под рукой и меняйте ключ вместе с эндпоинтом, иначе отладка уйдёт в сторону проверки графа, где всё в порядке.
Шаг 2. Запускаем ComfyUI в режиме CORS для локального тестирования
Браузер пользователя api2app обращается к ComfyUI с другого origin. Без разрешающего заголовка запрос блокируется ещё до того, как дойдёт до сервера, и в консоли появляется ошибка CORS. В уроке ComfyUI запускают так:
python main.py --enable-cors-header
В пример из урока в параметр подставляется домен api2app, чтобы разрешение выдавалось конкретному источнику. Ответ ComfyUI получает заголовок Access-Control-Allow-Origin, и браузер пропускает данные.
Это режим тестирования. Держать CORS открытым постоянно на машине, которая смотрит в интернет, не стоит: разрешение снимает защиту браузера для всех, кто знает адрес. Для внешнего доступа в уроке предусмотрен отдельный путь через прокси.
Шаг 3. Первая конфигурация API в api2app: постановка задачи
В разделе «API» на api2app нажимаем «Добавить новый API». Дальше вкладка «Сырые данные»: там создаётся объект с ключом prompt, и в него вставляется скопированный JSON workflow. Метод запроса меняется на POST, URL указывает на эндпоинт постановки задачи вашей установки ComfyUI (в стандартном API это путь /prompt).
Тест запускают в режиме «Браузер»: запрос уходит из браузера, и в ответе видно, что задача действительно встала в очередь, а не упала на валидации графа. Ответ содержит идентификатор задачи в очереди, и именно это значение понадобится во второй конфигурации.
Скопируйте идентификатор и проверьте, что он меняется от запуска к запуску. Если значение повторяется, скорее всего запрос уходит не туда или тело подставляется из кеша.
Шаг 4. Вторая конфигурация API: опрос статуса задачи
Создаётся новая конфигурация с методом GET. URL строится из адреса очереди и идентификатора задачи: /api/v2/jobs/{id}. В примере из урока используется именно этот путь.
В ответе приходит статус. Четыре состояния: queued - задача в очереди, running - генерация идёт, succeeded - результат готов, error - выполнение сорвалось. Эти же состояния будут управлять прогрессбаром.
При succeeded в ответе появляются outputs: URL для получения результата и идентификатор контента, он же asset_id (в ответе лежит в поле id).
Опрос повторяют. Один запрос покажет состояние на момент вызова, чтобы дождаться результата, приложение обращается к конфигурации снова и снова с небольшой паузой. Формат ответа зависит от версии ComfyUI и от того, работаете вы напрямую или через прокси, поэтому поля стоит сверить на тестовом запросе до того, как завязывать на них интерфейс.
Шаг 5. Третья конфигурация API: получение изображения по asset_id
Третья конфигурация тоже GET. URL собирается вокруг идентификатора контента: asset_id берётся из ответа второй конфигурации. В уроке этот запрос назван просмотром изображения. Конкретный вид пути зависит от сборки, поэтому один раз выполните его вручную и убедитесь, что в ответ приходит файл, а не JSON с текстом ошибки.
Цепочка целиком: POST постановки, идентификатор задачи, GET статуса, succeeded, asset_id, GET файла, картинка в интерфейсе. Три конфигурации образуют одну последовательность, а не три независимых запроса. Вторая без первой не знает, что опрашивать, третья без второй не знает, что скачивать. При отладке идите по цепочке с начала: сначала стабильный идентификатор задачи, потом статус succeeded, и только затем вывод файла.
Шаг 6. comfy-api-proxy: доступ из мира, токен и заголовок Authorization
Стандартное API ComfyUI рассчитано на локальное применение. Для доступа извне в уроке предлагается отдельный пакет, который ставится через pip:
pip install comfy-api-proxy
Запуск с ключом авторизации и CORS:
comfy-api-proxy --enable-cors-header --token=<ваш-токен>
Токен в примере из урока приведён как рабочий образец, для своего приложения генерируйте собственный. Это секрет: он даёт доступ к вашей очереди, и публиковать его в скриншотах, репозиториях и переписке не нужно.
После запуска меняются три вещи: адрес запроса становится /api/v2/jobs, ключ в теле запроса меняется с prompt на workflow, а в заголовки добавляется Authorization с тем же токеном. Все три пункта перечислены в источнике (урок по связке ComfyUI и api2app).
Что именно ломается при переходе на прокси
| Элемент | Локальный режим | Через comfy-api-proxy |
|---|---|---|
| Эндпоинт постановки задачи | Путь стандартного API ComfyUI | /api/v2/jobs |
| Ключ тела запроса | prompt | workflow |
| Заголовки | Без авторизации | Authorization с токеном |
| Поле авторизации в режиме «Браузер» | Не используется | Не должно быть приватным |
Чек-лист типовых ошибок: остался ключ prompt; не добавлен заголовок Authorization; в конфигурации указан эндпоинт локальной установки вместо прокси; поле авторизации помечено как приватное в режиме «Браузер» (кнопка с иконкой замка). В уроке отдельно оговорено: в режиме «Браузер» это поле приватным быть не должно, иначе тест не пройдёт. Причина в том, что приватное поле не уходит в браузерный запрос, а прокси без токена отвечает отказом.
Шаг 7. Связываем интерфейс с API: поля ввода, вывода и прогрессбар
Оговорка про источник: опубликованный текст урока обрывается на этапе создания конфигураций, поэтому детали привязки элементов, скрытых полей и статусов ниже описаны по логике работы api2app и не процитированы дословно из материала.
Набор элементов минимальный: текстовое поле для промпта, кнопка запуска, индикатор прогресса и область вывода изображения. Связываются они действиями ввода и вывода: действие ввода берёт значение из элемента интерфейса и подставляет в тело запроса, действие вывода раскладывает ответ API по полям формы.
Ключевой момент - скрытые поля. Их два: идентификатор задачи из ответа первой конфигурации и идентификатор файла (asset_id) из ответа второй. Пользователю они не видны, но без них вторая и третья конфигурации не получат параметров.
Последовательность при нажатии кнопки:
- Взять текст из поля и отправить POST в конфигурацию постановки задачи.
- Записать идентификатор задачи в скрытое поле.
- Опрашивать вторую конфигурацию, подставляя идентификатор и обновляя прогрессбар по статусу.
- При succeeded записать asset_id в скрытое поле и остановить опрос.
- Запросить третью конфигурацию и вывести изображение в область результата.
Прогрессбар привязывается к тем же четырём статусам: queued - ожидание в очереди, running - генерация, succeeded - готово, error - сбой. Полоска делает ожидание понятным: человек видит, что приложение работает, а не зависло на отправке.
Как обрабатывать статус error
На статусе error опрос нужно остановить, прогрессбар сбросить и показать понятное сообщение. Зависший в running интерфейс хуже явной ошибки: пользователь не понимает, ждать ему или закрывать вкладку. Конкретные тексты ошибок ComfyUI зависят от версии и от того, на каком узле упал граф, так что в интерфейс стоит выводить общую формулировку и кнопку повторной попытки. Логи полезно сохранять: без них причина сбоя останется загадкой.
Шаг 8. Публикация приложения и доступ извне без лишнего риска
Приложение публикуется в api2app, после чего им можно пользоваться по ссылке. Чтобы запросы доходили до локального ComfyUI, нужен обратный туннель: он принимает внешние соединения и передаёт их на машину с GPU. API при этом переводится в режим «Сервер», а заголовки авторизации помечаются приватными, чтобы токен оставался на серверной стороне и не попадал в клиентский код. Утёкший токен означает чужой доступ к вашей очереди, а вместе с ней к видеокарте и электричеству.
Базовой защитой от злоупотреблений в уроке предусмотрено ограничение дневных запросов с одного IP. Фильтр грубый: он отсекает случайный спам, обходится сменой адреса и не заменяет нормальную авторизацию. Пользователей за одним адресом он при этом уравнивает в квоте.
ComfyUI, выставленный в интернет напрямую, без прокси и токена, означает открытую очередь для любого желающего. Детали публикации, настройки туннеля и лимитов в опубликованном тексте урока не раскрыты, так что параметры сверяйте в актуальной документации сервисов перед тем, как открывать доступ.
Ограничения подхода и когда он не подойдёт
- Машина с ComfyUI должна быть включена. Перезагрузка ПК или падение интернета превращаются в ошибки у всех пользователей приложения.
- Одна GPU обрабатывает задачи последовательно. При нескольких одновременных запросах очередь растёт, и время ожидания становится непредсказуемым.
- Прокси и токен добавляют компонент, который нужно обслуживать: обновлять пакет, читать логи, ротировать ключ.
- Лимит запросов по IP не различает людей за одним адресом и не спасает от целенаправленного обхода.
- Конструктор без кода ограничивает логику: роли, оплата, история генераций и сложные сценарии ошибок упираются в его рамки.
- Формат ответов зависит от версии ComfyUI, поэтому обновление движка может потребовать правок в конфигурациях API.
Схема хорошо работает как быстрый способ отдать готовый workflow наружу: интерфейс собирается за вечер, а генерация остаётся под контролем владельца железа. Заменой продакшен-инфраструктуре она не становится. Когда запросов станет много, следующим шагом будет вынос ComfyUI на серверную GPU, очередь с приоритетами и собственная авторизация, а api2app останется удобным слоем для формы. Начать стоит с одного workflow и трёх конфигураций: этого достаточно, чтобы понять, где именно ваша схема упрётся в ограничения.