Files
icassp2027-experiment/test_streaming_multiwindow.py
T

300 lines
17 KiB
Python

"""CPU cache semantics, output routing and both feature-loader contracts."""
from __future__ import annotations
import copy
import json
import tempfile
import types
import unittest
from pathlib import Path
from unittest import mock
import numpy as np
import torch
import extract_easycom_multiwindow as driver
import streaming_turn_model as core
import streaming_turn_model_directly as direct
from easycom_head import load_session_data
from local_turn_features import LOCAL_FORMAT, load_local_features, reference_alignment
from streaming_turn_multiwindow import DEFAULT_MEMORY_SECONDS, MultiWindowOpenStreamReplayExtractor, validate_memory_seconds
class FakeModel:
config = types.SimpleNamespace(seconds_per_chunk=2.0, position_id_per_seconds=25.0,
text_config=types.SimpleNamespace(hidden_size=3))
device = torch.device("cpu")
dtype = torch.float32
class FakeCache:
def __init__(self):
self.tokens = []
def append(model, pieces, *, past_key_values, rope_offset, include_prefix):
cache = FakeCache() if past_key_values is None else past_key_values
if include_prefix != (not cache.tokens):
raise AssertionError("prefix ownership mismatch or readout committed")
values = pieces.payload_embeddings.flatten().tolist()
# Mutation exercises clone_cache isolation, not a conveniently immutable fake.
cache.tokens.append((values, rope_offset))
return cache, rope_offset + 1, pieces.closure_position_ids
def read(model, cache, *, last_n_layers=1, layer_fusion="mean", **kwargs):
value = 0.0
for values, offset in cache.tokens:
value = value * 1.17 + sum(values) + offset / 100
state = torch.tensor([[value, cache.tokens[0][0][0], cache.tokens[-1][0][1]]])
if layer_fusion == "stack":
return torch.stack([state + layer for layer in range(last_n_layers)], dim=1)
return state
class MultiwindowTests(unittest.TestCase):
def setUp(self):
self.prepared = []
self.future_change = False
def prepare(extractor, *, interval, **kwargs):
start, end = interval
self.prepared.append(interval)
signal = 1000.0 if self.future_change and end > 13.0 else 0.0
return core.OpenMediaPieces(
prefix_embeddings=torch.zeros(1, 1, 3),
prefix_position_ids=torch.zeros(3, 1, 1, dtype=torch.long),
payload_embeddings=torch.tensor([[[start, end, signal]]]),
payload_position_ids=torch.ones(3, 1, 1, dtype=torch.long),
closure_ids=torch.zeros(1, 1, dtype=torch.long),
closure_position_ids=torch.ones(3, 1, 1, dtype=torch.long),
)
for patch in (
mock.patch.object(core, "_readout_query_ids", return_value=torch.zeros(1, 1, dtype=torch.long)),
mock.patch.object(core.OpenStreamReplayExtractor, "_prepare_pieces", new=prepare),
mock.patch.object(core, "_append_open_pieces", side_effect=append),
mock.patch.object(core, "_read_open_state", side_effect=read),
):
patch.start()
self.addCleanup(patch.stop)
self.config = core.StreamConfig(memory_seconds=12, readout_last_n_layers=4, readout_layer_fusion="stack")
self.kwargs = dict(video_path="unused.mp4", audio_path=None, start=0.0, end=24.0,
use_audio_in_video=True, show_progress=False)
def multi(self, **kwargs):
extractor = MultiWindowOpenStreamReplayExtractor(
FakeModel(), object(), self.config, memory_seconds_list=DEFAULT_MEMORY_SECONDS
)
return extractor.extract(**{**self.kwargs, **kwargs})
def test_all_caps_match_separate_single_window_extractions_and_encode_once(self):
results = self.multi()
self.assertEqual(len(self.prepared), 120)
self.assertEqual(len(set(self.prepared)), 120)
for cap, result in results.items():
reference = core.OpenStreamReplayExtractor(
FakeModel(), object(), core.StreamConfig(memory_seconds=cap,
readout_last_n_layers=4, readout_layer_fusion="stack")
).extract(**self.kwargs)
self.assertTrue(torch.equal(result.times, reference.times))
self.assertTrue(torch.equal(result.states, reference.states), f"mismatch cap={cap}")
self.assertEqual(result.states.shape, (120, 4, 3))
self.assertEqual(result.diagnostics["production_processor_calls_shared"], 120)
self.assertTrue((result.diagnostics["actual_context_seconds"] <= cap + 1e-6).all())
self.assertTrue(torch.equal(result.diagnostics["state_source_times"], result.times))
self.assertEqual(result.diagnostics["payload_commit_mask"].sum().item(), 12)
# Representative physical timeline, independent of any learned scores.
index = 102 # cutoff=20.6 s
print("Timeline cutoff=20.6s, local A=[19.6,20.6), horizon0.2 target=20.8s")
for cap, result in results.items():
start = result.diagnostics["history_start_times"][index].item()
self.assertAlmostEqual(start, 22.0 - cap)
print(f" cap={cap:g}s global=[{start:g},20.6) age=0s")
def test_independent_replay_verification_and_no_future_backfill(self):
original = self.multi(verify_every=11)
for result in original.values():
self.assertEqual(len(result.verification), 10)
self.assertTrue(all(check["max_abs"] == 0 for check in result.verification))
self.future_change = True
changed = self.multi()
for cap in original:
self.assertTrue(torch.equal(original[cap].states[:65], changed[cap].states[:65]))
self.assertFalse(torch.equal(original[cap].states[65:], changed[cap].states[65:]))
def test_repeated_extract_resets_history_and_supports_mean(self):
extractor = MultiWindowOpenStreamReplayExtractor(
FakeModel(), object(), core.StreamConfig(), memory_seconds_list=[2, 4]
)
first = extractor.extract(**self.kwargs)
second = extractor.extract(**self.kwargs)
self.assertEqual(first[2].states.shape, (120, 3))
self.assertTrue(torch.equal(first[4].states, second[4].states))
for kwargs in ({"start": 0.2}, {"end": 0.1}, {"end": float("nan")}, {"verify_every": -1}):
with self.assertRaises(ValueError):
extractor.extract(**{**self.kwargs, **kwargs})
def test_invalid_caps(self):
for caps in ([], [2, 2], [2, 2.000000001], [0], [1], [3], [float("nan")], [float("inf")]):
with self.subTest(caps=caps), self.assertRaises(ValueError):
validate_memory_seconds(caps, 2)
def test_export_loads_both_head_and_same_local_audio_and_rejects_bad_alignment(self):
results = self.multi(end=4)
n = 20
payload = {"mode": "open-stream-replay", "model_id": "synthetic", "start": 0.0,
"num_steps": n, "target_path": "synthetic.npy", "config": {"step_seconds": .2},
"label_indices": torch.arange(n), "open_stream_policy": {}}
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
target_root = root / "targets"
target_root.mkdir()
targets = np.tile(np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 1, 1]], dtype=np.float32), (5, 1))
np.save(target_root / "Session_4.npy", targets)
paths = direct.save_multiwindow_banks(payload, results, root / "Session_4.pt")
side = {"format": LOCAL_FORMAT, "dataset": "easycom", "sample_id": "Session_4",
"partial": False, "times": results[2].times.clone(), "label_indices": torch.arange(n),
"audio": torch.ones(n, 5), "audio_valid": torch.ones(n, dtype=torch.bool),
"config": {"audio_dim": 5, "causal_policy": "half-open-trailing-window",
"uses_speech_labels": False, "encoder_id": "synthetic"}}
side_path = root / "audio.pt"
torch.save(side, side_path)
for cap, path in paths.items():
bank = torch.load(path, weights_only=False)
loaded = load_session_data(path.parent, target_root, 4, expected_mode="open-stream-replay")
self.assertTrue(torch.equal(loaded.states, results[cap].states))
local, _ = load_local_features(side_path, bank, sample_id="Session_4", dataset="easycom", modalities="audio")
self.assertEqual(local.shape, (n, 6)) # encoder vector + availability
self.assertFalse(bank["persistent_thinker_kv"])
self.assertEqual(bank["config"]["memory_seconds"], cap)
self.assertFalse(bank["partial_extraction"])
for indices in (torch.arange(n) + 1, torch.arange(n - 1)):
broken = {**bank, "label_indices": indices}
torch.save(broken, path)
with self.assertRaises(ValueError):
load_session_data(path.parent, target_root, 4, expected_mode="open-stream-replay")
with self.assertRaises(ValueError):
load_local_features(side_path, broken, sample_id="Session_4", dataset="easycom", modalities="audio")
torch.save(bank, path)
for broken in ({**bank, "times": bank["times"] + .2}, {**bank, "start": .2}):
with self.assertRaises(ValueError):
reference_alignment(broken)
with self.assertRaises(ValueError):
load_session_data(path.parent, target_root, 4) # Wrong mode.
with self.assertRaises(FileExistsError):
direct.save_multiwindow_banks(payload, results, root / "Session_4.pt")
bad = copy.deepcopy(results)
bad[12].states[0, 0, 0] = float("nan")
with self.assertRaises(ValueError):
direct.save_multiwindow_banks(payload, bad, root / "fresh" / "Session_4.pt")
self.assertFalse((root / "fresh" / "context_2s" / "Session_4.pt").exists())
class DriverTests(unittest.TestCase):
def test_driver_loads_model_once_across_sessions_and_writes_summary(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
target_root = root / "targets"
target_root.mkdir()
for session in ("Session_4", "Session_5"):
(root / session).mkdir()
np.save(target_root / f"{session}.npy", np.tile(np.array([[1, 0, 0]], dtype=np.float32), (20, 1)))
model, processor = object(), object()
def run(command, *, model=None, processor=None):
self.assertIs(model, loaded_model)
self.assertIs(processor, loaded_processor)
args = direct.parse_args(command)
times = (torch.arange(20, dtype=torch.float64) + 1) / 5
results = {cap: core.ExtractionResult(
times, torch.ones(20, 8), [], {"actual_context_seconds": torch.full((20,), cap)}
) for cap in args.memory_seconds_list}
payload = {"mode": "open-stream-replay", "start": 0, "num_steps": 20,
"target_path": str(args.target), "config": {"step_seconds": .2},
"label_indices": torch.arange(20), "open_stream_policy": {}}
direct.save_multiwindow_banks(payload, results, args.output)
loaded_model, loaded_processor = model, processor
with mock.patch.object(direct, "_easycom_directory_specs"), \
mock.patch.object(core, "load_qwen_omni", return_value=(model, processor)) as loader, \
mock.patch.object(direct, "main", side_effect=run) as runner:
driver.main(["--easycom-video-root", str(root), "--target-root", str(target_root),
"--sessions", "Session_4", "Session_5", "--memory-seconds-list", "2", "4",
"--output-root", str(root / "output")])
loader.assert_called_once()
self.assertEqual(runner.call_count, 2)
summary = json.loads((root / "output" / "extraction_summary.json").read_text())
self.assertEqual(set(summary["sessions"]), {"Session_4", "Session_5"})
self.assertFalse(summary["partial_extraction"])
self.assertEqual(summary["sessions"]["Session_5"]["banks"]["4s"]["shape"], [20, 8])
def test_direct_main_routes_multi_banks_and_preserves_scalar_output(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
times = (torch.arange(20, dtype=torch.float64) + 1) / 5
result = core.ExtractionResult(times, torch.ones(20, 8), [], {})
reader = types.SimpleNamespace(
clips=[types.SimpleNamespace(path=Path("unused.mp4"))], metadata=lambda: {"synthetic": True}
)
model = FakeModel()
model.eval = lambda: model
model.requires_grad_ = lambda value: model
base = ["--video", "unused.mp4", "--end", "4", "--output", str(root / "Session_4.pt")]
with mock.patch.object(direct, "CausalSourceAV", return_value=reader), \
mock.patch.object(direct, "_print_media_summary"), \
mock.patch.object(core, "backbone_metadata", return_value={"loader": "unknown"}), \
mock.patch.object(MultiWindowOpenStreamReplayExtractor, "__init__", return_value=None), \
mock.patch.object(MultiWindowOpenStreamReplayExtractor, "extract", return_value={2: result, 4: result}):
direct.main(base + ["--memory-seconds-list", "2", "4"], model=model, processor=object())
self.assertFalse((root / "Session_4.pt").exists())
bank = torch.load(root / "context_2s" / "Session_4.pt", weights_only=False)
self.assertEqual(bank["config"]["memory_seconds"], 2)
self.assertFalse(bank["persistent_thinker_kv"])
self.assertTrue(bank["partial_extraction"])
with mock.patch.object(direct, "CausalSourceAV", return_value=reader), \
mock.patch.object(direct, "_print_media_summary"), \
mock.patch.object(core, "backbone_metadata", return_value={"loader": "unknown"}), \
mock.patch.object(core.OpenStreamReplayExtractor, "__init__", return_value=None), \
mock.patch.object(core.OpenStreamReplayExtractor, "extract", return_value=result):
direct.main(base + ["--memory-seconds", "4"], model=model, processor=object())
self.assertTrue((root / "Session_4.pt").exists())
def test_cli_preflight_and_partial_target_policy(self):
with tempfile.TemporaryDirectory() as directory, mock.patch.object(direct, "_easycom_directory_specs"):
root = Path(directory)
(root / "Session_4").mkdir()
common = ["--easycom-video-root", str(root), "--sessions", "Session_4",
"--output-root", str(root / "output"), "--end", "24"]
args = driver.parse_args(common)
commands = driver.session_commands(args)
self.assertNotIn("--target", commands[0][1])
self.assertIn("--memory-seconds-list", commands[0][1])
self.assertEqual(direct.parse_args(commands[0][1]).readout_last_n_layers, 4)
with mock.patch.object(core, "load_qwen_omni") as loader, mock.patch.object(direct, "main") as main:
driver.main(common + ["--media-only"])
loader.assert_not_called()
main.assert_called_once()
for extra in (["--sessions", "Session_99"], ["--sessions", "Session_4", "Session_4"],
["--memory-seconds-list", "3"], ["--end", "0.1"]):
with self.assertRaises(ValueError):
driver.session_commands(driver.parse_args(common + extra))
existing = root / "output" / "context_2s" / "Session_4.pt"
existing.parent.mkdir(parents=True)
existing.touch()
with self.assertRaises(FileExistsError):
driver.session_commands(args)
def test_direct_legacy_parser_and_multi_conflicts(self):
base = ["--video", "unused.mp4", "--output", "unused.pt", "--end", "24"]
self.assertIsNone(direct.parse_args(base).memory_seconds_list)
for extra in (["--mode", "causal-prefix"], ["--memory-seconds", "8"],
["--head-checkpoint", "head.pt"], ["--start", ".2"]):
with self.assertRaises(SystemExit):
direct.main(base + ["--memory-seconds-list", "2", "4"] + extra)
if __name__ == "__main__":
unittest.main()