md rework. new code
This commit is contained in:
@@ -1680,6 +1680,66 @@ def test_preset_startup_rejects_pinned_shard_above_memory_budget(tmp_path, monke
|
||||
tracker.stop()
|
||||
|
||||
|
||||
def test_network_auto_join_clips_oversized_cpu_assignment(tmp_path, monkeypatch, capsys):
|
||||
"""Old trackers may assign too many CPU layers; node clips before model load."""
|
||||
import meshnet_node.startup as startup_mod
|
||||
|
||||
torch_calls: list[dict] = []
|
||||
registrations: list[dict] = []
|
||||
|
||||
class FakeBackend:
|
||||
total_layers = 40
|
||||
|
||||
class FakeTorchNodeServer:
|
||||
def __init__(self, **kwargs):
|
||||
torch_calls.append(kwargs)
|
||||
self.backend = FakeBackend()
|
||||
self.tracker_node_id = None
|
||||
|
||||
def start(self):
|
||||
return 7000
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
oversized_assignment = {
|
||||
"hf_repo": "unsloth/Qwen3.6-35B-A3B",
|
||||
"model": "qwen3.6-35b-a3b",
|
||||
"shard_start": 0,
|
||||
"shard_end": 36,
|
||||
"num_layers": 40,
|
||||
"gap_found": False,
|
||||
"bytes_per_layer": {"bfloat16": 1_797_594_419},
|
||||
"model_sources": [],
|
||||
}
|
||||
|
||||
monkeypatch.setattr(
|
||||
startup_mod,
|
||||
"detect_hardware",
|
||||
lambda: {"device": "cpu", "gpu_name": None, "vram_mb": 0, "ram_mb": 79 * 1024},
|
||||
)
|
||||
monkeypatch.setattr(startup_mod, "TorchNodeServer", FakeTorchNodeServer)
|
||||
monkeypatch.setattr(startup_mod, "_get_json", lambda *_args, **_kwargs: oversized_assignment)
|
||||
monkeypatch.setattr(startup_mod, "_post_json", lambda _url, payload: registrations.append(payload) or {"node_id": "n1"})
|
||||
monkeypatch.setattr(startup_mod, "_start_heartbeat", lambda *_args, **_kwargs: None)
|
||||
monkeypatch.setattr(startup_mod, "model_metadata_for", lambda *_args, **_kwargs: {"num_layers": 40})
|
||||
|
||||
node = run_startup(
|
||||
tracker_url="http://127.0.0.1:8080",
|
||||
wallet_path=tmp_path / "wallet.json",
|
||||
tracker_source_disabled=True,
|
||||
)
|
||||
try:
|
||||
assert torch_calls[0]["shard_start"] == 0
|
||||
assert torch_calls[0]["shard_end"] == 24
|
||||
assert registrations[0]["shard_end"] == 24
|
||||
output = capsys.readouterr().out
|
||||
assert "CPU-safe runtime budget fits 25/40 layers" in output
|
||||
assert "layers 0-24" in output
|
||||
finally:
|
||||
node.stop()
|
||||
|
||||
|
||||
def test_preset_model_with_hf_repo_loads_torch_backend(tmp_path, monkeypatch, capsys):
|
||||
"""Named presets that advertise hf_repo must load TorchNodeServer, not the stub server."""
|
||||
import meshnet_node.startup as startup_mod
|
||||
@@ -1996,6 +2056,55 @@ def test_network_assign_gap_found_field():
|
||||
tracker.stop()
|
||||
|
||||
|
||||
def test_network_assign_uses_conservative_cpu_runtime_budget():
|
||||
"""CPU assignments leave headroom for partial-load overhead, not just raw weights."""
|
||||
import json as _json
|
||||
import urllib.request as _ur
|
||||
|
||||
tracker = TrackerServer(model_presets={
|
||||
"qwen3.6-35b-a3b": {
|
||||
"hf_repo": "unsloth/Qwen3.6-35B-A3B",
|
||||
"aliases": ["unsloth/Qwen3.6-35B-A3B"],
|
||||
"layers_start": 0,
|
||||
"layers_end": 39,
|
||||
"recommended": True,
|
||||
"bytes_per_layer": {"bfloat16": 1_797_594_419},
|
||||
},
|
||||
})
|
||||
port = tracker.start()
|
||||
try:
|
||||
data = _json.dumps({
|
||||
"endpoint": "http://127.0.0.1:9200",
|
||||
"model": "qwen3.6-35b-a3b",
|
||||
"hf_repo": "unsloth/Qwen3.6-35B-A3B",
|
||||
"num_layers": 40,
|
||||
"shard_start": 0,
|
||||
"shard_end": 39,
|
||||
"hardware_profile": {},
|
||||
"score": 1.0,
|
||||
}).encode()
|
||||
req = _ur.Request(
|
||||
f"http://127.0.0.1:{port}/v1/nodes/register",
|
||||
data=data,
|
||||
headers={"Content-Type": "application/json"},
|
||||
method="POST",
|
||||
)
|
||||
with _ur.urlopen(req) as r:
|
||||
r.read()
|
||||
|
||||
resp = _get_json(
|
||||
f"http://127.0.0.1:{port}/v1/network/assign"
|
||||
"?device=cpu&vram_mb=0&ram_mb=80896"
|
||||
"&hf_repo=unsloth/Qwen3.6-35B-A3B"
|
||||
)
|
||||
|
||||
assert resp["gap_found"] is False
|
||||
assert resp["shard_start"] == 0
|
||||
assert resp["shard_end"] == 24
|
||||
finally:
|
||||
tracker.stop()
|
||||
|
||||
|
||||
def test_route_finds_hf_model_across_two_nodes():
|
||||
"""Tracker /v1/route returns ordered route for HF model even without a preset."""
|
||||
import json as _json
|
||||
|
||||
Reference in New Issue
Block a user