routing improvements - dynamic (wip)

This commit is contained in:
Dobromir Popov
2026-07-07 21:25:28 +02:00
parent e2b20883ca
commit 518c259cd3
6 changed files with 964 additions and 19 deletions

View File

@@ -9,6 +9,7 @@ from pathlib import Path
from .accounts import DEFAULT_ACCOUNTS_DB_PATH
from .billing import DEFAULT_BILLING_DB_PATH
from .hf_pricing import DEFAULT_HF_PRICING_LOG_DB_PATH
from .routing_stats import RoutingConfig
from .server import (
DEFAULT_CALLER_CREDIT_USDT,
DEFAULT_DEVNET_TOPUP_USDT,
@@ -51,6 +52,19 @@ def _load_env_defaults() -> None:
_load_env_file(Path.home() / ".config" / "meshnet" / "secrets.env")
def _routing_config_from_args(args: argparse.Namespace) -> RoutingConfig | None:
"""Build a RoutingConfig from CLI flags; None keeps env-var/server defaults."""
overrides = {
"explore_share": args.route_explore_share,
"weight_alpha": args.route_weight_alpha,
"stats_half_life_seconds": args.route_stats_half_life,
}
set_values = {key: value for key, value in overrides.items() if value is not None}
if not set_values:
return None
return RoutingConfig(**set_values)
def main() -> None:
_load_env_defaults()
common = argparse.ArgumentParser(add_help=False)
@@ -261,6 +275,33 @@ def main() -> None:
metavar="PATH",
help="Local HuggingFace snapshot root advertised as tracker model-file source (default: MESHNET_MODELS_DIR)",
)
common.add_argument(
"--route-explore-share",
type=float,
default=None,
metavar="FRACTION",
help=(
"Fraction of requests routed down unproven/stale routes to gather "
"throughput statistics (ADR-0021; default 0.3, lower once traffic grows)"
),
)
common.add_argument(
"--route-weight-alpha",
type=float,
default=None,
metavar="ALPHA",
help=(
"Traffic weight exponent among proven routes: share ∝ tps^alpha "
"(default 1.0 — a 1.5x-faster route gets 1.5x the traffic)"
),
)
common.add_argument(
"--route-stats-half-life",
type=float,
default=None,
metavar="SECONDS",
help="Half-life for decaying route throughput observations (default 600)",
)
parser = argparse.ArgumentParser(
prog="meshnet-tracker",
@@ -319,6 +360,7 @@ def main() -> None:
),
hf_pricing_refresh_interval=args.hf_pricing_refresh_interval,
models_dir=args.models_dir,
routing_config=_routing_config_from_args(args),
)
port = server.start()
print(f"meshnet-tracker listening on http://{args.host}:{port}", flush=True)

View File

