Real per-step progress bars for local GPU inference
Backend: - local_diffusion.py: add _make_step_cb() that writes step/total_steps/ progress into _states on every diffusers callback_on_step_end; wired into txt2img, inpaint, img2img with TypeError fallback for older diffusers - ai_tools.py: GET /api/generate/progress SSE endpoint — streams _states as JSON array every 200ms so clients get live denoising step counts Frontend: - progress_overlay.js: add connectProgressSSE(pipeType, baseUrl) / disconnectProgressSSE() — opens EventSource, maps step/total_steps to bar percentage (0→85% during denoising, 85→100 for decode/place) - text_to_image.js: connect SSE before POST, disconnect on done/error - selection_actions.js: connect SSE for AI edit / asymmetry operations Result: for local GPU, progress bar shows "Step 12 / 30" with exact fill; for remote providers and upscale (no step callbacks), shimmer animates. https://claude.ai/code/session_01WVDg7amsy1TTtxvpku7bcM
This commit is contained in:
@@ -4,10 +4,12 @@ All endpoints are under /api prefix.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
import base64
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from app.services.local_inpaint import (
|
||||
lama_inpaint, opencv_inpaint, lama_available, gpu_available, rembg_available,
|
||||
@@ -191,6 +193,37 @@ async def inpaint_remote(req: InpaintRemoteRequest):
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/generate/progress")
|
||||
async def generation_progress_stream():
|
||||
"""
|
||||
SSE stream of local GPU pipeline inference progress.
|
||||
Events are JSON arrays of pipeline state objects, emitted every 200 ms.
|
||||
Each object: {pipeline, state, step, total_steps, progress, message, model_id, …}
|
||||
Clients open this with EventSource before firing a generation POST,
|
||||
then close it when the POST resolves.
|
||||
"""
|
||||
from app.services.local_diffusion import get_all_model_states
|
||||
|
||||
async def event_gen():
|
||||
try:
|
||||
while True:
|
||||
states = get_all_model_states()
|
||||
yield f"data: {json.dumps(states)}\n\n"
|
||||
await asyncio.sleep(0.2)
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
return StreamingResponse(
|
||||
event_gen(),
|
||||
media_type="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"X-Accel-Buffering": "no",
|
||||
"Connection": "keep-alive",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.post("/generate/txt2img")
|
||||
async def txt2img(req: Txt2ImgRequest):
|
||||
"""Text-to-image via configured remote provider."""
|
||||
|
||||
Reference in New Issue
Block a user