fix workflow
This commit is contained in:
+500
@@ -0,0 +1,500 @@
|
||||
# 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 режимы
|
||||
Reference in New Issue
Block a user