Files
microdao-daarion/services/comfy-agent/app/api.py
Apple c41c68dc08 feat: Add Comfy Agent service for NODE3 image/video generation
- Create comfy-agent service with FastAPI + NATS integration
- ComfyUI client with HTTP/WebSocket support
- REST API: /generate/image, /generate/video, /status, /result
- NATS subjects: agent.invoke.comfy, comfy.request.*
- Async job queue with progress tracking
- Docker compose configuration for NODE3
- Update PROJECT-MASTER-INDEX.md with NODE2/NODE3 docs

Co-Authored-By: Warp <agent@warp.dev>
2026-02-10 04:13:49 -08:00

52 lines
2.0 KiB
Python

# services/comfy-agent/app/api.py
from fastapi import APIRouter, HTTPException
from .models import GenerateImageRequest, GenerateVideoRequest, JobStatus
from .jobs import JOB_STORE
from .worker import enqueue
router = APIRouter()
def _build_workflow_t2i(req: GenerateImageRequest) -> dict:
# MVP: placeholder graph; you will replace with your canonical Comfy workflow JSON.
# Keep it deterministic and param-driven.
return {
"1": {"class_type": "CLIPTextEncode", "inputs": {"text": req.prompt, "clip": ["2", 0]}},
"2": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": "sdxl.safetensors"}},
# TODO: Add complete workflow JSON for text-to-image
}
def _build_workflow_t2v(req: GenerateVideoRequest) -> dict:
# MVP placeholder for LTX-2 pipeline; replace with actual LTX-2 workflow.
return {
"1": {"class_type": "CLIPTextEncode", "inputs": {"text": req.prompt, "clip": ["2", 0]}},
# TODO: Add complete workflow JSON for text-to-video with LTX-2
}
@router.post("/generate/image", response_model=JobStatus)
async def generate_image(req: GenerateImageRequest):
job = JOB_STORE.create("text-to-image")
graph = _build_workflow_t2i(req)
enqueue(job.job_id, "text-to-image", graph)
return JOB_STORE.get(job.job_id)
@router.post("/generate/video", response_model=JobStatus)
async def generate_video(req: GenerateVideoRequest):
job = JOB_STORE.create("text-to-video")
graph = _build_workflow_t2v(req)
enqueue(job.job_id, "text-to-video", graph)
return JOB_STORE.get(job.job_id)
@router.get("/status/{job_id}", response_model=JobStatus)
async def status(job_id: str):
job = JOB_STORE.get(job_id)
if not job:
raise HTTPException(status_code=404, detail="job_not_found")
return job
@router.get("/result/{job_id}", response_model=JobStatus)
async def result(job_id: str):
job = JOB_STORE.get(job_id)
if not job:
raise HTTPException(status_code=404, detail="job_not_found")
return job