diff --git a/hermes_cli/web_routers/local_models.py b/hermes_cli/web_routers/local_models.py index 111b874696..3e98eb7872 100644 --- a/hermes_cli/web_routers/local_models.py +++ b/hermes_cli/web_routers/local_models.py @@ -40,20 +40,54 @@ logger = logging.getLogger(__name__) router = APIRouter() -_GIB = 1 << 30 _JOBS: Dict[str, Dict[str, Any]] = {} _JOBS_LOCK = threading.Lock() +# One quickstart at a time: the job sequences installs, downloads, a server bounce and a config write — two +# racing runs would interleave all four. Held for the job's lifetime, released in the worker. +_QUICKSTART_LOCK = threading.Lock() _LLAMACPP_PROVIDERS = ("llamacpp", "llama.cpp", "llama-cpp") _SPLIT_PART_RE = r"-\d{5}-of-\d{5}" -# One TCP stream to a CDN rarely fills a fast line; 8 ranged connections -# writing into a preallocated file saturate consumer gigabit. +# One TCP stream to a CDN rarely fills a fast line; 8 ranged connections into a preallocated file saturate gigabit. _DOWNLOAD_CONNECTIONS = 8 _CHUNK = 4 << 20 _SERVER_START_FAILED = "The local server could not start — check the runtime is installed" +class RuntimeInstallBody(BaseModel): + backend: Optional[str] = None # None/auto -> detect + + +class ModelDownloadBody(BaseModel): + model_id: str + + +class QuickstartBody(BaseModel): + model_id: str | None = None # default: the catalog's recommended entry + + +class ServerActionBody(BaseModel): + action: str # "stop" | "start" + + +class ModelEjectBody(BaseModel): + model_id: str + + +class ModelActivateBody(BaseModel): + model_id: str # exact variant id (a staged .gguf stem) + + +class BrowsedDownloadBody(BaseModel): + repo: str + paths: list[str] # one GGUF, or every part of a split, in order + + +class SideloadBody(BaseModel): + path: str # absolute path to a .gguf on this machine + + def _human_gb(n: int | float) -> str: - return f"{n / _GIB:.1f} GB" + return f"{n / (1 << 30):.1f} GB" def _k_label(tokens: int) -> str: @@ -110,9 +144,9 @@ def _finish(job: Dict[str, Any], detail: str) -> None: def _spawn_job(job: Dict[str, Any], name: str, body: Callable[[], None], *, fail_msg: str | None = None, on_exit: Callable[[], None] | None = None, download_label: str | None = None) -> None: - """Run ``body`` on a daemon thread; an exception marks the job errored (warning - ``fail_msg`` when given). ``on_exit`` always runs last. ``download_label`` makes it - a download job: it finishes as "