🚀 Fleet AHV — Sơ đồ & Chuẩn đặt tên

Cập nhật 11/07/2026. Nguồn chuẩn: /home/claudeproxy/Claude/ahv_fleet_runbook.md trên orchestrator. Giờ hệ thống toàn fleet = Asia/Ho_Chi_Minh (+7); riêng DB PostgreSQL giữ UTC nội bộ.

Chuẩn đặt tên (BẮT BUỘC)

  • Node = n<octet-cuối-IP> — 1:1 với IP, không bao giờ nhầm. Node mới .200 → n200.
  • Backend = {tính-năng}-{node} (external = ext-{provider}). Token tính năng: tryon, gen, swap, upscale, video, remove, rembg, scan, nsfw.
  • Category (API user gọi — KHÔNG đổi): try_on, ahv_image, faceswap, faceswap_video, upscale, remove_object, remove_background, scan_objects, nsfw_check + external.

Sơ đồ node hiện tại (5 node GPU + orchestrator)

NodeIPGPUBackend đang chạyGhi chú
n131103.253.21.13148G RTX PRO 5000tryon-131(6), gen-131, video-131(8), swap-131, upscale-131, swapprem-131Workhorse; node duy nhất đủ VRAM cho premium video (pilot)
n81103.253.21.8124G RTX PRO 4000tryon-81(2), gen-81, swapprem-81FLUX-ONLY (12/07: fsimage+fsvideo stopped — backend disabled thì service phải tắt; bật video-81 = start faceswap-video TRƯỚC)
n239103.253.21.23924G RTX PRO 4000tryon-239(1), gen-239, video-239(2), swapprem-239(OFF)Node nguồn rsync; fsimage stopped 12/07 (mồ côi 8.2G gây OOM tryon)
n183178.63.96.18320G (Hetzner)remove-183(:2031 diffusion), swap-183, upscale-183, rembg/scan/nsfw-183(:2026)Node phụ trợ — KHÔNG chạy flux
n22161.248.81.2224G RTX PRO 4000tryon-22(2), swapprem-22Vào trận 11/07 giữa sự cố quá tải; đủ 4 stack trên disk
Orchestrator15.235.200.66FastAPI :2022, PostgreSQL, JOB_WORKERS=80×4SPOF — cần dự phòng khi scale

Nguyên tắc thiết kế: (1) mọi node đủ mọi stack trên DISK — cái gì "nóng" trên VRAM do vận hành quyết theo tải; (2) thêm tính năng = thêm 1 stack + 1 category backend, không sửa kiến trúc; (3) thêm node = chạy onboard script; (4) mọi chuyển dịch kiểu "bật mới song song → verify job thật → tắt cũ", không gián đoạn.

💎 Premium tier (premium: 0/1) — LIVE 12/07/2026

Thiết kế: API cũ giữ nguyên 100%. Client thêm field premium (int, mặc định 0) vào payload. premium:0/thiếu = bản thường; premium:1 = bản cao cấp. BE tính giá theo cờ này.

Faceswap ẢNH premium (ĐÃ LIVE)

POST /v1/jobs {"task":"faceswap","payload":{
  "target_url":"...",   # ảnh gốc (người)
  "source_url":"...",   # ảnh mặt
  "premium": 1          # 1 = head-swap 9B (đổi cả ĐẦU + TÓC); 0/thiếu = inswapper cũ
}}
  • Routing: orchestrator workers.py dispatch_task → category nội bộ faceswap_premium (swapprem-131/81/22 ON; swapprem-239 OFF — node chật). faceswap_multi LUÔN đi bản cũ.
  • Engine: flux service (:1994) endpoint POST /head_swap — FLUX.2-klein-9B + LoRA bfs_head (adapter thứ 2 cạnh tryon; thiếu file LoRA → node tự fallback, /head_swap trả 503, try_on KHÔNG ảnh hưởng). LoRA: /opt/flux-klein-tryon/lora/bfs/bfs_head_v1_flux-klein_9b_step3750_rank64.safetensors (đủ 4 node).
  • Số đo (stress 2 vòng 12/07): ~14s/ảnh model nóng; 21/21 premium succeeded; premium:0 regression nguyên vẹn; premium chen 1 slot semaphore flux → try_on chậm nhẹ khi premium dồn (chấp nhận, premium ít lượng).
  • Demo test: /public/webhook-demo.html#faceswap → ô "Chất lượng" chọn Premium.
  • Rollback tức thì: PUT enabled=false các swapprem-* (premium:1 sẽ lỗi "No available backends", premium:0 không ảnh hưởng).