@@ -231,6 +231,7 @@
<section data-tab="overview"><h2>Tracker hive</h2><div id="hive" class="empty">loading…</div></section>
<section data-tab="overview"><h2>Nodes &amp; coverage</h2><div id="nodes" class="empty">loading…</div></section>
<section data-tab="overview"><h2>Model usage (RPM)</h2><div id="stats" class="empty">loading…</div></section>
<section data-tab="overview" class="wide"><h2>Routing (learned)</h2><div id="routing" class="empty">loading…</div></section>
<section data-tab="overview" class="wide"><h2>Call wall</h2><div id="call-wall" class="empty">loading...</div></section>
<section data-tab="chat" class="wide chat-section">
<div class="chat-app">
@@ -502,6 +503,42 @@ function callWallMaxQueue(rec) {
return nodeQueues.length ? Math.max(...nodeQueues) : 0;
}
function renderRouting(routing) {
const el = $("routing");
if (!el) return;
const models = (routing && routing.models) || {};
const entries = Object.entries(models);
if (!entries.length) {
el.innerHTML = '<div class="empty">no routable models yet</div>';
return;
}
const cfg = (routing && routing.config) || {};
let html = `<div class="dim" style="margin-bottom:6px">` +
`explore share: <b>${esc(String(cfg.explore_share ?? "?"))}</b> · ` +
`traffic ∝ tps^<b>${esc(String(cfg.weight_alpha ?? "?"))}</b> · ` +
`half-life: <b>${esc(String(cfg.stats_half_life_seconds ?? "?"))}s</b></div>`;
for (const [model, info] of entries) {
const routes = info.routes || [];
html += `<div style="margin-top:6px"><b>${esc(model)}</b> ` +
`<span class="dim">(epoch ${esc(String(info.epoch ?? 0))} · ${routes.length} route${routes.length === 1 ? "" : "s"})</span></div>`;
html += table(["route", "tps", "coeff", "share", "samples", "status"], routes.map(r => {
const hops = (r.hops || []).map(h => `${short(h.node_id, 12)}[${h.shard}]`).join(" → ");
const statusCls = r.status === "proven" ? "ok" : r.status === "stale" ? "warn" : "dim";
const coeff = (r.coefficient === null || r.coefficient === undefined)
? "—" : Number(r.coefficient).toFixed(2) + "×";
return [
esc(hops || short(r.signature, 40)),
`<span class="num">${esc(r.tps === null || r.tps === undefined ? "—" : tps(r.tps))}</span>`,
`<span class="num">${esc(coeff)}</span>`,
`<span class="num">${esc(Math.round((r.expected_share || 0) * 100) + "%")}</span>`,
`<span class="num">${esc(String(r.samples ?? 0))}</span>`,
`<span class="${statusCls}">${esc(r.status || "?")}</span>`,
];
}));
}
el.innerHTML = html;
}
function renderCallWall(consoleData, stats) {
const events = (consoleData && consoleData.events) || [];
const nowSec = Date.now() / 1000;
@@ -1448,12 +1485,13 @@ $("call-wall").addEventListener("click", (event) => {
async function refresh() {
$("self-url").textContent = location.host;
const [raft, map, stats, models, consoleData, adminData] = await Promise.all([
const [raft, map, stats, models, consoleData, routing, adminData] = await Promise.all([
fetchJson("/v1/raft/status"),
fetchJson("/v1/network/map"),
fetchJson("/v1/stats"),
fetchJson("/v1/models"),
fetchJson("/v1/console"),
fetchJson("/v1/routing"),
isAdmin ? Promise.all([
fetchJson("/v1/billing/summary"),
fetchJson("/v1/billing/settlements"),
@@ -1474,6 +1512,7 @@ async function refresh() {
renderSettlements(settlements);
renderFraud(wallets, summary);
renderStats(stats);
renderRouting(routing);
renderCallWall(consoleData, stats);
renderConsole(consoleData);
renderNodeThroughput(stats);

View File

@@ -0,0 +1,257 @@
"""Learned route statistics for dynamic bandit-style route selection (ADR-0021).
The tracker treats each viable route (ordered chain of node shards covering a
model) as a bandit arm. Observed end-to-end tokens/sec per route is kept as a
time-decayed EWMA. Selection splits traffic between:
- **exploit**: weighted-random among *proven* routes, weight ∝ tps ** alpha
(alpha=1.0 → a 1.5x-faster route gets 1.5x the traffic);
- **scout**: with probability `explore_share`, the least-measured unproven or
stale route is chosen so the tracker keeps learning as the network morphs.
Staleness has two mechanisms:
- continuous: sample mass decays with `stats_half_life_seconds`, so old
observations fade;
- abrupt: every node join/leave bumps the model's *topology epoch*; stats from
an older epoch keep their EWMA as a prior but drop back into the scout pool
until re-measured.
Route signatures embed node ids and shard ranges, so a node re-registering
with a different shard produces a new arm automatically.
"""
from __future__ import annotations
import random
import threading
import time
from dataclasses import dataclass, field
from typing import Any, Iterable
@dataclass(frozen=True)
class RoutingConfig:
explore_share: float = 0.3
weight_alpha: float = 1.0
stats_half_life_seconds: float = 600.0
min_sample_tokens: int = 8
# One fresh sample has mass 1.0 and decays from there; 0.5 keeps a single
# observation "proven" for one half-life before demoting it to the scout pool.
min_proven_weight: float = 0.5
max_candidate_routes: int = 8
prune_after_seconds: float = 86400.0
@dataclass
class RouteStat:
ewma_tps: float = 0.0
weight: float = 0.0 # decayed effective sample mass
last_sample_ts: float = 0.0
epoch: int = 0
samples: int = 0 # lifetime raw sample count (display only)
def decayed_weight(self, now: float, half_life: float) -> float:
if self.weight <= 0.0:
return 0.0
age = max(0.0, now - self.last_sample_ts)
return self.weight * 0.5 ** (age / half_life)
@dataclass
class RouteCandidate:
nodes: list[Any]
signature: str
prior_tps: float = 0.0
def route_signature(model_key: str, nodes: Iterable[Any]) -> str:
hops = "->".join(
f"{getattr(n, 'node_id', '?')}[{getattr(n, 'shard_start', '?')}-{getattr(n, 'shard_end', '?')}]"
for n in nodes
)
return f"{model_key}|{hops}"
class RouteStatsStore:
"""Thread-safe per-route decayed throughput statistics."""
def __init__(self, config: RoutingConfig | None = None) -> None:
self.config = config or RoutingConfig()
self._lock = threading.Lock()
self._stats: dict[str, RouteStat] = {}
self._epochs: dict[str, int] = {}
def epoch(self, model_key: str) -> int:
with self._lock:
return self._epochs.get(model_key, 0)
def bump_epoch(self, model_keys: Iterable[str | None]) -> None:
"""Mark the topology changed for the given model keys (node join/leave)."""
with self._lock:
for key in model_keys:
if key:
self._epochs[key] = self._epochs.get(key, 0) + 1
def record_sample(
self,
model_key: str,
signature: str,
tokens: int,
elapsed_seconds: float,
now: float | None = None,
) -> bool:
"""Fold one completed request into the route's EWMA.
Returns False (and records nothing) for samples below
`min_sample_tokens` — near-empty completions come from broken routes
and would poison the arm with meaningless throughput values.
"""
cfg = self.config
if tokens < cfg.min_sample_tokens or elapsed_seconds <= 0.0:
return False
tps = tokens / elapsed_seconds
ts = time.time() if now is None else now
with self._lock:
stat = self._stats.get(signature)
if stat is None:
stat = RouteStat()
self._stats[signature] = stat
carried = stat.decayed_weight(ts, cfg.stats_half_life_seconds)
total = carried + 1.0
stat.ewma_tps = (stat.ewma_tps * carried + tps) / total
stat.weight = total
stat.last_sample_ts = ts
stat.epoch = self._epochs.get(model_key, 0)
stat.samples += 1
return True
def snapshot(self, signature: str, model_key: str, now: float | None = None) -> dict:
"""Point-in-time view of one route's learned state."""
ts = time.time() if now is None else now
cfg = self.config
with self._lock:
stat = self._stats.get(signature)
current_epoch = self._epochs.get(model_key, 0)
if stat is None:
return {"tps": None, "weight": 0.0, "samples": 0, "status": "unsampled"}
weight = stat.decayed_weight(ts, cfg.stats_half_life_seconds)
if stat.epoch != current_epoch:
status = "stale"
elif weight < cfg.min_proven_weight:
status = "decayed" if stat.samples else "unsampled"
else:
status = "proven"
return {
"tps": round(stat.ewma_tps, 4) if stat.samples else None,
"weight": round(weight, 4),
"samples": stat.samples,
"status": status,
}
def prune(self, now: float | None = None) -> int:
"""Drop routes with no samples for `prune_after_seconds`."""
ts = time.time() if now is None else now
cutoff = ts - self.config.prune_after_seconds
with self._lock:
dead = [sig for sig, stat in self._stats.items() if stat.last_sample_ts < cutoff]
for sig in dead:
del self._stats[sig]
return len(dead)
def choose_route(
candidates: list[RouteCandidate],
store: RouteStatsStore,
model_key: str,
rng: random.Random | None = None,
now: float | None = None,
) -> tuple[RouteCandidate | None, dict]:
"""Pick a route: ε-scout among unproven arms, else weighted ∝ tps**alpha.
Returns (candidate, decision) where decision explains the pick for logs
and diagnostics: {"mode": "scout"|"exploit"|"prior", ...}.
"""
if not candidates:
return None, {"mode": "none"}
rng = rng or random
cfg = store.config
proven: list[tuple[RouteCandidate, float]] = []
scouts: list[tuple[RouteCandidate, float]] = []
for cand in candidates:
snap = store.snapshot(cand.signature, model_key, now=now)
if snap["status"] == "proven":
proven.append((cand, max(float(snap["tps"] or 0.0), 1e-6)))
else:
scouts.append((cand, float(snap["weight"])))
if scouts and (not proven or rng.random() < cfg.explore_share):
# Least-measured first so new/stale arms accumulate samples fastest;
# tiebreak on prior estimate so plausible routes get scouted first.
scouts.sort(key=lambda item: (item[1], -item[0].prior_tps))
pick = scouts[0][0]
return pick, {"mode": "scout", "signature": pick.signature}
if proven:
weights = [tps ** cfg.weight_alpha for _, tps in proven]
pick = rng.choices([cand for cand, _ in proven], weights=weights, k=1)[0]
return pick, {
"mode": "exploit",
"signature": pick.signature,
"candidates": len(proven),
}
# No stats anywhere yet — fall back to the prior (benchmark-derived) estimate.
weights = [max(cand.prior_tps, 1e-6) ** cfg.weight_alpha for cand in candidates]
pick = rng.choices(candidates, weights=weights, k=1)[0]
return pick, {"mode": "prior", "signature": pick.signature}
def route_table(
candidates: list[RouteCandidate],
store: RouteStatsStore,
model_key: str,
now: float | None = None,
) -> list[dict]:
"""Diagnostics rows: learned tps, coefficient vs best, expected traffic share."""
cfg = store.config
rows = []
for cand in candidates:
snap = store.snapshot(cand.signature, model_key, now=now)
rows.append({"candidate": cand, **snap})
proven = [r for r in rows if r["status"] == "proven"]
scouts = [r for r in rows if r["status"] != "proven"]
best_tps = max((float(r["tps"]) for r in proven), default=0.0)
exploit_budget = 1.0 - (cfg.explore_share if scouts and proven else 0.0)
if not proven:
exploit_budget = 0.0
weight_sum = sum(float(r["tps"]) ** cfg.weight_alpha for r in proven) or 1.0
out = []
for r in rows:
cand: RouteCandidate = r["candidate"]
if r["status"] == "proven":
share = exploit_budget * (float(r["tps"]) ** cfg.weight_alpha) / weight_sum
coefficient = round(float(r["tps"]) / best_tps, 3) if best_tps else None
else:
share = (
(cfg.explore_share if proven else 1.0) / len(scouts)
if scouts
else 0.0
)
coefficient = None
out.append({
"signature": cand.signature,
"hops": [
{
"node_id": getattr(n, "node_id", "?"),
"shard": f"{getattr(n, 'shard_start', '?')}-{getattr(n, 'shard_end', '?')}",
"endpoint": getattr(n, "endpoint", "?"),
}
for n in cand.nodes
],
"tps": r["tps"],
"coefficient": coefficient,
"expected_share": round(share, 4),
"samples": r["samples"],
"weight": r["weight"],
"status": r["status"],
"prior_tps": round(cand.prior_tps, 4),
})
out.sort(key=lambda r: (-(r["tps"] or 0.0), -r["prior_tps"]))
return out

View File

@@ -19,6 +19,8 @@ HTTP API contract:
- GET /v1/routes?model=<preset>&redundancy=<n>
Response 200: {"routes": [{"route": [...], "nodes": [...]}]}
Response 400/404/503: {"error": str}
- GET /v1/routing?model=<name> (ADR-0021 learned route table)
Response 200: {"config": {...}, "models": {model: {"epoch": int, "routes": [...]}}}
"""
import http.server
@@ -26,6 +28,7 @@ import hashlib
import itertools
import json
import os
import random
import socketserver
import sqlite3
import tarfile
@@ -47,6 +50,14 @@ from .billing import DEFAULT_BILLING_DB_PATH, BillingLedger
from .calibration import DEFAULT_CALIBRATION_DB_PATH, ToplocCalibrationStore
from .hf_pricing import DEFAULT_HF_PRICING_LOG_DB_PATH, HfPricingLog, refresh_preset_price
from .gossip import NodeGossip
from .routing_stats import (
RouteCandidate,
RouteStatsStore,
RoutingConfig,
choose_route,
route_signature,
route_table,
)
from .model_files import files_for_layer_range, snapshot_dir_for_repo
from .raft import RaftNode
@@ -738,6 +749,64 @@ def _select_route(
return route, ""
def _enumerate_routes(
nodes: list["_NodeEntry"],
required_start: int,
required_end: int,
model: str | None = None,
contracts: Any | None = None,
max_candidates: int = 8,
) -> list["RouteCandidate"]:
"""Enumerate viable route candidates for bandit selection (ADR-0021).
One candidate per distinct head (a node that can embed the prompt, i.e.
covers `required_start` from layer 0 of the range), each greedily completed
with the longest-advancing hops. The route's prior throughput estimate is
its bottleneck hop's queue-adjusted effective throughput — used only until
observed route samples exist.
"""
sharded = [
n for n in nodes
if n.shard_start is not None and n.shard_end is not None
]
# Heads must start the pipeline at the first required layer (they tokenize
# and embed the prompt — same condition as tracker_mode registration).
heads = [n for n in sharded if n.shard_start == required_start]
candidates: dict[str, RouteCandidate] = {}
for head in heads:
route = [head]
covered_up_to = head.shard_end
pool = [n for n in sharded if n is not head]
while covered_up_to < required_end:
best = None
for n in pool:
if n.shard_start <= covered_up_to + 1 and n.shard_end > covered_up_to:
if best is None or n.shard_end > best.shard_end or (
n.shard_end == best.shard_end
and _effective_throughput(n, model) * _reputation_multiplier(n, contracts)
> _effective_throughput(best, model) * _reputation_multiplier(best, contracts)
):
best = n
if best is None:
route = []
break
route.append(best)
covered_up_to = best.shard_end
pool = [n for n in pool if n is not best]
if not route:
continue
signature = route_signature(model or "?", route)
if signature in candidates:
continue
prior = min(
_effective_throughput(n, model) * _reputation_multiplier(n, contracts)
for n in route
)
candidates[signature] = RouteCandidate(nodes=route, signature=signature, prior_tps=prior)
ranked = sorted(candidates.values(), key=lambda c: -c.prior_tps)
return ranked[:max_candidates]
def _node_route_summary(nodes: list["_NodeEntry"]) -> list[dict]:
return [
{
@@ -1720,6 +1789,14 @@ def _drop_directive(node: _NodeEntry, model: str, start: int, end: int, quantiza
}
def _route_stats_keys(server: "_TrackerHTTPServer", entry: "_NodeEntry") -> list[str]:
"""All stats keys a node's routes may be recorded under (model, repo, resolved preset)."""
keys = {entry.model, entry.hf_repo}
resolved, _ = _resolve_model_preset(server.model_presets, entry.hf_repo or entry.model)
keys.add(resolved)
return [key for key in keys if key]
def _purge_expired_nodes_locked(server: "_TrackerHTTPServer") -> list[str]:
now = time.monotonic()
expired_ids = [
@@ -1732,6 +1809,11 @@ def _purge_expired_nodes_locked(server: "_TrackerHTTPServer") -> list[str]:
expired_entries.append((node_id, entry))
if expired_ids:
_rebalance_all_locked(server)
server.route_stats.bump_epoch(
key
for _, entry in expired_entries
for key in _route_stats_keys(server, entry)
)
for node_id, entry in expired_entries:
_tracker_log(
server,
@@ -2289,6 +2371,7 @@ class _TrackerHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
toploc_backend: Any | None = None,
hf_pricing_log: "HfPricingLog | None" = None,
models_dir: Path | None = None,
route_stats: "RouteStatsStore | None" = None,
) -> None:
super().__init__(addr, handler)
self.registry = registry
@@ -2324,6 +2407,8 @@ class _TrackerHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
self.console_lock = threading.Lock()
self.active_proxies: dict[str, _ActiveProxyContext] = {}
self.active_proxies_lock = threading.Lock()
self.route_stats: RouteStatsStore = route_stats or RouteStatsStore()
self.route_rng = random.Random()
class _TrackerHandler(http.server.BaseHTTPRequestHandler):
@@ -2499,6 +2584,8 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
self._handle_route(parsed)
elif parsed.path == "/v1/routes":
self._handle_routes(parsed)
elif parsed.path == "/v1/routing":
self._handle_routing(parsed)
elif parsed.path == "/v1/nodes/assign":
self._handle_assign(parsed)
elif parsed.path == "/v1/network/assign":
@@ -2964,25 +3051,50 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
rs, re = 0, (max((n.num_layers for n in all_nodes), default=1) - 1)
if pinned_nodes is not None:
route_nodes = pinned_nodes
routing_decision = {"mode": "pinned"}
else:
route_nodes, route_error = _select_route(all_nodes, rs, re, model=route_model, contracts=server.contracts)
if route_error:
_tracker_log(
server,
"warn",
"route unavailable",
model=model,
route_model=route_model,
error=route_error,
candidate_count=len(all_nodes),
candidates=_node_route_summary(all_nodes),
)
self._send_json(503, {"error": {
"message": route_error,
"type": "service_unavailable",
"code": "route_not_available",
}})
return
# ADR-0021: enumerate viable routes and pick one bandit-style —
# ε-scout among unproven routes, otherwise weighted ∝ observed tps^α.
route_candidates = _enumerate_routes(
all_nodes, rs, re,
model=route_model,
contracts=server.contracts,
max_candidates=server.route_stats.config.max_candidate_routes,
)
picked, routing_decision = choose_route(
route_candidates, server.route_stats, route_model, rng=server.route_rng,
)
if picked is not None:
route_nodes = picked.nodes
else:
# No head-anchored candidate — legacy greedy cover as fallback
# (also produces the layer-gap error message).
route_nodes, route_error = _select_route(all_nodes, rs, re, model=route_model, contracts=server.contracts)
routing_decision = {"mode": "greedy-fallback"}
if route_error:
_tracker_log(
server,
"warn",
"route unavailable",
model=model,
route_model=route_model,
error=route_error,
candidate_count=len(all_nodes),
candidates=_node_route_summary(all_nodes),
)
self._send_json(503, {"error": {
"message": route_error,
"type": "service_unavailable",
"code": "route_not_available",
}})
return
# The proxy target must be the route's own head: an independently
# chosen fastest node may not be part of the planned route, which
# previously injected downstream hops with wrong start layers
# (ADR-0020 mixed-topology flaw).
if route_nodes:
node = route_nodes[0]
target_url = f"{node.endpoint}/v1/chat/completions"
# Compute start_layer for each hop: each node begins where the previous ended + 1.
# This allows overlapping shard registrations without double-computation.
covered_up_to = rs - 1
@@ -3026,6 +3138,7 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
head_endpoint=node.endpoint,
downstream=downstream_urls,
route=route_debug or "<empty>",
routing=routing_decision,
nodes=_node_route_summary(route_nodes),
)
@@ -3402,6 +3515,15 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
can refine this later without changing the external stats shape.
"""
server: _TrackerHTTPServer = self.server # type: ignore[assignment]
if route_model and route_nodes:
# Route-level bandit sample (ADR-0021); the store itself rejects
# near-empty completions that would poison the arm.
server.route_stats.record_sample(
route_model,
route_signature(route_model, route_nodes),
total_tokens,
elapsed_seconds,
)
if server.stats is None or total_tokens <= 0:
return
elapsed_seconds = max(elapsed_seconds, 1e-6)
@@ -3825,7 +3947,14 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
for eid in stale_ids:
old = server.registry.pop(eid)
stale_entries.append((eid, old))
is_topology_change = node_id not in stale_ids or any(
(old.shard_start, old.shard_end) != (entry.shard_start, entry.shard_end)
for eid, old in stale_entries
if eid == node_id
)
server.registry[node_id] = entry
if is_topology_change:
server.route_stats.bump_epoch(_route_stats_keys(server, entry))
if entry.managed_assignment and not explicit_shard:
if entry.hf_repo:
_rebalance_hf_model_locked(server, entry.hf_repo)
@@ -5280,6 +5409,61 @@ class _TrackerHandler(http.server.BaseHTTPRequestHandler):
],
})
def _handle_routing(self, parsed: urllib.parse.ParseResult):
"""Learned route table (ADR-0021): per-model candidates with observed
tps, coefficient vs the best proven route, and expected traffic share."""
server: _TrackerHTTPServer = self.server # type: ignore[assignment]
params = urllib.parse.parse_qs(parsed.query)
only_model = (params.get("model") or [None])[0]
with server.lock:
self._purge_expired_nodes()
alive = list(server.registry.values())
model_keys: dict[str, list] = {}
for node in alive:
if node.shard_start is None or node.shard_end is None:
continue
key = node.hf_repo or node.model
if not key:
continue
model_keys.setdefault(key, []).append(node)
cfg = server.route_stats.config
out: dict[str, dict] = {}
for key, nodes in model_keys.items():
if only_model and key != only_model and not any(
key == alias for alias in _model_aliases(only_model)
):
continue
resolved_name, preset = _resolve_model_preset(server.model_presets, key)
# Stats are recorded under the proxy's resolved route_model —
# use the same key here or lookups always miss.
stats_key = resolved_name or key
if preset is not None:
rs, re = _preset_layer_bounds(preset)
else:
layer_counts = [n.num_layers for n in nodes if n.num_layers is not None]
if not layer_counts:
continue
rs, re = 0, max(layer_counts) - 1
candidates = _enumerate_routes(
nodes, rs, re,
model=stats_key,
contracts=server.contracts,
max_candidates=cfg.max_candidate_routes,
)
out[stats_key] = {
"epoch": server.route_stats.epoch(stats_key),
"routes": route_table(candidates, server.route_stats, stats_key),
}
self._send_json(200, {
"config": {
"explore_share": cfg.explore_share,
"weight_alpha": cfg.weight_alpha,
"stats_half_life_seconds": cfg.stats_half_life_seconds,
"min_sample_tokens": cfg.min_sample_tokens,
},
"models": out,
})
def _handle_routes(self, parsed: urllib.parse.ParseResult):
server: _TrackerHTTPServer = self.server # type: ignore[assignment]
params = urllib.parse.parse_qs(parsed.query)
@@ -5398,6 +5582,7 @@ class TrackerServer:
hf_pricing_refresh_interval: float = 86400.0,
hf_pricing_fetch_html: Any | None = None,
models_dir: str | Path | None = None,
routing_config: RoutingConfig | None = None,
) -> None:
self._host = host
self._requested_port = port
@@ -5500,6 +5685,18 @@ class TrackerServer:
self._models_dir = Path(raw_models_dir).expanduser() if raw_models_dir else None
self._hf_pricing_stop = threading.Event()
self._hf_pricing_thread: threading.Thread | None = None
if routing_config is None:
routing_config = RoutingConfig(
explore_share=float(os.environ.get("MESHNET_ROUTE_EXPLORE_SHARE", RoutingConfig.explore_share)),
weight_alpha=float(os.environ.get("MESHNET_ROUTE_WEIGHT_ALPHA", RoutingConfig.weight_alpha)),
stats_half_life_seconds=float(
os.environ.get("MESHNET_ROUTE_STATS_HALF_LIFE", RoutingConfig.stats_half_life_seconds)
),
min_sample_tokens=int(
os.environ.get("MESHNET_ROUTE_MIN_SAMPLE_TOKENS", RoutingConfig.min_sample_tokens)
),
)
self._route_stats = RouteStatsStore(routing_config)
self.port: int | None = None
def start(self) -> int:
@@ -5537,6 +5734,7 @@ class TrackerServer:
toploc_backend=self._toploc_backend,
hf_pricing_log=self._hf_pricing_log,
models_dir=self._models_dir,
route_stats=self._route_stats,
)
self.port = self._server.server_address[1]