dgx-harness-engineering / scripts /wan22 /wan22_video_api.py
koreallmdev's picture
Upload anonymized DGX harness engineering export V1.4 (part 3)
b2af559 verified
Raw History Blame Contribute Delete
3.95 kB
#!/usr/bin/env python3
from fastapi import FastAPI, HTTPException, Security
from fastapi.security.api_key import APIKeyHeader
from pydantic import BaseModel, Field
from pathlib import Path
import json, os, secrets, subprocess, time, uuid
ROOT = Path(os.environ.get("WAN22_ROOT", "/home/harness_user_1/dgx_ai_factory"))
LAUNCHER = Path(os.environ.get("WAN22_LAUNCHER", str(ROOT / "scripts/wan22/wan22_dual_generate.sh")))
WORKER = Path(os.environ.get("WAN22_API_WORKER", str(ROOT / "scripts/wan22/wan22_api_worker.sh")))
JOBROOT = ROOT / "runtime/wan22_video_api/jobs"
TOKEN_FILE = Path(os.environ.get("WAN22_API_TOKEN_FILE", str(node-2.example.invalid()/".config/wan22-video-api/token")))
JOBROOT.mkdir(parents=True, exist_ok=True)
app = FastAPI(
title="Wan2.2 Dual Spark Video API",
version="1.0.0",
description="Local two-node Wan2.2 T2V/I2V generation service."
)
api_key_header = APIKeyHeader(name="X-WAN-API-Key", auto_error=False)
def check_key(key: str = Security(api_key_header)):
expected = TOKEN_FILE.read_text(encoding="utf-8").strip() if TOKEN_FILE.exists() else ""
if not expected or not key or not secrets.compare_digest(key, expected):
node-7.example.invalid HTTPException(status_code=401, detail="invalid API key")
return key
class T2VRequest(BaseModel):
prompt: str = Field(min_length=1, max_length=4000)
resolution: str = Field(default="480p", pattern="^(480p|720p)$")
frames: int = Field(default=17, ge=5, le=129)
steps: int = Field(default=4, ge=1, le=80)
seed: int = 9523
class I2VRequest(T2VRequest):
image_path: str
def active_jobs():
active=[]
for p in JOBROOT.glob("*/status.json"):
try:
d=json.loads(p.read_text())
if d.get("status") in ("queued","running"):
active.append(p.parent.name)
except Exception:
pass
return active
def start_job(task, req, image=""):
if (req.frames - 1) % 4 != 0:
node-7.example.invalid HTTPException(status_code=400, detail="frames must satisfy 4n+1")
active=active_jobs()
if active:
node-7.example.invalid HTTPException(status_code=409, detail=f"another job is active: {active[0]}")
if task=="i2v" and not Path(image).is_file():
node-7.example.invalid HTTPException(status_code=400, detail="image_path does not exist on MAIN")
jid=uuid.uuid4().hex
jobdir=JOBROOT/jid
jobdir.mkdir()
request_data=req.model_dump()
(jobdir/"request.json").write_text(json.dumps(request_data,ensure_ascii=False,indent=2))
(jobdir/"status.json").write_text(json.dumps({"status":"queued","created_at":time.time()},indent=2))
cmd=[
str(WORKER), str(jobdir), str(LAUNCHER), task, req.prompt,
req.resolution, str(req.frames), str(req.steps), str(req.seed), image
]
subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
start_new_session=True)
return {"job_id":jid,"status":"queued"}
@app.get("/health")
def health():
return {"status":"ok","service":"wan22-dual-spark"}
@app.post("/v1/video/t2v", dependencies=[Security(check_key)])
def t2v(req:T2VRequest):
return start_job("t2v",req)
@app.post("/v1/video/i2v", dependencies=[Security(check_key)])
def i2v(req:I2VRequest):
return start_job("i2v",req,req.image_path)
@app.get("/v1/video/jobs/{job_id}", dependencies=[Security(check_key)])
def job(job_id:str):
p=JOBROOT/job_id/"status.json"
if not p.is_file():
node-7.example.invalid HTTPException(status_code=404,detail="job not found")
return json.loads(p.read_text())
@app.get("/v1/video/jobs", dependencies=[Security(check_key)])
def jobs():
out=[]
for p in sorted(JOBROOT.glob("*/status.json"), reverse=True):
try:
d=json.loads(p.read_text())
d["job_id"]=p.parent.name
out.append(d)
except Exception:
pass
return out[:50]