Faceswap VIDEO premium (PILOT — chờ duyệt mở)

  • Công thức chốt: ComfyUI + Wan2.2 Bernini-R fp8 gốc (tải bằng HF_HUB_DISABLE_XET=1 — chiêu né xet stall VN) + BFS wan22 LoRA + Wan2.1 VAE 16ch (dùng nhầm wan2.2_vae 48ch = lỗi "48 vs 16") + node wrapper BerniniRV2VSimple; hybrid mode (HI CFG3.5 amplify + LO turbo CFG1).
  • Số đo model nóng: 3s clip = ~2-3 phút; 5s ≈ 5 phút; 10s ≈ 6-13 phút (thời gian ≈ tỉ lệ độ dài). VRAM 24-36G → cần node 48G. Trần độ giống mặt ~70-80% (giới hạn LoRA community — chờ V4). Script: run_bfs.py trên n131 (argv: guide head prefix frames steps mode strength quant).

Quy tắc VRAM & Xếp hình

  • flux (FLUX.2-klein-9B NF4, phục vụ cả /tryon/generate trên :1994) load-peak 14.7G, steady ~13G.
  • fs-image 8.8G; video 9.5G; diffusion remove ~3.5G; upscale nằm trong fs-image.
  • Node 24G: CHỈ flux + phụ ≤5G. flux + fs-image = OOM (thiếu 300MB — đã đo thật). Node 20G: không chạy flux. Chỉ n131 48G ôm được nhiều.
  • Thứ tự bật trên node ghép: flux load TRƯỚC (cần peak), dịch vụ khác SAU.
  • Mọi node flux PHẢI có drop-in PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True (chống VRAM phình → 503).
  • Video (inswapper) slot = free_VRAM/1.5G VÀ ≤3-4/node-24G (giới hạn phiên NVENC) — quá slot = lỗi "NVENC stream encode failed" / ONNX init OOM (đo thật 11-12/07).
  • Service bloat theo giờ chạy (flux >20G, fsvideo phình ~2-8h/lần khi tải nặng) → restart node đó lúc vắng khi free <2G.
  • Backend enabled ⇔ service running — backend tắt thì TẮT service (mồ côi ăn VRAM: n81 15.5G + n239 8.2G phát hiện 12/07); backend bật thì service PHẢI chạy (video-81 4 job fail 12/07).
  • KHÔNG sync config.py giữa các node (BASE_URL per-node — sự cố 12/07: 76 job link 404 trong 40 phút). Chỉ sync code (server.py/pipeline.py).
  • KHÔNG tải file lớn (model) trên node đang production — bão hòa băng thông làm HTTP/SSH treo (sự cố 11h15 12/07). Tải lúc drain/khuya hoặc --limit-rate.
  • GPU 100% util thì nâng slots là VÔ ÍCH (thrash) — chỉ có thêm node hoặc kịch bản ưu tiên.

Lệnh vận hành nhanh (từ orchestrator)

# Backend API (basic auth admin:***)
GET/POST/PUT http://127.0.0.1:2022/admin/api/backends   # POST=thêm, PUT=sửa {category,name,...}
# Bật tính năng X trên node N
systemctl start <unit> → ufw allow port → curl test → POST backend slots nhỏ → theo dõi job thật → tăng slots
# Tắt an toàn: PUT enabled=false TRƯỚC (drain) → systemctl stop
# Restart flux 1 node an toàn (flux :1994 phục vụ CẢ tryon + gen!):
#   disable cả tryon-X lẫn gen-X → drain ~20s → restart → health 200 → enable lại
  • KHÔNG restart orchestrator lúc tải cao (fail job đang chạy) — đổi env qua systemd drop-in, restart lúc khuya vắng.
  • SSH mọi node: key gpu_admin (password login đã TẮT trên n22, nên tắt dần các node cũ). Key tạm onboard phải xoá sau khi xong.
  • Admin panel Server: danh sách node ở /opt/ai-orchestrator-2022/collect_server_metrics.py — thêm node mới vào SERVERS.

⚡ Playbook sự cố (đã kiểm chứng thực chiến)

1. try_on quá tải (khách kêu chậm)

Ngưỡng vào kịch bản: p50>58s & p95>90s & running>90 building 2 nhịp — HOẶC khách kêu + p50>60s kéo dài. Lưu ý: p50/p95 vọt nhưng running thấp (<30) = burst tự tan (mẫu ~20h18 hằng đêm), KHÔNG can thiệp.

BẬC 1: PUT enabled=false gen-81 + gen-239   # dồn GPU n81/n239 cho try_on
BẬC 2: PUT enabled=false swap-131            # dồn n131 (faceswap vẫn chạy qua swap-183)
TAN (p50<40 & running<25): bật lại NGƯỢC bậc: swap-131 → gen-239 → gen-81

Thực chiến 10-11/07: p50 94s→29.5s trong ~90 phút (BẬC 1+2 + đưa n22 vào cấp tốc), 0 job mất.

