Spaces:
Paused
Paused
v1.1.0: CPU-safe GPU detection, real GPU offload, hardened fallible calls, doctor command
dc03fa5 verified | """Space entry point: serve a THOX model and join the mesh. | |
| A Space differs from Colab in two ways that matter here, and both are handled | |
| rather than papered over: | |
| * It already has a public origin, so no tunnel is needed — but the origin comes | |
| from ``SPACE_HOST``, which HF injects, and *not* ``SPACE_URL``, which it does | |
| not. Reading the wrong one is how this repo previously registered | |
| ``http://0.0.0.0:7860`` into the mesh and routed every request into a black hole. | |
| * Its container is stopped with SIGTERM on rebuild or sleep, so deregistration | |
| usually does get a chance to run — unlike a reclaimed Colab VM. | |
| Model weights live in a **private** Hub repo, so ``HF_TOKEN`` must be set as a | |
| Space secret. It is read from the environment and never written to disk or logs. | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| import os | |
| import sys | |
| # The node package is copied to /app by the Dockerfile. | |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) | |
| from thoxmesh_node.agent import run # noqa: E402 | |
| from thoxmesh_node.errors import NodeError # noqa: E402 | |
| logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s %(message)s") | |
| logger = logging.getLogger("thox.space") | |
| def _defaults() -> None: | |
| """Apply Space-appropriate defaults without clobbering explicit settings. | |
| Every value is overridable through the Space's Variables/Secrets UI, so the | |
| same image can host a different THOX model by changing configuration only. | |
| """ | |
| defaults = { | |
| "THOX_NODE_KIND": "hf-space", | |
| # ThoxMini-3B, not the smaller ThoxLLM-327M-v2. The 327M model was tried | |
| # first and serves fine mechanically, but its output is degenerate -- | |
| # it answers "The capital of France is" with "]]]]]]]]]]". It is a tiny | |
| # base model with no instruction tuning and no chat template, so | |
| # llama.cpp applies a default template it was never trained against. | |
| # ThoxMini-3B Q4_K_M is ~2 GB, instruction-tuned, and coherent on CPU. | |
| "THOX_MODEL_ID": "thoxmini-3b", | |
| "THOX_ALIAS": "thoxmini-3b-space", | |
| "THOX_MODEL_REPO": "Thox-ai/ThoxMini-3B", | |
| "THOX_MODEL_FILE": "thoxmini-3b-Q4_K_M.gguf", | |
| "THOX_N_CTX": "2048", | |
| "THOX_PORT": "7860", | |
| "THOX_CAPABILITIES": "chat,completion", | |
| # A Space is not reclaimed the way a Colab VM is, but it does sleep, so it | |
| # still publishes a lease. The window is wider because the failure mode is | |
| # slower and a shorter TTL would just add heartbeat noise. | |
| "THOX_HEARTBEAT_SECONDS": "45", | |
| "THOX_TTL_SECONDS": "300", | |
| "THOX_EPHEMERAL": "false", | |
| # Free CPU serves ~1.5 tok/s for this model, so a large cap just means slow | |
| # requests and a router that ranks this node worse. 256 is deliberate. | |
| "THOX_MAX_TOKENS_CAP": "256", | |
| } | |
| for key, value in defaults.items(): | |
| os.environ.setdefault(key, value) | |
| def main() -> int: | |
| _defaults() | |
| if not os.environ.get("HF_TOKEN"): | |
| # Not fatal: an operator may point the Space at a public GGUF instead. | |
| logger.warning("HF_TOKEN is not set; a private model repo will fail to download") | |
| try: | |
| run(echo=os.environ.get("THOX_ECHO", "").lower() in ("1", "true"), block=True) | |
| except NodeError as exc: | |
| logger.error("node failed to start: %s", exc) | |
| return 1 | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |