Cephalon Cloud → ComfyUI Video Generation API Pipeline
Complete runnable scripts (this repository scripts/):
lucy_run.py— Workbench direct CLI (recommended route, no instance): WeChat scan login → SSO session creation → zero-cost quote → locked-price submission (idempotency key prevents double deduction) → polling → sha256 verification download → image asset upload. All four templates (t2v/i2v/fl2v/r2v) verified through execution on 2026-10-14.cephalon_api.py— user-center HMAC signing + instance (missions) CLIcephalon_verify.py— verification pipeline;cephalon_keys.py— key template (env variable priority)run_fairy_pipeline.py/concat_fairy.py— self-built instance multi-shot pipeline (old route)
Lucy Workflow Pipeline (lucy-agent image workflow, WeChat scan login, video.lucy.run Workbench quoting and real execution): See references/lucy-workflow-api.md.
Workbench Direct Quick Start (lucy_run.py)
export CEPHALON_PUBLIC_KEY=<32-byte hex> CEPHALON_PRIVATE_KEY=<64-byte hex>
python scripts/lucy_run.py login # WeChat scan, JWT saved to lucy_state/token.txt
python scripts/lucy_run.py balance # Balance (100 units = 1 CEP)
python scripts/lucy_run.py upload first_frame.png # Required for i2v/fl2v/r2v, returns asset_id
python scripts/lucy_run.py submit --template t2v --seconds 5 --yes \
--prompt "…… overall_soundscape: …… non_diegetic_music: None."
python scripts/lucy_run.py download <run_id>
Red line: Priced at 100 units/second (5s = 5 CEP, 10s = 10 CEP, 15s = 15 CEP); deduction happens on submission, cannot be cancelled after running; check the script's printed quote before submitting. Parameter details and gotchas are in the reference.
Authentication (verified, documentation has pitfalls)
- HMAC-SHA256. Signing string =
JSON(relative_uri + normalized body), key = private key (64-byte hex), request headersHmac-Key: public key (32-byte hex)+Hmac. - Critical pitfall: requestURI is the relative path relative to the user-center base path (e.g.,
/v1/load-balance), not the/cephalon/user-center/v1/...prefix shown in doc links. Including the prefix always yields 401 (empty body transport layer rejection, no error message). - Body normalization:
json.dumps(..., sort_keys=True), must be identical to the request body. - 401 (empty msg) = HMAC signing failure;
code 40000 "Login state expired"= no auth header; 404 = path does not exist. Use these three to calibrate. - Swapping public/private keys also yields 401. The script includes automatic retry with swapped keys.
- GET endpoint query parameters: parameter names in snake_case (page_index/page_size), but double-parameter query always returns 401 (server limitation, single parameter returns 200). mission_list with query parameters is unusable; skip it; use only create/status/access/close.
- POST/PUT endpoints: signing string = URI + normalized body JSON string (verified through testing).
API Endpoints (base: https://prod.unicorn.org.cn/cephalon/user-center)
- POST
/v1/user/missions/batchbody:{batch_size, type, gpu_version, gpu_num}→data: [Snowflake IDs] - GET
/v1/user/missions/{id}→ status/urls (no pagination parameters needed, single ID query works) - PUT
/v1/user/missions/close/batchbody:{ids: [...]} - GET
/v1/load-balance(no parameters) → list of user load balancing services - GET
/v1/load-balance/access-address/{id}→ frpc_address/fb_address
Instance Lifecycle (Billing)
- waiting (no charge) → doing (accepted but not running) → supplying (billing active, RTX4090 verified unit_cep=36 CEP/hour, confirmed on 2026-09-29 bill) → succeed/failed/canceled.
- Readiness typically 5-8 minutes (waiting ~5 min + doing ~2 min).
- The
urlsfield appears only after creation (["https://wp08.unicorn.org.cn:PORT", ".../fb"]). URLs are HTTPS + non-standard port, self-signed certificates (requests/urllib must disable certificate verification). - During doing phase, urls may not yet be issued: The create script exiting with "failed to get access address" is normal timing; wait 1-2 minutes and re-run create (the script's orphan prevention logic automatically reuses the instance and completes the base URL). Do not create new instances repeatedly.
- The mission status response includes
username/passwordfields (instance Web UI credentials, e.g., admin/cephalon); use these to log into the instance UI; unrelated to HMAC keys. - Must call the close interface after use, otherwise idle runtime burns CEP. After closing, update the local cephalon_state.json to succeed synchronously to avoid misjudging orphan instances next time. A single task bill is approximately 2124 units (≈21.24 CEP), including ~1 hour of supplying + model loading and generation; second verification on 2026-09-30: tight workflow (close immediately after generation) from creation to closure took only ~24 minutes, estimated ~14 CEP—key to saving money: close immediately after generation completes.
Orphan Instance Prevention (learned the hard way, actually burned ~1 hour of CEP on 2026-09-28)
- Persist the mission_id to disk immediately after successful creation, before entering the waiting loop. If the loop is killed by timeout/interruption, the ID won't be lost, preventing orphans.
- At the start of create, check the state file: if there is already a waiting/doing/supplying instance, reuse it; do not create a new one.
- Before submit, check instance status; reject submission if not supplying/doing.
- Once an orphan ID is lost, the user can only manually stop it in the Cephalon Cloud console (Snowflake IDs cannot be enumerated).
ComfyUI API (after instance is ready)
- POST
/promptbody:{"prompt": {API format workflow}, "client_id": "..."}→ prompt_id - GET
/history/{prompt_id}→ status.completed + outputs - GET
/view?filename=...&subfolder=...&type=output→ download artifacts - GET
/object_info(11MB) → all node definitions; GET/object_info/{NodeName}for a single node. - API format:
{node_id: {class_type, inputs: {param: value or [from_node, slot]}}}. - Official Wan2.2 template uses new UI nested subgraph format (definitions.subgraphs); the server will error if the subgraph type is not registered. Must inline expand to a flat API format. Template parsing: Wan2.2 T2V = high-noise model (UNETLoader)+lightx2v LoRA+ModelSamplingSD3(shift 5)+KSamplerAdvanced(0→2 steps) → low-noise model+LoRA+KSamplerAdvanced(2→4 steps) → VAEDecode → CreateVideo(fps 16) → SaveVideo. latent uses EmptyHunyuanLatentVideo(480x480x73, batch 1). Text encoder: umt5_xxl_fp8_e4m3fn_scaled.safetensors (CLIPLoader type=wan).
- 4-step generation takes approximately 4.5 seconds for a 480x480 16fps video (RTX4090 verified), from submission to completion ~3-4 minutes (including model loading).
- TI2V 5B official parameters are completely different from 14B (official template video_wan2_2_5B_ti2v.json verified by parsing): single KSampler 20 steps / cfg 5 / uni_pc / simple + ModelSamplingSD3 shift 8 + CreateVideo fps 24; 5B is a single model, no dual UNET high/low noise. 5B has no dedicated distilled LoRA, 4-step route is not applicable.
- TI2V 5B resolution is limited to 1280x704 or 704x1280 (Wan22ImageToVideoLatent width/height step is 32, 720 height is invalid; official "720P" is actually 704). Frame length constraint: (n-1)%4==0.
- Official template defaults to 121 frames = 5 seconds training length; >121 frames risks dynamic degradation, split long scenes into ≤121 frame multi-shots.
- I2V: Wan22ImageToVideoLatent's optional
start_imagetakes IMAGE; first frame uses easy loadImageBase64 (base64_data string input, no multipart upload).
Verified Model Library (3755 nodes)
- Entire Wan2.2 series (T2V/I2V 14B fp8, TI2V 5B fp16), Wan2.1, LTX-2 (19B/22B dev/distilled), LTX-Video 0.9.5, HunyuanVideo 720p, SVD, Mochi, Hunyuan3D-2. TI2V 5B has no dedicated distilled LoRA (357 LoRAs, none for 5b/ti2v); full quality 20-step sampling single shot 720p takes about 5-6 minutes.
- LoRA 357 (including wan2.2 lightx2v 4steps v1.1 high/low noise).
- Detection script: download object_info.json, then locally parse
CheckpointLoaderSimple/UNETLoader/VAELoader/CLIPLoaderchoices + video node existence.
Multi-Shot Chained Final Video (720p verified on 2026-10-01)
- Scripts:
scripts/run_fairy_pipeline.py(create/shot1/shot N/download/close) +build_ti2v_t2v.py/build_ti2v_i2v.py(workflow generators) +concat_fairy.py(xfade concatenation). Spec file: shots_spec.json. - Chaining for continuity: Shot 1 T2V → ffmpeg
-sseof -0.15to extract last frame (JPG q2 ~200KB, DO NOT use PNG—900KB will inevitably be interrupted over tunnel) → each subsequent shot uses the previous shot's last frame as start_image. - Tunnel instability three-piece set: POST /prompt timeout must be 300s (default 60s read timeout can't handle 900KB upload); submit with retry loop x5 (resubmission safe—already queued requests are rejected by server);
socket.setdefaulttimeout(180)to prevent download from hanging indefinitely. If submit response is lost, scan /history to find artifacts by filename prefix. - Shot 3 lesson: After POST was interrupted during the upload phase, the workflow never entered the queue (empty queue + no history record); background polling would loop indefinitely, burning CEP—first check /queue and /history to confirm before retrying.
- 20-step 720p single shot takes about 5-6 minutes generation + 1 minute upload/download; full 8-shot pipeline (including 40 minutes of fault idle time) took about 2 hours of instance rental.
- xfade concatenation: pairwise merge one by one (long filter_complex is hard to debug if one step fails), offset = cumulative duration - 0.5s, final video duration = Σ single shot - (N-1) × 0.5s.
Task Type Enumeration (for 4.1 creation)
comfyui_time (basic) / sd_time / sd_cmd_time (advanced) / fooocus_time / sd_xl_time / jp_time / jp_conda_time / tabby_time / ascend_time / wt_time / ssh_time. Professional ComfyUI requires confirming the type value in the web UI (documentation comments state API defaults to basic).