Wiself/Voice
52.6k
1"""End to end: full small GGUF + real cast + CPU inference. Needs2VOICE_TEST_LIVE=1 and llama-cli. ~700 MB model download (cached), then local.3"""4import json5import subprocess6import sys7import tempfile8import unittest9from pathlib import Path10 11sys.path.insert(0, str(Path(__file__).resolve().parent))12from _helpers import BASE_FILE, BASE_REPO, LLAMA_CLI # noqa: E40213from _helpers import cache_dir, fetch_file, needs_llama, ns, voice # noqa: E40214 15 16def shard_url(repo, filename):17 return f"https://huggingface.co/{repo}/resolve/main/{filename}"18 19 20@needs_llama21class TestEndToEnd(unittest.TestCase):22 @classmethod23 def setUpClass(cls):24 cls.work = Path(tempfile.mkdtemp(prefix="e2e-"))25 cls._old = voice.VOICES_DIR26 voice.VOICES_DIR = cls.work / "voices"27 # shared live voices from test_voices? No: self-contained — refetch head here28 # via the cached-voice path would couple suites; instead reuse cache dir voices29 # fetched by a direct cmd_get (second suite to do so stays independent).30 voice.cmd_get(ns(source="xcx0902/Qwen3-1.7B-catgirl", name="e2e-catgirl"))31 cls.model = fetch_file(shard_url(BASE_REPO, BASE_FILE),32 cache_dir() / BASE_FILE, label=BASE_FILE, n_parts=8)33 cls.voiced = cls.work / "voiced.gguf"34 voice.cmd_cast(ns(target=str(cls.model), voice="e2e-catgirl",35 out=str(cls.voiced)))36 37 @classmethod38 def tearDownClass(cls):39 voice.VOICES_DIR = cls._old40 41 def test_cast_replaces_head_only(self):42 from gguf import GGUFReader43 head = "token_embd.weight" # Qwen3 ties the head: no output.weight in GGUF44 before = {t.name: t.tensor_type.name for t in GGUFReader(str(self.model)).tensors}45 after_rd = GGUFReader(str(self.voiced))46 after = {t.name: t.tensor_type.name for t in after_rd.tensors}47 self.assertEqual(set(before), set(after))48 self.assertIn(head, after)49 self.assertEqual(after[head], "Q8_0")50 for n, q in before.items():51 if n != head:52 self.assertEqual(after[n], q, n)53 # values actually changed on the head54 b = [t for t in GGUFReader(str(self.model)).tensors if t.name == head][0]55 a = [t for t in after_rd.tensors if t.name == head][0]56 self.assertFalse((bytes(a.data) == bytes(b.data)))57 58 def test_inference_runs(self):59 from _helpers import run_guarded60 prompt = "The tavern door creaked open and"61 r = run_guarded(62 [LLAMA_CLI, "-m", str(self.voiced), "-p", prompt, "-n", "24",63 "--seed", "42", "-t", "8", "-c", "512", "--no-display-prompt",64 "--single-turn", "-e"], timeout=600)65 self.assertEqual(r.returncode, 0, (r.stdout + r.stderr)[-2000:])66 gen = r.stdout.strip()67 self.assertGreater(len(gen), 0)68 print(f"\n[llama] {gen[:200]}")69 70 71if __name__ == "__main__":72 unittest.main()73 