Download scripts/wan22/wan22_video_api.py from koreallmdev/dgx-harness-engineering: direct link, hf CLI and curl.
- Browser
- Download file 3.95 kB
-
https://huggingface.co/koreallmdev/dgx-harness-engineering/resolve/main/scripts/wan22/wan22_video_api.py
- Command line
-
hf download hf://koreallmdev/dgx-harness-engineering/scripts/wan22/wan22_video_api.py
-
curl -L -o wan22_video_api.py https://huggingface.co/koreallmdev/dgx-harness-engineering/resolve/main/scripts/wan22/wan22_video_api.py
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"} | |
| def health(): | |
| return {"status":"ok","service":"wan22-dual-spark"} | |
| def t2v(req:T2VRequest): | |
| return start_job("t2v",req) | |
| def i2v(req:I2VRequest): | |
| return start_job("i2v",req,req.image_path) | |
| 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()) | |
| 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] | |