2. 503 hàng loạt cùng 1 backend (≥3/nhịp)

= OOM node đó. Xem nvidia-smi; flux phình VRAM → restart flux node đó (quy trình drain ở trên); kiểm expandable_segments.

3. Link ảnh 404

Nguyên nhân kinh điển: (a) BASE_URL node trỏ sai IP (rsync đè config.py — bài học n22); (b) retention xoá sớm (đã chuẩn 24h mọi node). File thật luôn nằm trên node XỬ LÝ — cứu link đã phát bằng cách rsync files/ sang node có IP trong link.

4. disconnect/502 lẻ tẻ

1 lần đơn lẻ = transient, bỏ qua sau khi probe health OK. CÙNG backend ≥2/15 phút → probe, chết/chậm → restart service node đó.

5. Video quá tải / lỗi NVENC-ONNX

Video demand >25 running + video-131 lỗi → bật video-81: ssh n81 systemctl start faceswap-video TRƯỚC → PUT video-81 enabled slots 3. Node nào NVENC/ONNX >3/5phút → giảm slot node đó (free/1.5G) hoặc restart faceswap-video node đó (drain→restart→re-enable). Hết đỉnh → tắt video-81 (PUT off + stop service) trả VRAM cho try_on. Thực chiến 11/07: demand 75 → 3 node slot-theo-VRAM → 0 fail.

6. Job kẹt / metric running phồng ở đỉnh

Watchdog 900s chạy trễ dưới tải (coroutine đói — mục Audit 6/7). Job không leak vĩnh viễn (recover lúc boot). Fix triệt để cần sửa server.py + restart orchestrator lúc vắng (đang chờ duyệt).

Onboard node GPU mới

bash /home/claudeproxy/Claude/onboard_node.sh <IP>   # (node đã nhận key gpu_admin)

Script tự làm: rsync 4 stack từ n239, copy systemd units, libnvinfer, set BASE_URL/FSV_PUBLIC_BASE=IP mới, expandable_segments, ufw. SAU KHI rsync ALL_DONE — 2 việc BẮT BUỘC (bài học n22 10-11/07):

  1. Chạy lại bước set BASE_URL — rsync ĐÈ config.py bằng bản node nguồn → link sẽ trỏ sai IP.
  2. Kiểm Python: venv build cho python3.10; node OS mới (3.12) phải apt install python3.10 (deadsnakes) + ln -sf /usr/bin/python3.10 venv/bin/python3.

Rồi: start flux → health 200 → POST backend tryon-<octet> slots 2 → theo dõi job thật (link đúng IP!) → xoá key tạm onboard_tmp → thêm vào SERVERS admin panel → tắt password SSH.

Giám sát & Ngưỡng

Triết lý: im lặng khi xanh (LowNoise), chỉ báo khi: vượt ngưỡng/sự cố đã tự xử lý xong/cần quyết định. 1 chuỗi giám sát DUY NHẤT (token-dedup file .ahv_monitor_token, mỗi nhịp rotate — chuỗi lạ tự chết im lặng). Nhịp thích ứng: đỉnh 15-25 phút, xanh 50-55 phút.

  • Khung tải VN (+7): sáng 7-9h, trưa 11-14h, tối 19-22h nặng nhất (+burst ~20h18 tự tan), volume đang TĂNG từng ngày kể cả khuya.
  • Lỗi INPUT — KHÔNG tính sự cố hệ thống: no-face, mask trống, 400/404, NSFW_BLOCKED, "Failed to download image" (storage 5.223.78.252 xoá input ngay — lỗi try_on nhiều nhất), "image file is truncated".
  • Đọc node lỗi bằng field backend trong error JSON (tên chuẩn) — ĐỪNG tin text "VN2 try-on" (label cũ hardcode).
  • DB timestamp = UTC (giờ máy = VN+7). Node xử lý ≠ node lưu file.

📚 Bài học sự cố (đọc trước khi vận hành)

