wip -more responsive UI, better routing
This commit is contained in:
@@ -376,6 +376,92 @@ def test_split_shard_chat_streams_each_generated_token_incrementally():
|
||||
assert "data: [DONE]" in rest
|
||||
|
||||
|
||||
def test_current_requests_snapshot_while_generating():
|
||||
release_second = threading.Event()
|
||||
head = TorchNodeServer(backend=_FakePipelineHeadBackend(), tracker_mode=True)
|
||||
tail = TorchNodeServer(backend=_BlockingStreamingTailBackend(release_second))
|
||||
head_port = head.start()
|
||||
tail_port = tail.start()
|
||||
response = None
|
||||
try:
|
||||
payload = json.dumps({
|
||||
"model": "fake-model",
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"stream": True,
|
||||
"max_tokens": 2,
|
||||
}).encode()
|
||||
req = urllib.request.Request(
|
||||
f"http://127.0.0.1:{head_port}/v1/chat/completions",
|
||||
data=payload,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-Meshnet-Request-Id": "req-live-1",
|
||||
"X-Meshnet-Route": json.dumps([
|
||||
{"endpoint": f"http://127.0.0.1:{tail_port}", "start_layer": 22},
|
||||
]),
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
response = urllib.request.urlopen(req, timeout=5)
|
||||
deadline = time.time() + 2.0
|
||||
while time.time() < deadline:
|
||||
live = head.current_requests
|
||||
if live and live[0]["request_id"] == "req-live-1" and live[0]["tokens"] >= 1:
|
||||
break
|
||||
time.sleep(0.02)
|
||||
assert head.current_requests
|
||||
snap = head.current_requests[0]
|
||||
assert snap["request_id"] == "req-live-1"
|
||||
assert snap["tokens"] >= 1
|
||||
assert snap["tokens_per_sec"] >= 0
|
||||
assert snap["routing_complete"] is True
|
||||
release_second.set()
|
||||
response.read()
|
||||
finally:
|
||||
release_second.set()
|
||||
if response is not None:
|
||||
response.close()
|
||||
head.stop()
|
||||
tail.stop()
|
||||
|
||||
assert head.current_requests == []
|
||||
|
||||
|
||||
def test_distributed_generating_log_includes_tps(capsys):
|
||||
head = TorchNodeServer(backend=_FakePipelineHeadBackend(), tracker_mode=True)
|
||||
tail = TorchNodeServer(backend=_FakePipelineTailBackend())
|
||||
head_port = head.start()
|
||||
tail_port = tail.start()
|
||||
try:
|
||||
payload = json.dumps({
|
||||
"model": "fake-model",
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
"max_tokens": 1,
|
||||
}).encode()
|
||||
req = urllib.request.Request(
|
||||
f"http://127.0.0.1:{head_port}/v1/chat/completions",
|
||||
data=payload,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"X-Meshnet-Route": json.dumps([
|
||||
{"endpoint": f"http://127.0.0.1:{tail_port}", "start_layer": 22},
|
||||
]),
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=5) as resp:
|
||||
json.loads(resp.read())
|
||||
finally:
|
||||
head.stop()
|
||||
tail.stop()
|
||||
|
||||
out = capsys.readouterr().out
|
||||
assert "generating step=1/1" in out
|
||||
assert " tps=" in out
|
||||
assert "generation complete tokens=1" in out
|
||||
assert out.count("generating step=1/1") == 1
|
||||
|
||||
|
||||
def test_int_tensor_header_serializes_torch_tensors():
|
||||
torch = pytest.importorskip("torch")
|
||||
|
||||
|
||||
@@ -1498,6 +1498,70 @@ def test_tracker_heartbeat_updates_node():
|
||||
tracker.stop()
|
||||
|
||||
|
||||
def test_tracker_heartbeat_stores_current_requests():
|
||||
"""Node-reported in-flight request snapshots appear on the network map."""
|
||||
tracker = TrackerServer()
|
||||
tracker_port = tracker.start()
|
||||
try:
|
||||
reg = _post_json(
|
||||
f"http://127.0.0.1:{tracker_port}/v1/nodes/register",
|
||||
{
|
||||
"endpoint": "http://127.0.0.1:9001",
|
||||
"model": "progress-model",
|
||||
"shard_start": 0,
|
||||
"shard_end": 31,
|
||||
"hardware_profile": {},
|
||||
"score": 1.0,
|
||||
},
|
||||
)
|
||||
node_id = reg["node_id"]
|
||||
_post_json(
|
||||
f"http://127.0.0.1:{tracker_port}/v1/nodes/{node_id}/heartbeat",
|
||||
{
|
||||
"queue_depth": 1,
|
||||
"current_requests": [{
|
||||
"request_id": "req-abc123",
|
||||
"model": "progress-model",
|
||||
"kind": "chat",
|
||||
"tokens": 17,
|
||||
"elapsed_seconds": 42.5,
|
||||
"tokens_per_sec": 0.4,
|
||||
"routing_complete": True,
|
||||
}],
|
||||
},
|
||||
)
|
||||
network = _get_json(f"http://127.0.0.1:{tracker_port}/v1/network/map")
|
||||
node = next(item for item in network["nodes"] if item["node_id"] == node_id)
|
||||
assert node["stats"]["queue_depth"] == 1
|
||||
assert node["stats"]["current_requests"] == [{
|
||||
"request_id": "req-abc123",
|
||||
"model": "progress-model",
|
||||
"kind": "chat",
|
||||
"tokens": 17,
|
||||
"elapsed_seconds": 42.5,
|
||||
"tokens_per_sec": 0.4,
|
||||
"routing_complete": True,
|
||||
}]
|
||||
finally:
|
||||
tracker.stop()
|
||||
|
||||
|
||||
def test_normalize_current_requests_sanitizes_payload():
|
||||
from meshnet_tracker.server import _normalize_current_requests
|
||||
|
||||
assert _normalize_current_requests(None) == []
|
||||
assert _normalize_current_requests([
|
||||
{"request_id": "req-1", "model": "m", "tokens": "9", "tokens_per_sec": "1.5"},
|
||||
{"model": "missing-id"},
|
||||
"bad",
|
||||
]) == [{
|
||||
"request_id": "req-1",
|
||||
"model": "m",
|
||||
"tokens": 9,
|
||||
"tokens_per_sec": 1.5,
|
||||
}]
|
||||
|
||||
|
||||
def test_tracker_heartbeat_expiry():
|
||||
"""Nodes that miss their heartbeat window are excluded from routes."""
|
||||
tracker = TrackerServer(heartbeat_timeout=0.05) # 50 ms
|
||||
|
||||
Reference in New Issue
Block a user