59a0a3844c
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
139 lines
3.9 KiB
Python
139 lines
3.9 KiB
Python
from types import SimpleNamespace
|
|
|
|
import msgspec
|
|
import pytest
|
|
|
|
from tokenspeed.runtime.pd.kv_events import (
|
|
BlockRemoved,
|
|
BlockStored,
|
|
EventPublisherFactory,
|
|
KVEventBatch,
|
|
KVEventsConfig,
|
|
NullEventPublisher,
|
|
drain_scheduler_kv_events,
|
|
scheduler_kv_event_to_wire_event,
|
|
)
|
|
|
|
|
|
class _FakePublisher(NullEventPublisher):
|
|
def __init__(self, attn_dp_rank: int = 0, **kwargs):
|
|
super().__init__(attn_dp_rank=attn_dp_rank)
|
|
self.kwargs = kwargs
|
|
|
|
|
|
def test_vllm_style_enable_kv_cache_events_config_is_accepted() -> None:
|
|
config = KVEventsConfig.from_cli(
|
|
'{"publisher":"zmq","endpoint":"tcp://*:5557","enable_kv_cache_events":true}'
|
|
)
|
|
|
|
assert config.enable_kv_cache_events is True
|
|
assert EventPublisherFactory.is_enabled(
|
|
'{"publisher":"zmq","endpoint":"tcp://*:5557","enable_kv_cache_events":true}'
|
|
)
|
|
|
|
|
|
def test_factory_returns_null_publisher_when_events_are_disabled() -> None:
|
|
publisher = EventPublisherFactory.create(
|
|
'{"publisher":"zmq","endpoint":"tcp://*:5557","enable_kv_cache_events":false}',
|
|
attn_dp_rank=3,
|
|
)
|
|
|
|
assert isinstance(publisher, NullEventPublisher)
|
|
|
|
|
|
def test_enable_only_config_defaults_to_zmq_publisher() -> None:
|
|
original = EventPublisherFactory._registry["zmq"]
|
|
EventPublisherFactory._registry["zmq"] = _FakePublisher
|
|
try:
|
|
publisher = EventPublisherFactory.create(
|
|
'{"enable_kv_cache_events":true}',
|
|
attn_dp_rank=4,
|
|
)
|
|
finally:
|
|
EventPublisherFactory._registry["zmq"] = original
|
|
|
|
assert isinstance(publisher, _FakePublisher)
|
|
|
|
|
|
def test_scheduler_block_stored_translation() -> None:
|
|
event = SimpleNamespace(
|
|
kind="BlockStored",
|
|
block_hashes=[123],
|
|
parent_block_hash=None,
|
|
token_ids=[1, 2, 3, 4],
|
|
block_size=4,
|
|
)
|
|
|
|
wire_event = scheduler_kv_event_to_wire_event(event)
|
|
|
|
assert wire_event == BlockStored(
|
|
block_hashes=[123],
|
|
parent_block_hash=None,
|
|
token_ids=[1, 2, 3, 4],
|
|
block_size=4,
|
|
)
|
|
|
|
|
|
def test_scheduler_block_removed_translation() -> None:
|
|
event = SimpleNamespace(kind="BlockRemoved", block_hashes=[123, 456])
|
|
|
|
wire_event = scheduler_kv_event_to_wire_event(event)
|
|
|
|
assert wire_event == BlockRemoved(block_hashes=[123, 456])
|
|
|
|
|
|
def test_scheduler_translation_uses_event_kind_not_shape() -> None:
|
|
event = SimpleNamespace(
|
|
kind="FutureSchedulerEvent",
|
|
block_hashes=[123],
|
|
token_ids=[1, 2],
|
|
)
|
|
|
|
with pytest.raises(TypeError, match="FutureSchedulerEvent"):
|
|
scheduler_kv_event_to_wire_event(event)
|
|
|
|
|
|
def test_drain_scheduler_kv_events_skips_binding_when_disabled() -> None:
|
|
assert drain_scheduler_kv_events(object(), enabled=False) == []
|
|
|
|
|
|
def test_drain_scheduler_kv_events_errors_clearly_when_binding_is_missing() -> None:
|
|
with pytest.raises(RuntimeError, match="Scheduler.drain_kv_events"):
|
|
drain_scheduler_kv_events(object(), enabled=True)
|
|
|
|
|
|
def test_drain_scheduler_kv_events_returns_scheduler_events() -> None:
|
|
event = SimpleNamespace(block_hashes=[123])
|
|
scheduler = SimpleNamespace(drain_kv_events=lambda: [event])
|
|
|
|
assert drain_scheduler_kv_events(scheduler, enabled=True) == [event]
|
|
|
|
|
|
def test_kv_event_batch_msgpack_shape_is_dynamo_compatible() -> None:
|
|
payload = msgspec.msgpack.encode(
|
|
KVEventBatch(
|
|
ts=1.5,
|
|
events=[
|
|
BlockStored(
|
|
block_hashes=[123],
|
|
parent_block_hash=None,
|
|
token_ids=[1, 2],
|
|
block_size=2,
|
|
),
|
|
BlockRemoved(block_hashes=[123]),
|
|
],
|
|
attn_dp_rank=2,
|
|
)
|
|
)
|
|
|
|
decoded = msgspec.msgpack.decode(payload)
|
|
|
|
assert decoded == [
|
|
1.5,
|
|
[
|
|
["BlockStored", [123], None, [1, 2], 2],
|
|
["BlockRemoved", [123]],
|
|
],
|
|
2,
|
|
]
|