NgàySự cốBài học
04-05/07remove_object LaMa → diffusionNâng cấp kiểu song song + fallback; đo A/B 10 case trước khi chốt
06/07Link 404 hàng loạt2 lỗi chồng nhau: retention 1h quá ngắn (→24h) + BASE_URL .81 sai. Fix cả gốc lẫn onboard script
06/07Audit 2 vòngBug latent (FSV_PUBLIC_BASE .183, flux .183 còn enabled) phải quét chủ động, đừng chờ nổ
10-11/07try_on quá tải kỷ lục (p50 108s, p95 912s)3 node bão hòa thật sự → BẬC 1+2 + n22 cấp tốc. 3 bug khi onboard gấp: rsync đè BASE_URL, python 3.12 vs venv 3.10, log DONE giả do kill vòng lặp (luôn dùng exit-code marker)
11/07Video SPOF: drain n131 test → 305 job video "no backend"Video giờ ≥2 node (video-131+video-239); TRƯỚC khi rút node khỏi category 1-node phải bật dự phòng; test GPU ban ngày chỉ khi có node gánh
11-12/07Video đỉnh trưa 70+ concurrent, NVENC/ONNX failSlot video = free/1.5G và ≤3-4/node (NVENC session limit); fsvideo bloat theo giờ → restart lúc vắng
12/07Premium ảnh deploy — 3 bug tự bắt trong audit(a) KHÔNG sync config.py giữa node (BASE_URL — 76 job 404/40min); (b) service mồ côi ăn VRAM 2 node (n81 15.5G, n239 8.2G → OOM tryon-239); (c) backend enabled ⇔ service running. Tải model lớn trên node production làm nghẽn băng thông (HTTP/SSH treo ~10min)
09-10/07Đánh giá FLUX 4B vs 9BNF4 trên card này: 4B KHÔNG nhanh hơn 9B (nghẽn băng thông), chất lượng thấp hơn → gen/try_on giữ 9B; 4B chỉ đáng cho head_swap (VRAM 4.2G); giảm bước 8→4 mới là đòn bẩy tốc độ thật (distilled)

Việc treo chờ quyết: mở bán premium video (pilot xong, cần node 48G riêng nếu số lượng), watchdog fix, retention bảng tasks, label "VN2" hardcode, cache input chống download-fail, orchestrator dự phòng (SPOF). ĐÃ XONG 12/07: premium ảnh 9B (live), video redundancy 2 node.

AHV AI Orchestrator v2.0

Hệ thống điều phối AI đa tác vụ – quản lý hàng đợi, phân phối backend GPU, webhook callback, và giám sát thời gian thực.

Stack

Python 3.10+ · FastAPI · Uvicorn (uvloop + httptools) · PostgreSQL (psycopg async) · Systemd

Architecture

Client ──► Nginx (SSL/Cloudflare) ──► FastAPI (4 workers, uvloop) │ ├─► asyncio.Queue ──► 40 Job Workers ──► GPU Backends │ ├─► PostgreSQL (jobs, ema_predictions, webhook_logs) │ ├─► SSE Stream ──► Dashboard (realtime) │ └─► Webhook (3 retries, exponential backoff)

Job Processing Flow

  1. Client gửi POST /v1/jobs với task, payload, webhook_url
  2. Job lưu DB với status queued, trả về job_id + predict_time (EMA)
  3. Webhook phase 1: gửi event queued
  4. Worker lấy job, chọn backend GPU (round-robin + semaphore)
  5. Webhook phase 2: gửi event started
  6. Backend xử lý xong → trả kết quả
  7. Webhook phase 3: gửi event finished với URL + meta
  8. EMA predictor tự học từ duration thực tế

Installation & Setup

Requirements

  • Python 3.10+
  • PostgreSQL 14+
  • Nginx (reverse proxy, SSL)
  • Linux with Systemd

Quick Start

cd /opt/ai-orchestrator-2022
pip install -r requirements.txt

# Create database
sudo -u postgres createdb ahv_orchestrator
sudo -u postgres psql -c "CREATE USER ahv_user WITH PASSWORD 'ahv_pass_2026';"
sudo -u postgres psql -c "GRANT ALL ON DATABASE ahv_orchestrator TO ahv_user;"

# Run (development)
uvicorn server:app --host 0.0.0.0 --port 2022 --reload

# Run (production)
sudo systemctl start ai-orchestrator-2022
sudo systemctl enable ai-orchestrator-2022

Service Management

sudo systemctl status ai-orchestrator-2022   # View status
sudo systemctl restart ai-orchestrator-2022  # Restart (~2s)
sudo systemctl reload ai-orchestrator-2022   # Zero-downtime reload
journalctl -u ai-orchestrator-2022 -f        # Live logs

Environment Variables

Database

VariableDefaultDescription
DB_HOSTlocalhostPostgreSQL host
DB_PORT5432PostgreSQL port
DB_NAMEahv_orchestratorDatabase name
DB_USERahv_userDatabase user
DB_PASSWORDahv_pass_2026Database password

API & Auth

VariableDefaultDescription
API_KEYanhyeuemAPI key for clients
ADMIN_PASSAnhyeuemAdmin panel password (Basic Auth)

Timeout & Performance

VariableDefaultDescription
REQUEST_TIMEOUT300Backend call timeout (seconds)
CONNECT_TIMEOUT15Connection timeout (seconds)
JOB_MAX_RUNTIME900Max job runtime before watchdog kills it
MAX_QUEUE_SIZE1000Maximum queue capacity
JOB_WORKERS40Concurrent worker count

Webhook

