Signed-off-by: linzhenpl07 <linzhenpl07@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
193 lines
6.6 KiB
Python
193 lines
6.6 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
"""Equivalence and session-isolation tests for Cosmos3StateAdapter (RFC #4480).
|
|
|
|
These drive the adapter directly with tiny CPU tensors -- no model, no GPU --
|
|
and assert it stores/returns the per-branch UND K/V exactly and isolates
|
|
durable state between sessions. They do not cover the transformer's mutable
|
|
load-to-forward handoff.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
import torch
|
|
|
|
from vllm_omni.experimental.world_models.adapters.state_cosmos3_adapter import (
|
|
Cosmos3StateAdapter,
|
|
)
|
|
from vllm_omni.experimental.world_models.session_state import SessionStateManager
|
|
|
|
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
|
|
|
LAYERS = 3
|
|
SEQ, DIM = 3, 4
|
|
|
|
|
|
def _fake_cached_kv(seed: int) -> list[tuple[torch.Tensor, torch.Tensor]]:
|
|
"""A list of per-layer (K, V); contents seeded so branches/sessions differ."""
|
|
g = torch.Generator().manual_seed(seed)
|
|
return [(torch.randn(1, SEQ, DIM, generator=g), torch.randn(1, SEQ, DIM, generator=g)) for _ in range(LAYERS)]
|
|
|
|
|
|
def _adapter(session_id: str, manager: SessionStateManager | None = None) -> Cosmos3StateAdapter:
|
|
# `manager is not None`, not `manager or ...`: an empty SessionStateManager is
|
|
# falsy (it defines __len__), so `or` would silently swap in a fresh manager.
|
|
return Cosmos3StateAdapter(session_id, manager if manager is not None else SessionStateManager())
|
|
|
|
|
|
def _assert_kv_equal(got: list | None, expected: list) -> None:
|
|
assert got is not None and len(got) == len(expected) == LAYERS
|
|
for (k, v), (ke, ve) in zip(got, expected):
|
|
torch.testing.assert_close(k, ke)
|
|
torch.testing.assert_close(v, ve)
|
|
|
|
|
|
class _FakeTransformer:
|
|
cached_kv = None
|
|
cached_freqs_gen = None
|
|
|
|
|
|
# ----------------------------- equivalence -----------------------------
|
|
|
|
|
|
def test_branch_starts_uninitialized() -> None:
|
|
a = _adapter("s0")
|
|
for is_neg in (False, True):
|
|
assert a.is_branch_initialized(is_neg) is False
|
|
assert a.get_branch_kv(is_neg) is None
|
|
|
|
|
|
def test_set_then_get_roundtrip() -> None:
|
|
a = _adapter("s0")
|
|
kv = _fake_cached_kv(1)
|
|
a.set_branch_kv(False, kv)
|
|
assert a.is_branch_initialized(False) is True
|
|
_assert_kv_equal(a.get_branch_kv(False), kv)
|
|
|
|
|
|
def test_cond_uncond_branches_isolated() -> None:
|
|
a = _adapter("s0")
|
|
kv_pos, kv_neg = _fake_cached_kv(1), _fake_cached_kv(2)
|
|
a.set_branch_kv(False, kv_pos)
|
|
a.set_branch_kv(True, kv_neg)
|
|
_assert_kv_equal(a.get_branch_kv(False), kv_pos)
|
|
_assert_kv_equal(a.get_branch_kv(True), kv_neg)
|
|
|
|
|
|
def test_encode_once_capture_is_noop_when_initialized() -> None:
|
|
a = _adapter("s0")
|
|
kv = _fake_cached_kv(1)
|
|
a.set_branch_kv(False, kv)
|
|
tr = _FakeTransformer()
|
|
tr.cached_kv = _fake_cached_kv(999) # different content
|
|
a.capture_from_transformer(tr, False)
|
|
_assert_kv_equal(a.get_branch_kv(False), kv) # still the first one
|
|
|
|
|
|
def test_capture_writes_when_uninitialized() -> None:
|
|
a = _adapter("s0")
|
|
tr = _FakeTransformer()
|
|
tr.cached_kv = _fake_cached_kv(5)
|
|
tr.cached_freqs_gen = (torch.ones(2), torch.zeros(2))
|
|
a.capture_from_transformer(tr, False)
|
|
_assert_kv_equal(a.get_branch_kv(False), _fake_cached_kv(5))
|
|
|
|
|
|
def test_capture_without_freqs_does_not_partially_initialize() -> None:
|
|
a = _adapter("s0")
|
|
tr = _FakeTransformer()
|
|
tr.cached_kv = _fake_cached_kv(5)
|
|
tr.cached_freqs_gen = None
|
|
with pytest.raises(RuntimeError, match="did not produce GEN RoPE frequencies"):
|
|
a.capture_from_transformer(tr, False)
|
|
assert a.is_branch_initialized(False) is False
|
|
|
|
|
|
def test_load_into_transformer_without_freqs_sets_kv() -> None:
|
|
a = _adapter("s0")
|
|
kv = _fake_cached_kv(1)
|
|
a.set_branch_kv(False, kv)
|
|
tr = _FakeTransformer()
|
|
a.load_into_transformer(tr, False)
|
|
_assert_kv_equal(tr.cached_kv, kv)
|
|
assert tr.cached_freqs_gen is None
|
|
|
|
|
|
def test_set_then_get_preserves_per_layer_values() -> None:
|
|
# The stored value must reflect the tensors passed in (per-layer).
|
|
a = _adapter("s0")
|
|
kv = _fake_cached_kv(3)
|
|
a.set_branch_kv(False, kv)
|
|
_assert_kv_equal(a.get_branch_kv(False), kv)
|
|
|
|
|
|
def test_load_into_transformer_restores_kv_and_freqs_gen() -> None:
|
|
a = _adapter("s0")
|
|
tr = _FakeTransformer()
|
|
tr.cached_kv = _fake_cached_kv(3)
|
|
tr.cached_freqs_gen = (torch.ones(2), torch.zeros(2))
|
|
a.capture_from_transformer(tr, False)
|
|
tr.cached_kv = None
|
|
tr.cached_freqs_gen = None
|
|
a.load_into_transformer(tr, False)
|
|
_assert_kv_equal(tr.cached_kv, _fake_cached_kv(3))
|
|
assert tr.cached_freqs_gen is not None
|
|
torch.testing.assert_close(tr.cached_freqs_gen[0], torch.ones(2))
|
|
|
|
|
|
def test_partial_branch_raises() -> None:
|
|
a = _adapter("s0")
|
|
a.set_branch_kv(False, _fake_cached_kv(1))
|
|
a._session.get("und_kv/1").reset() # type: ignore[union-attr]
|
|
with pytest.raises(RuntimeError, match="partially initialized"):
|
|
a.get_branch_kv(False)
|
|
|
|
|
|
# ----------------------------- reset -----------------------------
|
|
|
|
|
|
def test_reset_clears_both_branches() -> None:
|
|
a = _adapter("s0")
|
|
a.set_branch_kv(False, _fake_cached_kv(1))
|
|
a.set_branch_kv(True, _fake_cached_kv(2))
|
|
a.reset()
|
|
for is_neg in (False, True):
|
|
assert a.is_branch_initialized(is_neg) is False
|
|
assert a.get_branch_kv(is_neg) is None
|
|
|
|
|
|
# ----------------------- concurrency isolation (core) -----------------------
|
|
|
|
|
|
def test_two_sessions_do_not_clobber() -> None:
|
|
manager = SessionStateManager()
|
|
a, b = _adapter("A", manager), _adapter("B", manager)
|
|
kv_a, kv_b = _fake_cached_kv(10), _fake_cached_kv(20)
|
|
a.set_branch_kv(False, kv_a)
|
|
assert b.is_branch_initialized(False) is False # B not seeing A's state
|
|
b.set_branch_kv(False, kv_b)
|
|
_assert_kv_equal(a.get_branch_kv(False), kv_a)
|
|
_assert_kv_equal(b.get_branch_kv(False), kv_b)
|
|
|
|
|
|
def test_fresh_adapter_same_session_sees_prior_state() -> None:
|
|
manager = SessionStateManager()
|
|
kv = _fake_cached_kv(7)
|
|
_adapter("s", manager).set_branch_kv(False, kv)
|
|
again = Cosmos3StateAdapter("s", manager)
|
|
_assert_kv_equal(again.get_branch_kv(False), kv)
|
|
|
|
|
|
def test_adapter_survives_session_lru_eviction() -> None:
|
|
# An in-use adapter keeps its state even after the manager evicts its
|
|
# session id from the lookup table (LRU bounded by max_sessions).
|
|
manager = SessionStateManager(max_sessions=2)
|
|
a = _adapter("active", manager)
|
|
kv = _fake_cached_kv(11)
|
|
a.set_branch_kv(False, kv)
|
|
for i in range(3):
|
|
_adapter(f"other{i}", manager)
|
|
assert "active" not in manager
|
|
_assert_kv_equal(a.get_branch_kv(False), kv)
|