tenseleyflow/documentlanguagemodel / 9896d14

Browse files

test(integration): pack → unpack → prompt byte-identical at temp=0 (sprint 14.5)

Authored by espadonne
SHA
9896d141c8b9289f5429028d1863bbe2e858705e
Parents
dad3103
Tree
755deff

1 changed file

StatusFile+-
A tests/integration/pack/test_prompt_round_trip.py 106 0
tests/integration/pack/test_prompt_round_trip.pyadded
@@ -0,0 +1,106 @@
1
+"""Pack → unpack → `dlm prompt` produces byte-identical output (Sprint 14).
2
+
3
+Sprint 14 DoD §1: "pack a tiny-model store → delete original → unpack →
4
+`dlm prompt` works and produces the same output as before pack". The
5
+structural round-trip check lives in `test_round_trip.py`; this test
6
+owns the prompt-output invariant at `temperature=0` (greedy decoding,
7
+deterministic when weights are bit-identical).
8
+
9
+Doesn't destroy the shared `trained_store.home` — other session tests
10
+still need it. Instead, unpacks into a fresh home and compares
11
+generations from the two live stores side by side.
12
+"""
13
+
14
+from __future__ import annotations
15
+
16
+import os
17
+from pathlib import Path
18
+
19
+import pytest
20
+from typer.testing import CliRunner
21
+
22
+pytestmark = pytest.mark.slow
23
+
24
+
25
+_PROMPT_QUERY = "hello"
26
+_PROMPT_MAX_TOKENS = 32
27
+
28
+
29
+def _run_prompt(home: Path, doc: Path) -> str:
30
+    """Invoke `dlm prompt --temp 0` under the given home. Return stdout."""
31
+    from dlm.cli.app import app
32
+
33
+    os.environ["DLM_HOME"] = str(home)
34
+    runner = CliRunner(mix_stderr=False)
35
+    result = runner.invoke(
36
+        app,
37
+        [
38
+            "prompt",
39
+            str(doc),
40
+            _PROMPT_QUERY,
41
+            "--temp",
42
+            "0",
43
+            "--max-tokens",
44
+            str(_PROMPT_MAX_TOKENS),
45
+        ],
46
+    )
47
+    assert result.exit_code == 0, f"prompt failed: {result.stderr}"
48
+    return result.stdout
49
+
50
+
51
+@pytest.mark.slow
52
+def test_pack_unpack_prompt_is_byte_identical(trained_store, tmp_path: Path) -> None:
53
+    """Pre-pack prompt output == post-unpack prompt output (greedy decode)."""
54
+    from dlm.cli.app import app
55
+
56
+    offline_vars = ("HF_HUB_OFFLINE", "TRANSFORMERS_OFFLINE", "HF_DATASETS_OFFLINE")
57
+    saved = {k: os.environ.pop(k, None) for k in offline_vars}
58
+
59
+    try:
60
+        pre = _run_prompt(trained_store.home, trained_store.doc)
61
+
62
+        # Pack under the original home.
63
+        pack_path = tmp_path / "round-trip.pack"
64
+        runner = CliRunner()
65
+        pack_result = runner.invoke(
66
+            app,
67
+            [
68
+                "pack",
69
+                str(trained_store.doc),
70
+                "--out",
71
+                str(pack_path),
72
+            ],
73
+        )
74
+        assert pack_result.exit_code == 0, pack_result.output
75
+        assert pack_path.is_file()
76
+
77
+        # Unpack into a fresh home. The original store stays intact so
78
+        # other session-scoped tests aren't affected.
79
+        fresh_home = tmp_path / "restored-home"
80
+        fresh_home.mkdir()
81
+        restored_dir = tmp_path / "restored-doc"
82
+        restored_dir.mkdir()
83
+        os.environ["DLM_HOME"] = str(fresh_home)
84
+        unpack_result = runner.invoke(
85
+            app,
86
+            [
87
+                "unpack",
88
+                str(pack_path),
89
+                "--out",
90
+                str(restored_dir),
91
+            ],
92
+        )
93
+        assert unpack_result.exit_code == 0, unpack_result.output
94
+
95
+        restored_doc = restored_dir / trained_store.doc.name
96
+        assert restored_doc.is_file(), f"no restored doc at {restored_doc}"
97
+        post = _run_prompt(fresh_home, restored_doc)
98
+
99
+        assert pre == post, (
100
+            "pack round-trip broke prompt output at temp=0.\n"
101
+            f"pre:  {pre!r}\npost: {post!r}"
102
+        )
103
+    finally:
104
+        for k, v in saved.items():
105
+            if v is not None:
106
+                os.environ[k] = v