VariableDefaultDescription
WEBHOOK_TIMEOUT12Webhook delivery timeout (seconds)
WEBHOOK_RETRIES3Max retry attempts
WEBHOOK_BACKOFF_BASE0.6Exponential backoff base (seconds)
WEBHOOK_SECRET""HMAC-SHA256 signing secret

EMA Predictor

VariableDefaultDescription
PREDICT_EMA_ALPHA0.25Learning rate (0–1, higher = more responsive)
PREDICT_MIN_SEC3Minimum predicted time (seconds)

Database: Table jobs

Main table storing all job records. Each API call creates one row.

ColumnTypeDescription
job_idTEXT PKUUID unique identifier
taskTEXT indexedTask type: faceswap, try_on, ahv_image, ...
payloadJSONClient-provided parameters
webhook_urlTEXTCallback URL
statusTEXT indexedqueued · running · succeeded · failed · rejected
progressINTEGER0–100
resultJSONSuccess result: {url, filename, meta}
errorJSONError details: {code, message}
created_atTIMESTAMPJob creation time
started_atTIMESTAMPProcessing start time
finished_atTIMESTAMPCompletion time
duration_secFLOATActual processing duration (seconds)
predict_timeINTEGERPredicted duration from EMA (seconds)
eta_utcTIMESTAMPEstimated completion time
attemptsINTEGERRetry attempt count
expires_atTIMESTAMPAuto-delete after this time

Indexes: idx_jobs_created, idx_jobs_expires, idx_jobs_status_created

Database: Table ema_predictions

Stores Exponential Moving Average values for time prediction per task variant.

ColumnTypeDescription
keyTEXT PKTask variant key (e.g. faceswap:up1:ms1080)
valueFLOATCurrent EMA estimate (seconds)
countINTEGERNumber of samples learned
updated_atTIMESTAMPLast update time

EMA Formula: new = alpha × observed + (1 - alpha) × old. Alpha = 0.25 means 25% weight on new observations.

Database: Table webhook_logs

Tracks failed webhook deliveries for debugging. Auto-cleaned after 7 days.

ColumnTypeDescription
idSERIAL PKAuto-increment ID
job_idTEXT indexedRelated job
eventTEXTqueued · started · finished
urlTEXTWebhook URL that failed
attemptINTEGERWhich retry attempt
status_codeINTEGERHTTP status code returned
errorTEXTError message
created_atTIMESTAMPLog timestamp

API: Jobs

POST /v1/jobs — Create Job

Creates a new async processing job.

curl -X POST http://15.235.200.66:2022/v1/jobs \
  -H "Content-Type: application/json" \
  -H "X-API-Key: anhyeuem" \
  -d '{
    "task": "faceswap",
    "payload": {
      "target_url": "https://example.com/photo.jpg",
      "source_url": "https://example.com/face.jpg"
    },
    "webhook_url": "https://your-server.com/webhook"
  }'

Response

{
  "job_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "queued",
  "predict_time": 10,
  "eta_utc": "2026-06-14T10:00:10Z"
}

GET /v1/jobs/{job_id} — Get Job Status

Success Response

{
  "job_id": "a1b2c3d4-...",
  "task": "faceswap",
  "status": "succeeded",
  "progress": 100,
  "result": {
    "url": "http://15.235.200.66:2022/uploads/result.jpg",
    "filename": "result.jpg",
    "meta": {"width": 1080, "height": 1920, "backend": "FSVN1"}
  },
  "duration_sec": 3.5
}

Error Response

{
  "job_id": "a1b2c3d4-...",
  "status": "failed",
  "error": {"code": 502, "message": "Backend timeout after 300s"}
}

API: Upload & NSFW

POST /upload — Upload File

curl -X POST http://15.235.200.66:2022/upload -F "file=@photo.jpg"
{"url": "http://15.235.200.66:2022/uploads/abc123.jpg", "filename": "abc123.jpg"}

POST /v1/nsfw-check — NSFW Check

curl -X POST http://15.235.200.66:2022/v1/nsfw-check \
  -H "Content-Type: application/json" \
  -d '{"image_url": "https://example.com/photo.jpg", "strict": "medium"}'
{"status": "ok", "nsfw_score": 0.02, "is_nsfw": false, "threshold": 0.5, "label": "safe"}

API: Health & Diagnostics

EndpointDescription
GET /healthz{"ok": true, "status": "healthy", "queue_size": 5}
GET /diag/backendsAll backend nodes: slots, in-flight, enabled, last_error
GET /diag/queue{"queued": 10, "running": 5}
GET /statsRecent jobs list (with ?limit= and ?task= filter)
GET /daily-statsDaily aggregation (timezone-aware via ?tz=)
GET /eventsSSE stream for realtime dashboard updates

API: Admin Statistics

