qol improvements
This commit is contained in:
@@ -391,6 +391,171 @@ def test_real_model_startup_summary_shows_total_layers(tmp_path, monkeypatch, ca
|
||||
assert "Node ID: node-test-123" in output
|
||||
|
||||
|
||||
def test_public_tracker_model_node_registers_relay_metadata_from_tracker_url_only(
|
||||
tmp_path,
|
||||
monkeypatch,
|
||||
):
|
||||
"""A node only needs the public tracker URL to discover relay metadata and register."""
|
||||
import meshnet_node.startup as startup_mod
|
||||
|
||||
class FakeBackend:
|
||||
total_layers = 24
|
||||
|
||||
class FakeTorchNodeServer:
|
||||
def __init__(self, **kwargs):
|
||||
self.kwargs = kwargs
|
||||
self.backend = FakeBackend()
|
||||
self.port = None
|
||||
self.chat_completion_count = 0
|
||||
self.total_requests = 0
|
||||
self.failed_requests = 0
|
||||
self.queue_depth = 0
|
||||
|
||||
def start(self):
|
||||
self.port = 8001
|
||||
return self.port
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
class FakeRelayHttpBridge:
|
||||
def __init__(self, relay_url, peer_id, local_base_url, advertised_addr):
|
||||
self.relay_url = relay_url
|
||||
self.peer_id = peer_id
|
||||
self.local_base_url = local_base_url
|
||||
self.advertised_addr = advertised_addr
|
||||
|
||||
@property
|
||||
def relay_addr(self):
|
||||
return f"ws://public-relay.example/rpc/{self.peer_id}"
|
||||
|
||||
def start(self):
|
||||
return types.SimpleNamespace(
|
||||
peer_id=self.peer_id,
|
||||
relay_addr=self.relay_addr,
|
||||
)
|
||||
|
||||
def wait_connected(self, timeout=5.0):
|
||||
return True
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
monkeypatch.setattr(
|
||||
startup_mod,
|
||||
"detect_hardware",
|
||||
lambda: {"device": "cpu", "gpu_name": None, "vram_mb": 0},
|
||||
)
|
||||
monkeypatch.setattr(startup_mod, "_detect_num_layers", lambda _model_id: 24)
|
||||
monkeypatch.setattr(startup_mod, "TorchNodeServer", FakeTorchNodeServer)
|
||||
monkeypatch.setattr(startup_mod, "RelayHttpBridge", FakeRelayHttpBridge)
|
||||
|
||||
tracker = TrackerServer(relay_url="ws://public-relay.example/ws")
|
||||
tracker_port = tracker.start()
|
||||
tracker_url = f"http://127.0.0.1:{tracker_port}"
|
||||
try:
|
||||
node = run_startup(
|
||||
tracker_url=tracker_url,
|
||||
model_id="Qwen/Qwen2.5-0.5B-Instruct",
|
||||
advertise_host="203.0.113.10",
|
||||
wallet_path=tmp_path / "wallet.json",
|
||||
)
|
||||
try:
|
||||
network_map = _get_json(f"{tracker_url}/v1/network/map")
|
||||
finally:
|
||||
node.stop()
|
||||
finally:
|
||||
tracker.stop()
|
||||
|
||||
assert network_map["relay_url"] == "ws://public-relay.example/ws"
|
||||
assert len(network_map["nodes"]) == 1
|
||||
registered = network_map["nodes"][0]
|
||||
assert registered["hf_repo"] == "Qwen/Qwen2.5-0.5B-Instruct"
|
||||
assert registered["endpoint"] == "http://203.0.113.10:8001"
|
||||
assert registered["relay_addr"].startswith("ws://public-relay.example/rpc/")
|
||||
assert registered["peer_id"]
|
||||
|
||||
|
||||
def test_later_node_auto_joins_existing_public_hf_model_with_only_tracker_url(
|
||||
tmp_path,
|
||||
monkeypatch,
|
||||
):
|
||||
"""After a model exists, a node can join by knowing only the public tracker URL."""
|
||||
import meshnet_node.startup as startup_mod
|
||||
|
||||
captured = {}
|
||||
|
||||
class FakeBackend:
|
||||
total_layers = 24
|
||||
|
||||
class FakeTorchNodeServer:
|
||||
def __init__(self, **kwargs):
|
||||
captured.update(kwargs)
|
||||
self.backend = FakeBackend()
|
||||
self.port = None
|
||||
self.chat_completion_count = 0
|
||||
self.total_requests = 0
|
||||
self.failed_requests = 0
|
||||
self.queue_depth = 0
|
||||
|
||||
def start(self):
|
||||
self.port = 8002
|
||||
return self.port
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
monkeypatch.setattr(
|
||||
startup_mod,
|
||||
"detect_hardware",
|
||||
lambda: {"device": "cpu", "gpu_name": None, "vram_mb": 0},
|
||||
)
|
||||
monkeypatch.setattr(startup_mod, "TorchNodeServer", FakeTorchNodeServer)
|
||||
|
||||
tracker = TrackerServer()
|
||||
tracker_port = tracker.start()
|
||||
tracker_url = f"http://127.0.0.1:{tracker_port}"
|
||||
try:
|
||||
data = json.dumps({
|
||||
"endpoint": "http://203.0.113.20:8001",
|
||||
"model": "Qwen2.5-0.5B-Instruct",
|
||||
"hf_repo": "Qwen/Qwen2.5-0.5B-Instruct",
|
||||
"num_layers": 24,
|
||||
"shard_start": 0,
|
||||
"shard_end": 11,
|
||||
"tracker_mode": True,
|
||||
"hardware_profile": {},
|
||||
"score": 1.0,
|
||||
}).encode()
|
||||
req = urllib.request.Request(
|
||||
f"{tracker_url}/v1/nodes/register",
|
||||
data=data,
|
||||
headers={"Content-Type": "application/json"},
|
||||
method="POST",
|
||||
)
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
resp.read()
|
||||
|
||||
node = run_startup(
|
||||
tracker_url=tracker_url,
|
||||
advertise_host="203.0.113.21",
|
||||
wallet_path=tmp_path / "wallet.json",
|
||||
)
|
||||
try:
|
||||
route_resp = _get_json(
|
||||
f"{tracker_url}/v1/route?model=Qwen/Qwen2.5-0.5B-Instruct"
|
||||
)
|
||||
finally:
|
||||
node.stop()
|
||||
finally:
|
||||
tracker.stop()
|
||||
|
||||
assert captured["model_id"] == "Qwen/Qwen2.5-0.5B-Instruct"
|
||||
assert captured["shard_start"] == 12
|
||||
assert captured["shard_end"] == 23
|
||||
assert route_resp["route"] == ["http://203.0.113.20:8001", "http://203.0.113.21:8002"]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Full startup integration test
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -113,6 +113,90 @@ def test_tracker_serves_health_while_proxy_request_is_in_flight():
|
||||
slow_thread.join(timeout=1.0)
|
||||
|
||||
|
||||
def test_tracker_routes_hf_model_alias_from_quickstart():
|
||||
"""The documented qwen2.5-0.5b alias resolves a full HF repo registration."""
|
||||
tracker = TrackerServer()
|
||||
tracker_port = tracker.start()
|
||||
try:
|
||||
_post_json(
|
||||
f"http://127.0.0.1:{tracker_port}/v1/nodes/register",
|
||||
{"endpoint": "http://127.0.0.1:9100",
|
||||
"model": "Qwen2.5-0.5B-Instruct",
|
||||
"hf_repo": "Qwen/Qwen2.5-0.5B-Instruct",
|
||||
"num_layers": 24,
|
||||
"shard_start": 0,
|
||||
"shard_end": 23,
|
||||
"tracker_mode": True,
|
||||
"hardware_profile": {},
|
||||
"score": 1.0},
|
||||
)
|
||||
|
||||
route_resp = _get_json(
|
||||
f"http://127.0.0.1:{tracker_port}/v1/route?model=qwen2.5-0.5b"
|
||||
)
|
||||
finally:
|
||||
tracker.stop()
|
||||
|
||||
assert route_resp["route"] == ["http://127.0.0.1:9100"]
|
||||
|
||||
|
||||
def test_tracker_proxy_accepts_hf_model_alias_from_quickstart():
|
||||
"""The tracker proxy accepts the same model alias used by the quickstart curl."""
|
||||
|
||||
class ChatHandler(http.server.BaseHTTPRequestHandler):
|
||||
def log_message(self, fmt, *args):
|
||||
pass
|
||||
|
||||
def do_POST(self):
|
||||
if self.path != "/v1/chat/completions":
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
return
|
||||
length = int(self.headers.get("Content-Length", 0))
|
||||
request_body = json.loads(self.rfile.read(length) or b"{}")
|
||||
body = json.dumps({
|
||||
"model": request_body["model"],
|
||||
"choices": [{"message": {"content": "56"}}],
|
||||
}).encode()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
node = http.server.HTTPServer(("127.0.0.1", 0), ChatHandler)
|
||||
node_thread = threading.Thread(target=node.serve_forever, daemon=True)
|
||||
node_thread.start()
|
||||
tracker = TrackerServer()
|
||||
tracker_port = tracker.start()
|
||||
try:
|
||||
_post_json(
|
||||
f"http://127.0.0.1:{tracker_port}/v1/nodes/register",
|
||||
{"endpoint": f"http://127.0.0.1:{node.server_address[1]}",
|
||||
"model": "Qwen2.5-0.5B-Instruct",
|
||||
"hf_repo": "Qwen/Qwen2.5-0.5B-Instruct",
|
||||
"num_layers": 24,
|
||||
"shard_start": 0,
|
||||
"shard_end": 23,
|
||||
"tracker_mode": True,
|
||||
"hardware_profile": {},
|
||||
"score": 1.0},
|
||||
)
|
||||
|
||||
response = _post_json(
|
||||
f"http://127.0.0.1:{tracker_port}/v1/chat/completions",
|
||||
{"model": "qwen2.5-0.5b",
|
||||
"messages": [{"role": "user", "content": "What is 7 times 8?"}]},
|
||||
)
|
||||
finally:
|
||||
tracker.stop()
|
||||
node.shutdown()
|
||||
node.server_close()
|
||||
node_thread.join(timeout=1.0)
|
||||
|
||||
assert response["choices"][0]["message"]["content"] == "56"
|
||||
|
||||
|
||||
def test_tracker_registration_node_id_includes_wallet_prefix_and_stable_suffix():
|
||||
tracker = TrackerServer()
|
||||
tracker_port = tracker.start()
|
||||
|
||||
Reference in New Issue
Block a user