diff --git a/server/runner.py b/server/runner.py index 71c1316..0e87d85 100644 --- a/server/runner.py +++ b/server/runner.py @@ -28,6 +28,18 @@ JOBS_DIR = db.DATA / "jobs" # primary-only semaphore because the cloud API keys live only on the primary. LANE_LIMITS = {"cpu": 3, "net": 6} +# Operators cheap enough (CPU-light, small assets) to keep OFF the big GPUs so +# qwen-image-edit / trellis / seedvr2 always find an ultra free. For these the +# dispatcher prefers the home helper on a direct link; every other operator keeps +# the default local-first order (i.e. still prefers the fast ultras). +LIGHT_OPS = {"bg_remove_local", "ffmpeg_frames", "ffprobe"} +# Lower rank = tried first for a LIGHT_OP. m4 (m4pro) is the home helper on a direct +# ~8ms link; the two ultras (local=m3ultra, m1=ultra) come next so a busy m4pro still +# falls back fast; the work-site machines are Sydney-DERP-relayed from the farm +# controller, so they are the last resort. +# the primary is named by hostname since 2026-08-05 (was "local"); keep both keys +_LIGHT_ORDER = {"m4": 0, "m1": 1, "m3ultra": 2, "local": 2, "studio": 3, "m4mini": 4, "mini": 5} + class Runner: def __init__(self): @@ -210,9 +222,13 @@ class Runner: async def _acquire_node(self, op_id: str, lane: str) -> dict: """Return the first node with free capacity in this lane that supports the - operator (local always does; a remote must list it + be reachable). Polls.""" + operator (local always does; a remote must list it + be reachable). Polls. + LIGHT_OPS prefer the home helper (m4pro) so the ultras stay free for heavy work.""" + order = self.gpu_nodes + if op_id in LIGHT_OPS: + order = sorted(order, key=lambda n: _LIGHT_ORDER.get(n.get("name", ""), 9)) while True: - for n in self.gpu_nodes: + for n in order: if not remote.node_supports(n, op_id): continue cap = self._node_capacity(n, lane)