Auth Required: All admin endpoints require Basic Auth (username: admin, password: ADMIN_PASS)

EndpointDescription
GET /admin/stats/overviewSummary: total jobs, success rate, avg duration, queue status
GET /admin/stats/hourlyHourly traffic breakdown + avg response time
GET /admin/stats/by-kindJob distribution by task type
GET /admin/stats/daily-trend7-day trend (stacked area chart data)
GET /admin/stats/top-usersTop API users by webhook URL
GET /admin/stats/top-errorsMost common error messages
GET /admin/stats/backend-perfPer-backend performance metrics
GET /admin/stats/exportExport jobs data (?fmt=json or csv)
GET /admin/stats/liveLive counters: queued, running, succeeded, failed
GET /admin/stats/webhook-failuresFailed webhook delivery logs
DELETE /admin/stats/webhook-failuresPurge all webhook failure logs

Query params: ?tz=Asia/Ho_Chi_Minh, ?days=7, ?limit=20

API: Backend Management

EndpointMethodBody
/admin/api/backendsGET
/admin/api/backendsPOST{"category", "name", "url", "slots", "enabled"}
/admin/api/backendsPUT{"category", "name", "url", "slots", "enabled", "new_name"}
/admin/api/backendsDELETE{"category", "name"}
/admin/api/retryGET / POSTGet/set retry counts per task type
/admin/api/tryon-settingsGET / POSTVirtual Try-On default settings
/admin/api/cx-settingsGET / POSTCX Image model settings

Tasks: Faceswap & Upscale

faceswap

Swap a face onto a target image. Timeout: 180s

Required: target_url, source_url

faceswap_multi

Swap multiple faces. Timeout: 180s

Required: target_url, source_urls[]

faceswap_video

Swap face in video, preserves audio. Timeout: 600s

Required: target_url, source_url

upscale

AI image enhancement + upscaling. Timeout: 300s

Required: target_url or image_url

Optional Parameters

ParamDefaultDescription
max_side1080Max output dimension (px)
max_faces0 (all)Limit number of faces to swap
upscale1Enable upscaling (0/1)
restore_face1Enable face restoration (0/1)
sharpen0.12Sharpening strength (0–1)

NSFW Protection: All face tasks automatically run NSFW check. Violations result in status: "rejected" with code NSFW_BLOCKED.

Tasks: Image Generation

ahv_image

Smart-routed text-to-image / image-to-image. Timeout: 300s

Backends: VN2 GPU, CX API, FlowProxy (auto-selected)

gg_image

Google Imagen-4 via FlowProxy. Timeout: 300s

Async polling: submit → poll → download

cx_image

CX/GPT Image API. Timeout: 120s

Direct API call with auto-upload

minimax_image

MiniMax image generation. Timeout: 120s

Supports I2I with image_url

Payload

ParamRequiredDescription
promptYesText description of desired image
image_urlNoSource image for image-to-image
width / heightNoOutput dimensions
aspect_ratioNo1:1, 16:9, 9:16
modelNoModel override (backend-specific)
stepsNoInference steps
guidanceNoGuidance scale
seedNoRandom seed for reproducibility

ahv_image routing: VN2 + no image_url → retries non-VN2. VN2 + image_url → VN2 GPU I2I. CX backend → CX API. FlowProxy → Imagen-4.

Tasks: Video Generation

ahv_video

Image-to-video via Google Veo / FlowProxy. Timeout: 600s

Required: prompt

minimax_video

MiniMax text/image-to-video. Timeout: 600s

Required: prompt

Payload

ParamRequiredDescription
promptYesVideo description
image_urlNoSource image (for I2V)
durationNoVideo duration (default: 6s)
aspect_ratioNo16:9, 9:16, 1:1
fpsNoFrames per second

Tasks: Virtual Try-On

try_on

Virtual clothing try-on. Timeout: 300s

Smart routing: VN2 (sync) / PiAPI (async) / CX / FlowProxy

Payload

ParamRequiredDescription
person_image_urlYesPhoto of the person (aliases: model_input, person_image)
garment_image_urlYesPhoto of garment (aliases: dress_input, garment_image)
top_urlNoUpper garment (for split top/bottom)
bottom_urlNoLower garment (for split top/bottom)
promptNoAdditional instructions

Smart Routing:
VN2 (port 1994) → Direct sync call (300s timeout)
PiAPI/Kling → Async submit + polling
CX → Multi-image prompt approach
FlowProxy → Imagen-4 with try-on prompt

Tasks: Image Tools

remove_object

Remove objects from image. Timeout: 120s

image_url, prompt (describe object)

scan_objects

Detect & label objects. Timeout: 60s

image_url

remove_background

Auto background removal. Timeout: 60s

image_url

nsfw_check

