Files
wan-22-comfy/API-GUIDE.md
T
2026-03-07 20:17:16 +03:00

501 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DaSiWa API Guide
Полное руководство по использованию DaSiWa I2V/FLF2V API для генерации видео через ComfyUI.
---
## 📋 Содержание
1. [Обзор](#обзор)
2. [Аутентификация](#аутентификация)
3. [Endpoints](#endpoints)
4. [Параметры генерации](#параметры-генерации)
5. [Примеры использования](#примеры-использования)
6. [Коды ошибок](#коды-ошибок)
7. [Best Practices](#best-practices)
---
## Обзор
DaSiWa API — асинхронный REST API для генерации видео из изображений с использованием DaSiWa WAN 2.2 Lightspeed моделей через ComfyUI.
**Архитектура:** Submit → Poll → Retrieve (как RunPod)
**Base URL:** `http://<server_ip>:8080`
**Аутентификация:** HMAC-SHA256 с timestamp и nonce
---
## Аутентификация
Все endpoints (кроме `/health`) требуют HMAC подписи.
### Заголовки запроса
```
X-Client-Id: <ваш_client_id>
X-Timestamp: <unix_timestamp>
X-Nonce: <случайная_строка_32_символа>
X-Signature: <hmac_sha256_подпись>
```
### Алгоритм подписи
```python
import hmac
import hashlib
import time
import secrets
timestamp = str(int(time.time()))
nonce = secrets.token_hex(16)
body = json.dumps(payload).encode('utf-8')
message = f"{timestamp}.{nonce}.".encode() + body
signature = hmac.new(
secret_key.encode(),
message,
hashlib.sha256
).hexdigest()
```
### Защита от replay-атак
- **Timestamp:** запросы старше 5 минут отклоняются
- **Nonce:** каждый nonce можно использовать только один раз
- **Signature:** уникальна для каждого запроса
---
## Endpoints
### `GET /health`
Health check сервера. **Не требует аутентификации.**
**Response:**
```json
{
"status": "ok",
"comfyui": "ok",
"queue": 0,
"timestamp": 1234567890
}
```
**Поля:**
- `status` — статус API сервера (`ok` / `error`)
- `comfyui` — статус ComfyUI (`ok` / `unavailable`)
- `queue` — количество задач в очереди
- `timestamp` — текущее время сервера (unix)
---
### `POST /run`
Поставить задачу на генерацию видео в очередь.
**Request Body:**
```json
{
"image_base64": "base64_encoded_image_data",
"prompt": "woman dancing gracefully",
"negative_prompt": "blurry, low quality",
"last_image_base64": "base64_encoded_last_frame",
"width": 528,
"height": 768,
"length": 81,
"steps": 4,
"cfg": 1.0,
"seed": -1,
"fps": 16,
"sampler_name": "euler",
"scheduler": "linear_quadratic"
}
```
**Обязательные поля:**
- `image_base64` — первый кадр (base64)
- `prompt` — текстовое описание
**Опциональные поля:**
- `last_image_base64` — последний кадр для FLF2V режима
- `negative_prompt` — негативный промпт (default: встроенный)
- `width` — ширина (default: 528, кратно 16)
- `height` — высота (default: 768, кратно 16)
- `length` — количество кадров (default: 81)
- `steps` — шаги сэмплинга (default: 4)
- `cfg` — CFG scale (default: 1.0)
- `seed` — сид (-1 = random, default: -1)
- `fps` — кадров в секунду (default: 16)
- `sampler_name` — сэмплер (default: "euler")
- `scheduler` — планировщик (default: "linear_quadratic")
**Response:**
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "IN_QUEUE"
}
```
**Коды ответа:**
- `200` — задача принята
- `400` — ошибка валидации (нет изображения)
- `401` — ошибка аутентификации
---
### `GET /status/<job_id>`
Получить статус задачи.
**Response (IN_QUEUE):**
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "IN_QUEUE"
}
```
**Response (IN_PROGRESS):**
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "IN_PROGRESS"
}
```
**Response (COMPLETED):**
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "COMPLETED",
"output": {
"video": "base64_encoded_video_data",
"seed": 42,
"mode": "I2V",
"elapsed": 45.2
}
}
```
**Response (FAILED):**
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "FAILED",
"error": "Video generation failed — no output from ComfyUI"
}
```
**Коды ответа:**
- `200` — статус получен
- `404` — задача не найдена
- `401` — ошибка аутентификации
---
### `POST /purge/<job_id>`
Удалить завершённую задачу из памяти сервера (освободить RAM от base64 видео).
**Response:**
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"purged": true
}
```
**Коды ответа:**
- `200` — задача удалена
- `400` — нельзя удалить активную задачу (IN_QUEUE / IN_PROGRESS)
- `404` — задача не найдена
- `401` — ошибка аутентификации
---
## Параметры генерации
### Режимы работы
**I2V (Image to Video):**
- Генерация видео из одного изображения
- Передаётся только `image_base64`
**FLF2V (First-Last Frame to Video):**
- Генерация видео между двумя кадрами
- Передаются `image_base64` + `last_image_base64`
### Рекомендуемые значения
| Параметр | I2V | FLF2V | Описание |
|----------|-----|-------|----------|
| `width` | 528 | 528 | Ширина (кратно 16) |
| `height` | 768 | 768 | Высота (кратно 16) |
| `length` | 81 | 81 | Кол-во кадров (~5 сек при 16fps) |
| `steps` | 4 | 4 | DaSiWa оптимизирован под 4 шага |
| `cfg` | 1.0 | 1.0 | CFG scale (DaSiWa работает с 1.0) |
| `fps` | 16 | 16 | Кадров в секунду |
| `sampler_name` | euler | euler | Сэмплер |
| `scheduler` | linear_quadratic | linear_quadratic | Планировщик |
### Ограничения
- **Размеры:** должны быть кратны 16
- **Length:** рекомендуется кратно 8 + 1 (например: 81, 89, 97)
- **Steps:** DaSiWa Lightspeed оптимизирован под 4 шага (можно больше, но медленнее)
- **CFG:** значения > 2.0 могут давать артефакты
---
## Примеры использования
### Python (с библиотекой requests)
```python
import requests
import base64
import json
import time
from hmac_auth import sign_request
# Загрузка ключей
with open('keys.json') as f:
keys = json.load(f)
# Подготовка изображения
with open('photo.png', 'rb') as f:
image_b64 = base64.b64encode(f.read()).decode()
# Payload
payload = {
"image_base64": image_b64,
"prompt": "woman dancing gracefully",
"width": 528,
"height": 768,
"length": 81,
"steps": 4,
"cfg": 1.0,
"seed": -1,
"fps": 16
}
# 1. Submit job
body = json.dumps(payload).encode('utf-8')
auth_headers = sign_request(body, keys['secret_key'], keys['client_id'])
headers = {'Content-Type': 'application/json', **auth_headers}
response = requests.post(
'http://server:8080/run',
data=body,
headers=headers
)
job_id = response.json()['id']
print(f"Job ID: {job_id}")
# 2. Poll status
while True:
auth_headers = sign_request(b"", keys['secret_key'], keys['client_id'])
response = requests.get(
f'http://server:8080/status/{job_id}',
headers=auth_headers
)
data = response.json()
if data['status'] == 'COMPLETED':
video_b64 = data['output']['video']
video_bytes = base64.b64decode(video_b64)
with open('output.mp4', 'wb') as f:
f.write(video_bytes)
print(f"Video saved! Seed: {data['output']['seed']}")
break
elif data['status'] == 'FAILED':
print(f"Error: {data['error']}")
break
else:
print(f"Status: {data['status']}")
time.sleep(5)
# 3. Purge job
auth_headers = sign_request(b"{}", keys['secret_key'], keys['client_id'])
requests.post(
f'http://server:8080/purge/{job_id}',
json={},
headers={'Content-Type': 'application/json', **auth_headers}
)
```
### cURL
```bash
# 1. Submit job
curl -X POST http://server:8080/run \
-H "Content-Type: application/json" \
-H "X-Client-Id: your_client_id" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Nonce: $(openssl rand -hex 16)" \
-H "X-Signature: <calculated_signature>" \
-d '{
"image_base64": "...",
"prompt": "woman dancing"
}'
# Response: {"id": "abc-123", "status": "IN_QUEUE"}
# 2. Check status
curl http://server:8080/status/abc-123 \
-H "X-Client-Id: your_client_id" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Nonce: $(openssl rand -hex 16)" \
-H "X-Signature: <calculated_signature>"
# 3. Purge
curl -X POST http://server:8080/purge/abc-123 \
-H "Content-Type: application/json" \
-H "X-Client-Id: your_client_id" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Nonce: $(openssl rand -hex 16)" \
-H "X-Signature: <calculated_signature>" \
-d '{}'
```
---
## Коды ошибок
| Код | Описание | Решение |
|-----|----------|---------|
| `400` | Нет входного изображения | Передайте `image_base64` |
| `401` | Invalid client ID | Проверьте `client_id` в `keys.json` |
| `401` | Invalid timestamp | Синхронизируйте время на клиенте и сервере |
| `401` | Nonce already used | Replay-атака или дублирующий запрос |
| `401` | Invalid signature | Проверьте `secret_key` и алгоритм подписи |
| `404` | Job not found | Job ID не существует или уже удалён |
| `500` | Internal server error | Проверьте логи сервера (`journalctl -u dasiwa-api`) |
---
## Best Practices
### 1. Polling интервал
- **Рекомендуется:** 5-10 секунд
- **Не рекомендуется:** < 2 секунд (нагрузка на сервер)
- Генерация обычно занимает 30-60 секунд
### 2. Timeout
- Установите timeout на polling: 30 минут (1800 секунд)
- Если задача не завершилась за это время — проверьте логи сервера
### 3. Purge после использования
- Всегда вызывайте `/purge/<id>` после получения видео
- Base64 видео занимает ~10-50 MB RAM на сервере
- Без purge память будет расти
### 4. Обработка ошибок
```python
try:
result = wait_for_completion(server, job_id, ...)
except RuntimeError as e:
if "Timeout" in str(e):
# Задача зависла — проверьте сервер
pass
elif "Job failed" in str(e):
# Ошибка генерации — проверьте параметры
pass
```
### 5. Retry логика
- При `401` ошибках — не retry (проблема с ключами)
- При `500` ошибках — retry с exponential backoff
- При `404` на `/status` — задача потеряна, не retry
### 6. Размер изображений
- Оптимально: 528x768 или 768x528
- Большие размеры → больше VRAM → медленнее
- Маленькие размеры → хуже качество
### 7. Seed для воспроизводимости
- Если нужен тот же результат — используйте тот же seed
- Seed из ответа `output.seed` — сохраните для повтора
### 8. Мониторинг очереди
```python
response = requests.get('http://server:8080/health')
queue_size = response.json()['queue']
if queue_size > 5:
print("Очередь большая, ожидайте дольше")
```
---
## Лимиты и производительность
### Текущие лимиты
- **Одновременные задачи:** 1 (1 GPU = 1 задача)
- **Размер очереди:** не ограничен (но рекомендуется < 10)
- **Размер изображения:** max 2048x2048 (теоретически)
- **Длина видео:** max ~300 кадров (ограничено VRAM)
### Производительность
| Параметры | Время генерации | VRAM |
|-----------|-----------------|------|
| 528x768, 81 frames, 4 steps | ~30-45s | ~18 GB |
| 768x528, 81 frames, 4 steps | ~30-45s | ~18 GB |
| 528x768, 161 frames, 4 steps | ~60-90s | ~24 GB |
*Время указано для RTX 4090 / A100*
---
## Troubleshooting
### Задача зависла в IN_PROGRESS
1. Проверьте логи сервера: `journalctl -u dasiwa-api -f`
2. Проверьте ComfyUI: `curl http://localhost:8188`
3. Перезапустите сервис: `systemctl restart dasiwa-api`
### Ошибка "Video generation failed"
- ComfyUI не запущен или недоступен
- Недостаточно VRAM
- Workflow файл повреждён
### Медленная генерация
- Проверьте загрузку GPU: `nvidia-smi`
- Убедитесь что модели загружены в VRAM (первый запрос медленнее)
- Уменьшите `length` или размеры
---
## Changelog
### v2.0 (2026-03-07)
- ✨ Асинхронный API (submit + poll)
- ✨ Endpoints: `/run`, `/status`, `/purge`
- ✨ Background worker thread
- ✨ Queue management
- 🔧 Обновлён на DaSiWa WAN 2.2 Lightspeed
- 🔧 Упрощён workflow (14 нод вместо 50+)
### v1.0 (2026-03-06)
- 🎉 Первый релиз
- ✅ Синхронный `/generate` endpoint
- ✅ HMAC аутентификация
- ✅ I2V и FLF2V режимы