""" Minimal in-process background job dispatcher for long-running admin jobs (catalog ingestion, store seeding, ML model training, nutrition enrichment). This deliberately does NOT use Starlette's `BackgroundTasks`. BackgroundTasks run *synchronously after the response is sent*: an async background task is awaited directly on the server's event loop, and a sync one is awaited in the request's thread. Either way the request handler does not return until the job finishes. For jobs that take minutes (LLM calls, web scraping, ML training, Open Food Facts lookups), that turns a "kick off a job and return 202" endpoint into a blocking call and, for async tasks, freezes the whole API event loop for the duration. A daemon thread returns control to the caller immediately, and the job's progress stays visible via the job_store polling endpoints the UI already uses. Daemon threads are a deliberate, documented trade-off (see `app/api/job_store.py`): state is process-local and not safe across multiple uvicorn workers - fine for this project's intended single-process, CPU-only deployment. """ from __future__ import annotations import threading from typing import Any, Callable def run_in_background(func: Callable[[], Any], *, name: str) -> None: """Start `func` on a new daemon thread and return immediately.""" thread = threading.Thread(target=func, name=name, daemon=True) thread.start()