Content moderation check. Timeout: 30s

image_url, strict (low/medium/high)

Backend System

Multi-backend Architecture

Each task type has a category with multiple nodes (GPU servers):

Category: faceswap
├── FSVN1   (30 slots) → http://103.253.21.239:1993/faceswap
└── FSVN48G (30 slots) → http://178.63.96.183:1993/faceswap

Category: try_on
├── TryOn-VN2 (3 slots) → http://103.253.21.239:1994/tryon
└── TryOn-HZ1 (3 slots) → http://178.63.96.183:1994/tryon

Selection Algorithm

  1. Iterate enabled backends in order
  2. Pick first backend with free semaphore slot
  3. If all busy → pick least-loaded backend
  4. Disabled backends → skipped entirely

Backend Class Properties

PropertyTypeDescription
namestrDisplay name (e.g. "FSVN1")
urlstrGPU endpoint URL
slotsintMax concurrent requests
enabledboolActive/inactive toggle
api_keystrPer-node API key (MiniMax, CX, ...)
semSemaphoreRuntime concurrency limiter
total_doneintJobs processed (live counter)
last_errorstrMost recent error (diagnostics)

Hot Reload: Add/edit/delete backends via Admin UI or API — no service restart needed. Config persists in backends_config.json.

Webhook System

3-Phase Event Flow

Job Created ──► {"event": "queued", "status": "queued", "predict_time": 10} Job Running ──► {"event": "started", "status": "running", "progress": 0} Job Done ──► {"event": "finished", "status": "succeeded", "url": "...", "meta": {...}}

Success Payload

{
  "job_id": "a1b2c3d4-...",
  "task": "faceswap",
  "status": "succeeded",
  "event": "finished",
  "progress": 100,
  "url": "http://15.235.200.66:2022/uploads/result.jpg",
  "filename": "result.jpg",
  "meta": {"backend": "FSVN1", "width": 1080, "height": 1920, "duration_sec": 3.5}
}

Error Payload

{
  "job_id": "a1b2c3d4-...",
  "status": "failed",
  "event": "finished",
  "error": {"code": 502, "message": "Backend timeout after 300s"}
}

NSFW Rejected Payload

{
  "job_id": "a1b2c3d4-...",
  "status": "rejected",
  "event": "finished",
  "error": {"code": "NSFW_BLOCKED", "message": "Content violates policy"}
}

Retry & Security

FeatureDetail
Retry count3 attempts
BackoffExponential: 0.6s → 1.2s → 2.4s (+ random jitter)
SignatureX-Signature: sha256=<hex> (HMAC-SHA256, if WEBHOOK_SECRET set)
Failure loggingSaved to webhook_logs table, viewable in Admin UI
Auto cleanupLogs older than 7 days automatically deleted

Auth & Security

API Key Authentication

