Spaces:
Paused
Paused
File size: 8,568 Bytes
dc03fa5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | """Typed configuration for the node agent, resolved from the environment.
Every field is settable by environment variable so the same module runs
unmodified in a Colab cell, a Docker Space and a local shell. ``.env.example``
documents the same names with placeholder values; no real secret is ever
committed.
"""
from __future__ import annotations
import os
from dataclasses import dataclass, field
from typing import Any, Mapping
from .errors import ConfigError
#: Supabase project hosting the MeshStack v2 control plane.
DEFAULT_SUPABASE_PROJECT = "rtaktyshnzbjktdyeemg"
#: Transport used to publish this node into the mesh.
TRANSPORT_DEVICE_V2 = "device_v2"
TRANSPORT_CONTROLLER = "controller"
TRANSPORT_AUTO = "auto"
VALID_TRANSPORTS = (TRANSPORT_AUTO, TRANSPORT_DEVICE_V2, TRANSPORT_CONTROLLER)
#: Protocol values allowed by the v2 schema CHECK constraint.
VALID_PROTOCOLS = ("openai", "ollama", "llama_cpp", "litert", "custom")
def _env_int(source: Mapping[str, str], key: str, default: int, *, minimum: int = 1) -> int:
raw = (source.get(key) or "").strip()
if not raw:
return default
try:
value = int(raw)
except ValueError as exc:
raise ConfigError(f"{key} must be an integer, got {raw!r}") from exc
if value < minimum:
raise ConfigError(f"{key} must be >= {minimum}, got {value}")
return value
def _env_bool(source: Mapping[str, str], key: str, default: bool) -> bool:
raw = (source.get(key) or "").strip().lower()
if not raw:
return default
return raw in ("1", "true", "yes", "on")
def _env_flags(source: Mapping[str, str], key: str, default: tuple[str, ...]) -> dict[str, bool]:
"""Parse a comma-separated capability list into the jsonb flag map.
ThoxRoute and ``resolve_route`` both test capabilities by truthy key lookup,
so the list is expanded into ``{"chat": True, ...}`` rather than kept as an
array.
"""
raw = (source.get(key) or "").strip()
names = [part.strip() for part in raw.split(",") if part.strip()] if raw else list(default)
return {name: True for name in names}
@dataclass(frozen=True)
class NodeConfig:
"""Fully resolved node configuration."""
# Identity of what we serve
model_id: str
alias: str
protocol: str
context_window: int
capabilities: dict[str, bool]
# Where we serve it
host: str
port: int
public_url: str
# How we join the mesh
transport: str
supabase_project: str
controller_url: str
node_kind: str
ephemeral: bool
heartbeat_seconds: int
ttl_seconds: int
register_retry_seconds: int
# Local backend
model_repo: str
model_file: str
n_threads: int
n_gpu_layers: str
max_tokens_cap: int
request_timeout_seconds: int
extra: dict[str, Any] = field(default_factory=dict)
@property
def device_function_url(self) -> str:
"""Absolute URL of the ``meshstack-device-v2`` edge function."""
return f"https://{self.supabase_project}.supabase.co/functions/v1/meshstack-device-v2"
def validate(self) -> None:
"""Fail fast on contradictions the agent cannot resolve at runtime."""
if self.protocol not in VALID_PROTOCOLS:
raise ConfigError(
f"THOX_PROTOCOL must be one of {VALID_PROTOCOLS}, got {self.protocol!r}; "
"the v2 schema rejects anything else"
)
if self.transport not in VALID_TRANSPORTS:
raise ConfigError(f"THOX_MESH_TRANSPORT must be one of {VALID_TRANSPORTS}")
if self.ttl_seconds <= self.heartbeat_seconds:
raise ConfigError(
f"TTL ({self.ttl_seconds}s) must exceed the heartbeat interval "
f"({self.heartbeat_seconds}s), otherwise the lease expires between beats"
)
if self.transport == TRANSPORT_CONTROLLER and not self.controller_url:
raise ConfigError("controller transport selected but THOX_CONTROLLER_URL is empty")
if not self.model_id:
raise ConfigError("THOX_MODEL_ID must not be empty")
if not self.alias:
raise ConfigError("THOX_ALIAS must not be empty")
def load_config(env: Mapping[str, str] | None = None) -> NodeConfig:
"""Resolve configuration from ``env`` (defaults to ``os.environ``).
Defaults target the free-tier case deliberately: a CPU-sized GGUF, a 30s
heartbeat and a 120s TTL. The TTL is four heartbeats so a node survives a
couple of transient failures but a dead Colab session drops out of routing
inside two minutes.
"""
source = env if env is not None else os.environ
heartbeat = _env_int(source, "THOX_HEARTBEAT_SECONDS", 30)
ttl = _env_int(source, "THOX_TTL_SECONDS", 120)
config = NodeConfig(
# Defaults name a model that exists. An earlier default was
# "thox-micro-125m", which is not published on the Hub at all, so any
# caller relying on defaults was pointed at nothing.
model_id=(source.get("THOX_MODEL_ID") or "thoxmini-3b").strip(),
alias=(source.get("THOX_ALIAS") or source.get("THOX_MODEL_ID") or "thoxmini-3b").strip(),
protocol=(source.get("THOX_PROTOCOL") or "openai").strip(),
context_window=_env_int(source, "THOX_N_CTX", 2048),
capabilities=_env_flags(source, "THOX_CAPABILITIES", ("chat", "completion")),
host=(source.get("THOX_HOST") or "0.0.0.0").strip(),
port=_env_int(source, "THOX_PORT", 7860),
public_url=(source.get("THOX_PUBLIC_URL") or "").strip().rstrip("/"),
transport=(source.get("THOX_MESH_TRANSPORT") or TRANSPORT_AUTO).strip().lower(),
supabase_project=(source.get("THOX_SUPABASE_PROJECT") or DEFAULT_SUPABASE_PROJECT).strip(),
controller_url=(source.get("THOX_CONTROLLER_URL") or "").strip().rstrip("/"),
node_kind=(source.get("THOX_NODE_KIND") or "unknown").strip(),
ephemeral=_env_bool(source, "THOX_EPHEMERAL", True),
heartbeat_seconds=heartbeat,
ttl_seconds=ttl,
register_retry_seconds=_env_int(source, "THOX_REGISTER_RETRY_SECONDS", 10),
model_repo=(source.get("THOX_MODEL_REPO") or "Thox-ai/ThoxMini-3B").strip(),
model_file=(source.get("THOX_MODEL_FILE") or "thoxmini-3b-Q4_K_M.gguf").strip(),
n_threads=_env_int(source, "THOX_N_THREADS", 0, minimum=0),
# "auto" offloads everything when a GPU is detected and nothing when it
# is not, so one value is correct on both Colab runtime types.
n_gpu_layers=(source.get("THOX_N_GPU_LAYERS") or "auto").strip(),
max_tokens_cap=_env_int(source, "THOX_MAX_TOKENS_CAP", 512),
request_timeout_seconds=_env_int(source, "THOX_REQUEST_TIMEOUT_SECONDS", 120),
)
config.validate()
return config
def detect_node_kind(env: Mapping[str, str] | None = None) -> str:
"""Best-effort guess at where we are running.
Used for telemetry labelling and to pick sensible tunnel behaviour. A Space
is already publicly addressable and needs no tunnel; Colab always does.
"""
source = env if env is not None else os.environ
if source.get("SPACE_ID") or source.get("SPACE_HOST"):
return "hf-space"
if source.get("COLAB_RELEASE_TAG") or source.get("COLAB_GPU") is not None:
return "colab"
if "google.colab" in (source.get("PYTHONPATH") or ""):
return "colab"
return "local"
def space_public_url(env: Mapping[str, str] | None = None) -> str:
"""Public origin of the current Space, or ``""`` when not on a Space.
Hugging Face injects ``SPACE_HOST`` (``owner-name.hf.space``) but not
``SPACE_URL``; this repo previously registered ``http://0.0.0.0:7860``
because it read the variable that does not exist. Returning ``""`` when the
origin is unknown is deliberate — advertising an unreachable address is
worse than not registering.
"""
source = env if env is not None else os.environ
host = (source.get("SPACE_HOST") or "").strip()
if host:
return host if "://" in host else f"https://{host}"
space_id = (source.get("SPACE_ID") or "").strip()
if space_id and "/" in space_id:
owner, name = space_id.split("/", 1)
slug = f"{owner}-{name}".replace("_", "-").replace(".", "-").lower()
return f"https://{slug}.hf.space"
return ""
|