Did you check DeepWiki?
Describe the bug
When the REST API server is started with lazy model loading (ACESTEP_INIT_SERVICE=false, the default), the first request that triggers model initialization blocks the entire process — including GET /health and POST /query_result for unrelated jobs — for the full duration of model loading (DiT model load, LM tokenizer load, constrained-decoding setup, and especially vLLM's torch.compile warm-up on first LM use). In our case this exceeded 90+ seconds and made the server appear completely unreachable to a downstream client, even though it was working correctly the whole time and the job eventually succeeded.
Root cause: in acestep/api/job_runtime_state.py, ensure_models_initialized calls do_model_initialization(...) as a plain synchronous call inside async def, with no await/run_in_executor/asyncio.to_thread. Since this coroutine runs on the single-threaded asyncio event loop, that call blocks the whole event loop — and therefore every other in-flight request — until it returns.
async def ensure_models_initialized(app_state: Any) -> None:
...
async with app_state._init_lock:
...
do_model_initialization(app=_AppProxy(app_state), **init_kwargs) # <- blocks the event loop
By contrast, the actual generation step in acestep/api/job_execution_runtime.py gets this right, using the same ThreadPoolExecutor already sitting on app_state:
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(executor, _blocking_generate)
To Reproduce
Steps to reproduce the behavior:
- Start
acestep-api (or the Docker image) with lazy init left at its default (ACESTEP_INIT_SERVICE=false).
- Submit a
POST /release_task request with thinking: true on a fresh process (nothing loaded yet).
- Immediately poll
GET /health or POST /query_result from a second client while the first request is being processed.
- Observe: these calls do not return until model initialization (DiT + LM + vLLM compile) finishes — often 60–120+ seconds on an 8GB-tier GPU — instead of returning promptly with a "still loading"/"queued" status.
Expected behavior
GET /health and POST /query_result should stay responsive during model initialization, the same way they already do once a job is running. A client should be able to tell "still initializing" apart from "server unreachable" without an indefinite/unbounded wait on a single HTTP call.
Proposed fix
Wrap the do_model_initialization(...) call in ensure_models_initialized with the same loop.run_in_executor(executor, ...) pattern already used for generation. Flagging this as an issue rather than jumping straight to a PR since it touches a code path shared across CUDA/CPU/MPS/XPU — want to check whether there's a reason it's currently synchronous (e.g. a backend/thread-safety constraint specific to model init) before assuming run_in_executor is safe everywhere. Happy to submit the PR once that's confirmed.
Screenshots
N/A — this is a backend/API timing issue, not a UI issue.
Desktop (please complete the following information):
- OS: Windows 11 (Docker Desktop, WSL2 backend)
- Deployment:
ghcr.io/ace-step/ace-step-1.5:latest (Docker), CUDA backend
- GPU: RTX 4060 Laptop, 8GB (tier3),
acestep-v15-turbo + acestep-5Hz-lm-0.6B
Smartphone (please complete the following information):
N/A
Additional context
Confirmed via process inspection during the hang that the worker process stayed in a normal running state the whole time (not deadlocked/crashed) — this is a scheduling/blocking issue, not a crash. torch._inductor compile-worker subprocesses were actively spinning during the slow window, consistent with vLLM's first-use torch.compile cost. Not yet reproduced/checked on CPU/MPS/XPU backends.
Did you check DeepWiki?
Describe the bug
When the REST API server is started with lazy model loading (
ACESTEP_INIT_SERVICE=false, the default), the first request that triggers model initialization blocks the entire process — includingGET /healthandPOST /query_resultfor unrelated jobs — for the full duration of model loading (DiT model load, LM tokenizer load, constrained-decoding setup, and especially vLLM'storch.compilewarm-up on first LM use). In our case this exceeded 90+ seconds and made the server appear completely unreachable to a downstream client, even though it was working correctly the whole time and the job eventually succeeded.Root cause: in
acestep/api/job_runtime_state.py,ensure_models_initializedcallsdo_model_initialization(...)as a plain synchronous call insideasync def, with noawait/run_in_executor/asyncio.to_thread. Since this coroutine runs on the single-threaded asyncio event loop, that call blocks the whole event loop — and therefore every other in-flight request — until it returns.By contrast, the actual generation step in
acestep/api/job_execution_runtime.pygets this right, using the sameThreadPoolExecutoralready sitting onapp_state:To Reproduce
Steps to reproduce the behavior:
acestep-api(or the Docker image) with lazy init left at its default (ACESTEP_INIT_SERVICE=false).POST /release_taskrequest withthinking: trueon a fresh process (nothing loaded yet).GET /healthorPOST /query_resultfrom a second client while the first request is being processed.Expected behavior
GET /healthandPOST /query_resultshould stay responsive during model initialization, the same way they already do once a job is running. A client should be able to tell "still initializing" apart from "server unreachable" without an indefinite/unbounded wait on a single HTTP call.Proposed fix
Wrap the
do_model_initialization(...)call inensure_models_initializedwith the sameloop.run_in_executor(executor, ...)pattern already used for generation. Flagging this as an issue rather than jumping straight to a PR since it touches a code path shared across CUDA/CPU/MPS/XPU — want to check whether there's a reason it's currently synchronous (e.g. a backend/thread-safety constraint specific to model init) before assumingrun_in_executoris safe everywhere. Happy to submit the PR once that's confirmed.Screenshots
N/A — this is a backend/API timing issue, not a UI issue.
Desktop (please complete the following information):
ghcr.io/ace-step/ace-step-1.5:latest(Docker), CUDA backendacestep-v15-turbo+acestep-5Hz-lm-0.6BSmartphone (please complete the following information):
N/A
Additional context
Confirmed via process inspection during the hang that the worker process stayed in a normal running state the whole time (not deadlocked/crashed) — this is a scheduling/blocking issue, not a crash.
torch._inductorcompile-worker subprocesses were actively spinning during the slow window, consistent with vLLM's first-usetorch.compilecost. Not yet reproduced/checked on CPU/MPS/XPU backends.