Path PatternAuth Required
/, /public/*, /upload, /healthzNo
/stats, /events, /tasksNo
/v1/jobs, /v1/nsfw-checkAPI Key (X-API-Key header)
/admin/api/*Basic Auth (admin)

CORS

All origins allowed (*) with credentials support.

NSFW Protection

Auto-applied to: faceswap, faceswap_video, upscale, try_on.

Violation → status: "rejected", code: NSFW_BLOCKED

Web Interfaces

Landing Page

Product overview, features, pricing, code examples

Dashboard

Realtime monitoring via SSE, traffic charts, job list

Admin Panel

Backend management, retry config, stats, webhook logs

API Demo

Test all APIs directly in browser with live preview

API Docs

Complete API reference with examples & error codes

Wiki

This page — full system documentation

File Structure

/opt/ai-orchestrator-2022/
├── server.py              # FastAPI app: routes, DB, queue, webhook, auth, SSE (~1800 lines)
├── workers.py             # Task dispatch: backend calls, normalize responses (~1370 lines)
├── handlers.py            # Helpers: NSFW, webhook retry, EMA, payload builders (~620 lines)
├── backends.py            # Backend registry: config, node management, selection (~430 lines)
├── backends_config.json   # Backend node config (persists across restarts)
├── .env                   # Local environment overrides
├── requirements.txt       # Python dependencies
├── README.md              # Project documentation
│
├── public/
│   ├── index.html         # Landing page
│   ├── dashboard.html     # Realtime monitoring dashboard
│   ├── admin.html         # Admin panel (backend management)
│   ├── webhook-demo.html  # API testing tool
│   ├── docs-webhook.html  # API documentation
│   ├── wiki.html          # System wiki (this page)
│   └── uploads/           # Uploaded files directory
│
└── Systemd: /etc/systemd/system/ai-orchestrator-2022.service

File Responsibilities

FileLinesRole
server.py~1800Main app: FastAPI routes, SQLAlchemy models, asyncio job queue, webhook delivery, auth middleware, SSE events, admin stats endpoints, watchdog
workers.py~1370Task dispatch: route task → backend GPU, HTTP calls, response normalization, async polling (PiAPI, FlowProxy, MiniMax)
handlers.py~620Helpers: NSFW detection, webhook retry with backoff, EMA time prediction, payload validation & building
backends.py~430Backend management: load/save config, Backend class, round-robin selection, semaphore concurrency control

Deployment & Operations

Systemd Service Configuration

[Service]
ExecStart=uvicorn server:app --host 0.0.0.0 --port 2022
         --workers 4 --loop uvloop --http httptools
         --timeout-keep-alive 75 --limit-concurrency 2000
         --backlog 8192 --proxy-headers
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=1
TimeoutStopSec=10
KillSignal=SIGINT
LimitNOFILE=65535

Performance Tuning

OptimizationDetail
uvloop + httptools~10% throughput improvement over stdlib
4 Uvicorn workersMulti-core CPU utilization
40 Job workers40 concurrent job processing
DB poolsize=20, max_overflow=40
Graceful reloadsystemctl reload = zero-downtime (SIGHUP)
Fast restart~2 seconds (SIGINT + TimeoutStopSec=10)
CTE queriesAdmin stats use Common Table Expressions

Automatic Maintenance

MechanismIntervalAction
Stuck job watchdogEvery 30sJobs running > 900s → force fail
Expired job cleanupEvery 60sJobs past expires_at → delete
Webhook log cleanupEvery 60sLogs older than 7 days → delete

Monitoring Commands

# Health check
curl http://15.235.200.66:2022/healthz

# Queue status
curl http://15.235.200.66:2022/diag/queue

# Backend health
curl http://15.235.200.66:2022/diag/backends

# Live logs
journalctl -u ai-orchestrator-2022 -f

Developer Guide

⚠ Mandatory Dev Rules (BẮT BUỘC)

Áp dụng cho mọi việc triển khai tính năng / dev / bugfix. Không được báo "pass"/"xong" nếu chưa làm đủ:

  1. Dùng Superpowers để triển khai (brainstorming → writing-plans → TDD → code-review → verification-before-completion).
  2. Audit + review + stress test thật 2 VÒNG mới được báo pass — hai vòng độc lập, có bằng chứng (output thật), không tự nhận pass khi chưa chạy kiểm chứng.
  3. Nếu có giao diện (UI): phải test bằng trình duyệt thật (Playwright/Chromium) trên cả mobile lẫn desktop thật.
  4. Mọi test phải mô phỏng như người dùng thật — đi đúng luồng người dùng, không mock cho qua.

Lý do: ưu tiên độ ổn định và trải nghiệm người dùng tuyệt đối — "người dùng là quan trọng nhất".

Adding a New Backend Node

  1. Go to Admin Panel → select category
  2. Click "Add Backend"
  3. Enter: name, URL, slots, API key (if needed)
  4. Enable → backend is ready immediately (no restart needed)

Adding a New Task Type

Step 1: Add dispatch function in workers.py

async def _dispatch_new_task(payload: dict) -> dict:
    """Call backend, return normalized result."""
    prompt = payload.get("prompt")
    if not prompt:
        raise HTTPException(400, detail="prompt required")

    backend = pick_backend("new_task")
    async with backend.sem:
        async with httpx.AsyncClient(timeout=300) as client:
            resp = await client.post(backend.url, json={"prompt": prompt})

    data = resp.json()
    return {"ok": True, "url": data["result_url"], "meta": {"backend": backend.name}}

Step 2: Register in dispatch_task()

# In workers.py, inside dispatch_task()
if task == "new_task":
    return await _dispatch_new_task(payload)

Step 3: Add fallback estimate in server.py

PREDICT_FALLBACKS = {
    ...
    "new_task": 30,  # default prediction: 30 seconds
}

Step 4: Add backend category & configure nodes via Admin UI

Processing Flow

POST /v1/jobs ↓ server.py: validate → create job in DB → push to asyncio.Queue ↓ server.py: _job_worker() picks from queue ↓ server.py: _process_job() → workers.py: dispatch_task() ↓ workers.py: pick_backend() → call GPU → normalize response ↓ server.py: update DB → send webhook → update EMA predictions

Coding Conventions

ConventionDetail
Normalized responseAll tasks return: {ok, url, filename, meta, error}
Semaphore guardAlways use async with backend.sem: for concurrency
Error handlingUse BackendHTTPException(502, message, backend_name)
Async pollingPattern: submit → poll every 3s → download → re-upload
NSFW checkAdd at start of dispatch for user-facing image tasks
RetryOnly on 502 errors (backend issues), never on 4xx (client errors)

Tip: Test new task types via API Demo page before deploying to production.