- Python 70.8%
- TypeScript 20.1%
- CSS 4.6%
- PowerShell 2%
- Shell 1.8%
- Other 0.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
Build ComfyUI-OpenAI-API image / build-and-push (push) Successful in 2m14s
Both create a new video job derived from a completed source video, sharing
one builder (_create_derived_video_job) plus _load_completed_source_video
(404 not found / 409 not complete) and _parse_video_derivation_payload
(JSON or multipart).
POST /v1/videos/extensions:
- body: prompt (required) + video:{id} (JSON) or video.id (form) reference
- inherits the source's model/seconds/size/quality unless the request
overrides them; kind follows the source (or img2video if an image ref is
supplied); records extended_from in job metadata
- returns a queued Video object with remixed_from_video_id pointing at the
source
POST /v1/videos/{video_id}/remix:
- body: prompt (required); same inheritance as extensions
- re-runs the source workflow with the new prompt; records remixed_from in
metadata; returns queued Video object with remixed_from_video_id
Route order verified: /v1/videos/extensions (POST) registers before
/v1/videos/{video_id} (GET) so it is not shadowed; {video_id}/remix is a
distinct suffix. 8 new tests (extension: 400 no-prompt/no-ref, inherit
model+seconds+size+quality, seconds override wins, 409 incomplete source;
remix: new job w/ new prompt + inherited seconds, 400 no-prompt, 404
unknown source). 180 tests green. README: curl examples for both.
|
||
| .forgejo/workflows | ||
| .github/workflows | ||
| assets | ||
| comfyui-api-workflows | ||
| frontend | ||
| i18n | ||
| packaging | ||
| scripts | ||
| src/comfyui2api | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| AGENTS.md | ||
| docker-compose.yml | ||
| docker-entrypoint.sh | ||
| Dockerfile | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| README.upstream-zh.md | ||
| requirements.txt | ||
| start.bat | ||
| start.ps1 | ||
| start.sh | ||
| uv.lock | ||
🚀 ComfyUI-OpenAI-API
ComfyUI → OpenAI-совместимый HTTP API: генерация картинок и видео по запросам
/v1/images/generations, /v1/images/edits, /v1/videos/* — как будто вы
обращаетесь к любой OpenAI-совместимой модели.
Этот репозиторий — «Вариант B»: зрелая база
opq007/Comfyui2api (мультибэкенд,
SQLite, job-планировщик) + Workflow Studio из форка RzMY + графовая
рандомизация seed из форка xundaoai + русский дашборд + Docker/CI/Compose
инфраструктура maydayoffice.
Исходный (китайский) README upstream сохранён в README.upstream-zh.md.
✨ Что даёт
- 🎨 Мультимодальность: text-to-image / image-to-image / text-to-video / image-to-video — всё определяется capability-анализом workflow (KSampler / VideoHelperSuite и т.д.), а не настройками.
- 🖥️ Несколько ComfyUI-бэкендов: инстансы регистрируются в веб-панели (раздел «Инстансы»), хранятся в SQLite. У каждого: URL, health-check, лимит параллельных слотов, стратегия маршрутизации (round-robin / random / failover), привязка к опубликованным моделям.
- 🌐 Публикуемые модели: вы сами решаетите, какие workflow становятся «моделями» (slug + capability), а какие остаются внутренними.
- ⚙️ Workflow Studio: загрузка workflow (drag-n-drop или выбор файла),
автообнаружение полей (prompt / negative / steps / cfg / size / seed / image),
редактор маппинга (JSON-sidecar
.comfyui2api/*.params.json), онлайн-тест генерации прямо из панели с живым предпросмотром. - 🎲 Рандомизация seed: даже если workflow не объявляет
seedпараметром, граф-обход пересажаетnoise_seed/seed/seed2на KSampler / SamplerCustom / SamplerCustomAdvanced / RandomNoise. Явныйseedв запросе уважается (репродуктивность). - 🔁 Hot-reload: изменения в каталоге workflow подхватываются без рестарта.
- 📡 Realtime-прогресс: WebSocket ComfyUI → события узла/прогресса →
task.progress_percent+ SSE/поллинг в панели. - 🔐 Fail-closed: API требует
API_TOKEN, админка/ui—ADMIN_TOKEN. Брутфорс админки блокируется по IP (rate-limit). - 🪶 Совместимость с New-API / один клик к любому OpenAI-клиенту:
base_url=https://ваш-хост:8000/v1,model=<slug>,Authorization: Bearer <API_TOKEN>.
📡 Быстрый старт (Docker)
git clone https://git.maydayoffice.kz/maydayoffice/ComfyUI-OpenAI-API
cd ComfyUI-OpenAI-API
cp .env.example .env
# отредактируйте .env: API_TOKEN и ADMIN_TOKEN (минимум)
docker compose up -d
Откройте http://localhost:8000/ui → введите ADMIN_TOKEN → добавьте
инстанс ComfyUI (http://host.docker.internal:8188 или внешний адрес) →
создайте публикуемую модель из загруженного workflow → попробуйте через
Playground.
Готовый образ: git.maydayoffice.kz/maydayoffice/comfyui-openai-api:latest
(собирается Forgejo CI при пуше в main, см. .forgejo/workflows/ci-cd.yml).
Проблема с правами на
./workflows. Контейнер работает от непривилегированного пользователяappuser(uid10001). Входная точка образа (docker-entrypoint.sh) при старте как root делаетchown -R /dataв10001, а потом снижает права черезsetpriv— поэтому bind-монтаж./workflows(даже если на хосте он принадлежит другому uid) становится записываемым. Если вы запускаете старый образ без этого фикса и «Сохранить и перезагрузить» / загрузка workflow молча не работают (в логахPermissionError, HTTP 500), вручную выполните на хосте:sudo chown -R 10001:10001 ./workflows— либо пересоберите/перетяните свежий образ.
🔌 OpenAI-совместимый API
Text-to-image
curl -s -X POST http://localhost:8000/v1/images/generations \
-H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
-d '{
"model": "my-t2i",
"prompt": "a red paper boat on calm water, cinematic",
"n": 1,
"size": "1024x1024",
"quality": "high",
"response_format": "b64_json"
}'
Image-to-image
curl -s -X POST http://localhost:8000/v1/images/edits \
-H "Authorization: Bearer $API_TOKEN" \
-F "model=my-i2i" -F "prompt=make it night" \
-F "image=@input.png" \
-F "strength=0.5"
Image variations (prompt не требуется — вариация по картинке)
# multipart (как в OpenAI) или JSON-тело с image: data-URL / URL
curl -s -X POST http://localhost:8000/v1/images/variations \
-H "Authorization: Bearer $API_TOKEN" \
-F "model=my-i2i" -F "image=@input.png" -F "n=2"
Video (если workflow — video-capable)
curl -s -X POST http://localhost:8000/v1/videos \
-H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
-d '{"model":"my-t2v","prompt":"sunset over the sea","size":"1280x720"}'
Поле model — это slug публикуемой модели (см. раздел
«Публикуемые модели» в /ui), а не имя workflow напрямую.
Video edits — по уже готовому видео (OpenAI video.id)
# JSON: ссылка на завершённую видео-задачу (id из POST /v1/videos)
curl -s -X POST http://localhost:8000/v1/videos/edits \
-H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
-d '{"model":"my-i2v","prompt":"make it darker","video":{"id":"video_<job_id>"}}'
# или multipart: прямая загрузка видеофайла / ссылки
curl -s -X POST http://localhost:8000/v1/videos/edits \
-H "Authorization: Bearer $API_TOKEN" \
-F "model=my-i2v" -F "prompt=make it darker" -F "video=@take.mp4"
Задачей-источником может быть только завершённое видео (иначе 409).
Отвечает как POST /v1/videos — объект video со status: "queued";
результат — через GET /v1/videos/{id} и GET /v1/videos/{id}/content.
Video extension — продолжить готовое видео
# prompt описывает продолжение; model/seconds/size наследуются от исходника
# (переопределяются полями запроса), video.id — ссылка на завершённую задачу
curl -s -X POST http://localhost:8000/v1/videos/extensions \
-H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
-d '{"prompt":"the camera pans right","video":{"id":"video_<job_id>"}}'
Video remix — перегенерировать с новым prompt
# тот же workflow/модель, но новый prompt; остальные параметры наследуются
curl -s -X POST http://localhost:8000/v1/videos/video_<job_id>/remix \
-H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" \
-d '{"prompt":"now in black and white"}'
Оба эндпоинта отвечают объектом video (status:"queued") с полем
remixed_from_video_id, указывающим на источник. Результат — через
GET /v1/videos/{id} и /content.
Видео: запрос статуса, скачивание, список, удаление
# Статус одной задачи (id — из ответа POST /v1/videos)
curl -s http://localhost:8000/v1/videos/{video_id} \
-H "Authorization: Bearer $API_TOKEN"
# Готовый файл (только когда status=completed; URL также возвращается в поле url)
curl -s -o result.mp4 http://localhost:8000/v1/videos/{video_id}/content \
-H "Authorization: Bearer $API_TOKEN"
# Список видео-задач (limit до 100, after — курсор: video_id, вернёт старшие по времени)
curl -s "http://localhost:8000/v1/videos?limit=10" \
-H "Authorization: Bearer $API_TOKEN"
# Удалить задачу: из памяти, из SQLite и с диска (runs/{job_id}/)
curl -s -X DELETE http://localhost:8000/v1/videos/{video_id} \
-H "Authorization: Bearer $API_TOKEN"
Параметры (стандартный набор)
prompt, negative_prompt, model, n, size/width/height, steps,
scheduler, cfg, seed, quality, seconds/frames/fps (видео),
strength (i2i), response_format (url | b64_json), произвольные
extra_body-поля, проксируемые в маппинг.
📂 Структура каталогов (в контейнере)
| Mount | Path | Содержимое |
|---|---|---|
./workflows |
/data/workflows |
ComfyUI API JSON (.json) + sidecar-маппинг (.comfyui2api/*.params.json) |
| volume | /data/runs |
Результаты задач (картинки/видео) |
| volume | /data/data |
SQLite: задачи, инстансы, модели |
| (в образе) | /opt/webui |
Собранная версия дашборда (язык переключается в рантайме) |
| (в образе) | /app/i18n |
apply_i18n.py + locales/*.json (проверка/повторный перевод после merge upstream) |
WORKFLOWS_DIR, RUNS_DIR, DATA_DIR, DATABASE_PATH,
COMFYUI2API_UI_DIST_DIR уже заданы в ENV образа; переопределять не нужно
(кроме случаев, когда вы знаете, зачем).
🧑💻 Workflow Studio (раздел «Workflow» в /ui)
- Upload — файл workflow (ComfyUI → File → Export (API)), либо drag-n-drop. Имя файла = имя workflow.
- Detected fields — автообнаруженные кандидаты (prompt / negative / steps / cfg / seed / width / height / image).
- Mapping — привяжите стандартные API-параметры к полям графа
(через UI или raw JSON). Результат пишется в sidecar
.comfyui2api/<workflow>.params.jsonи hot-reload'ится. - Test — inline-запуск тестовой генерации с прогрессом и предпросмотром результата (для txt2img / img2img без image-ввода).
Все операции Studio идут через админ API /v1/admin/workflows*
(POST upload, GET details, PUT/DELETE mapping, DELETE workflow,
POST test).
🎲 Рандомизация seed
Дополнение к spec-based обработке (upstream random'ит seed, только если
workflow объявил его параметром): граф-обход
randomize_graph_noise_seeds() в jobs.py пересажает noise_seed /
seed / seed2 на узлах KSampler, SamplerCustom,
SamplerCustomAdvanced, RandomNoise для каждого job.
Гвард: если клиент прислал seed явно (в standard_params), граф не
трогается — повторный запрос с тем же seed вернёт тот же результат.
Тесты: tests/test_seed_randomize.py (4 кейса).
🔌 Админ API (/v1/admin, токен ADMIN_TOKEN)
| Метод | Путь | Назначение |
|---|---|---|
| GET | /v1/admin/overview |
статистика, конфиг (без секретов) |
| GET/POST/PUT/DELETE | /v1/admin/instances[/{slug}] |
бэкенды |
| GET/POST/PUT/DELETE | /v1/admin/models[/{slug}] |
публикуемые модели |
| GET | /v1/admin/workflows |
список workflow |
| POST | /v1/admin/workflows |
upload (Studio) |
| GET | /v1/admin/workflows/{name} |
details + fields + suggested template (Studio) |
| PUT/DELETE | /v1/admin/workflows/{name}/mapping |
маппинг (Studio) |
| DELETE | /v1/admin/workflows/{name} |
удаление workflow (Studio) |
| POST | /v1/admin/workflows/{name}/test |
inline-тест (Studio) |
| GET/POST | /v1/admin/tasks[/{id}] |
журнал задач |
| GET | /v1/admin/tasks/{id}/outputs |
результаты |
| POST | /v1/admin/shutdown |
остановить сервис |
📄 Конфигурация
Все параметры — в .env.example (скопировать в .env). Минимум:
API_TOKEN=... # обязателен
ADMIN_TOKEN=... # обязателен
API_PORT=8000
Полный список (опциональные): PUBLIC_BASE_URL, SIGNED_URL_SECRET,
JOB_RETENTION_SECONDS, MAX_JOBS_IN_MEMORY, TIMEOUT_S, HTTP_TIMEOUT_S,
HEALTH_CHECK_*, лимиты и т.д.
🐳 Сборка образа (CI)
- Forgejo:
.forgejo/workflows/ci-cd.yml. Триггеры: push вmain+ ручной (workflow_dispatchсcommit/force). - Образ:
git.maydayoffice.kz/maydayoffice/comfyui-openai-api:{sha-7,full,latest}. - Этапы: 1)
node:20-alpine+ pnpm 8:pnpm install --frozen-lockfile→python3 i18n/apply_i18n.py frontend(проверка покрытия: каждый словарьlocales/<code>.jsonимеет тот же набор ключей, чтоen.json, каждый ключt("msg_N")есть во всех словарях, params совпадают с{placeholder}'ами — иначе падает build) →pnpm build(tsc --noEmit && vite build). Перевод происходит в рантайме (в браузере): все словариfrontend/src/lib/locales/*.jsonподхватываются черезimport.meta.globи попадают в бандл; в коде — только семантические ключиmsg_N, никакого пользовательского текста. Недостающий ключ автоматически показывается на английском (fallbacken). 2)python:3.11-slim:pip install .+/opt/webui+ENV+appuser. - Перед сборкой CI гоняет
python -m unittest discover -s testsв эфемерном контейнереpython:3.11-slim— ровно то, что потом окажется в образе.
🧬 Upstream / forks — что взято от кого
| Комитет | Источник | Что |
|---|---|---|
| База | opq007/Comfyui2api 3e69d6f |
мультибэкенд, SQLite store, job scheduler, instance pool, admin limiter, signed URLs, multi-backend routing |
| Workflow Studio | RzMY/Comfyui2api 9c3f110 |
загрузка workflow из UI, fields, mapping editor, inline test; перенесён модульно (merge не чистый — 15 конфликтов) |
| Граф-рандом seed | xundaoai/Comfyui2api | _randomize_noise_seeds() на KSampler/SamplerCustom/RandomNoise, адаптирован под нашу fail-soft модель (не трогает явный seed) |
| i18n + Docker/CI/Compose | maydayoffice/ComfyUI2API (Вариант A) |
runtime i18n на семантических ключах с динамическим набором языков: i18n/apply_i18n.py (coverage-верификатор, авто-обнаруживает locales/*.json) + словари frontend/src/lib/locales/{en,ru,zh}.json (~286 ключей, en — fallback, новые языки добавляются без правки кода), Dockerfile (multi-stage), .forgejo/workflows/ci-cd.yml, docker-compose.yml, .env.example, .dockerignore |
| Лицензия | MIT (все форки) | см. LICENSE |
🛠 Разработка
# бэкенд
python -m venv .venv && . .venv/bin/activate
pip install -e .
python -m unittest discover -s tests -v
# фронтенд (в коде — только семантические ключи t("msg_N"); перевод в рантайме)
cd frontend && pnpm install && pnpm build # typecheck
python ../i18n/apply_i18n.py frontend # i18n key coverage
Новая строка UI: возьмите следующий свободный
msg_N, добавьте его во все словариfrontend/src/lib/locales/<code>.jsonи вызывайтеt("msg_N"). Для параметризованной строки используйтеt("msg_N", { x: value })и{x}в шаблоне.Новый язык: положите файл
frontend/src/lib/locales/<code>.jsonс тем же набором ключей (плюс обязательныйnative_name— название языка собственными словами). Он подхватится автоматически (import.meta.glob) в выпадающем меню, а верификатор начнёт требовать его полноты.en.json— язык fallback: недостающие в других языках ключи показываются на английском.
AGENTS.md — рабочие правила для AI-агентов (в т.ч. как правильно сделать
re-sync с opq007/main).
📚 Лицензия
MIT (наследуется от opq007/Comfyui2api, RzMY, xundaoai — все форки MIT).