6 changed files with 989 additions and 1151 deletions
@ -0,0 +1,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 режимы |
||||
@ -1,2 +0,0 @@
@@ -1,2 +0,0 @@
|
||||
# ЭТОТ ФАЙЛ БОЛЬШЕ НЕ ИСПОЛЬЗУЕТСЯ — удали его |
||||
# API работает напрямую через Python на порту 8080 |
||||
Loading…
Reference in new issue