main
py 135 lines 3.87 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from typing import Any
4
5 import msgspec
6 import zmq
7 from msgspec.msgpack import Decoder
8
9 from vllm.v1.core.kv_cache_utils import ExternalBlockHash
10
11
12 #
13 # Types copied from vllm.distributed.kv_events
14 #
15 class EventBatch(msgspec.Struct, array_like=True, omit_defaults=True, gc=False):
16 ts: float
17 events: list[Any]
18
19
20 class KVCacheEvent(msgspec.Struct, omit_defaults=True, gc=False, tag=True):
21 """Base class for all KV cache-related events."""
22
23
24 class BlockStored(KVCacheEvent):
25 block_hashes: list[ExternalBlockHash]
26 parent_block_hash: ExternalBlockHash | None
27 token_ids: list[int]
28 block_size: int
29
30 lora_id: int | None
31 """Deprecated: use `lora_name` for KV block key hash.
32 Retained for backward compatibility.
33 """
34
35 medium: str | None
36 lora_name: str | None
37
38 extra_keys: list[tuple[Any, ...] | None] | None = None
39 """Extra keys used in block hash computation, one entry per block in
40 block_hashes. Each entry contains MM identifiers, LoRA name, cache_salt,
41 prompt embeddings data, etc. for that specific block.
42 """
43
44 group_idx: int | None = None
45 kv_cache_spec_kind: str | None = None
46 kv_cache_spec_sliding_window: int | None = None
47 locality: str | None = None
48 session_id: str | None = None
49
50
51 class BlockRemoved(KVCacheEvent):
52 block_hashes: list[ExternalBlockHash]
53 medium: str | None
54 group_idx: int | None = None
55 locality: str | None = None
56
57
58 class AllBlocksCleared(KVCacheEvent):
59 pass
60
61
62 class KVEventBatch(EventBatch):
63 events: list[BlockStored | BlockRemoved | AllBlocksCleared]
64
65
66 def process_event(event_batch):
67 print(f"Received event batch at {event_batch.ts}:")
68 for event in event_batch.events:
69 print(f" - {event}")
70
71
72 def main():
73 decoder = Decoder(type=KVEventBatch)
74 last_seq = -1
75
76 context = zmq.Context()
77
78 # Set up the main subscription socket
79 sub = context.socket(zmq.SUB)
80 sub.connect("tcp://localhost:5557")
81 topic = "kv-events"
82 sub.setsockopt_string(zmq.SUBSCRIBE, topic)
83
84 # Initialize replay socket
85 replay = context.socket(zmq.REQ)
86 replay.connect("tcp://localhost:5558")
87 poller = zmq.Poller()
88 poller.register(replay, zmq.POLLIN)
89
90 print("Listening for KV cache events on topic:", topic)
91
92 while True:
93 try:
94 if sub.poll(50):
95 _, seq_bytes, payload = sub.recv_multipart()
96 seq = int.from_bytes(seq_bytes, "big")
97
98 if last_seq >= 0 and seq > last_seq + 1:
99 missed = seq - last_seq - 1
100 print(
101 f"Missed {missed} messages (last: {last_seq}, current: {seq})"
102 )
103
104 replay.send((last_seq + 1).to_bytes(8, "big"))
105
106 while poller.poll(timeout=200):
107 _, seq_bytes, replay_payload = replay.recv_multipart()
108 if not replay_payload:
109 # End of replay marker is sent as an empty frame
110 # for the payload
111 break
112
113 replay_seq = int.from_bytes(seq_bytes, "big")
114
115 if replay_seq > last_seq:
116 event_batch = decoder.decode(replay_payload)
117 process_event(event_batch)
118 last_seq = replay_seq
119 if replay_seq >= seq - 1:
120 break
121
122 event_batch = decoder.decode(payload)
123 process_event(event_batch)
124
125 # ... do other periodic work or check for shutdown ...
126
127 except KeyboardInterrupt:
128 print("Interrupted")
129 break
130 except Exception as e:
131 print("Error decoding message:", e)
132
133
134 if __name__ == "__main__":
135 main()