phantom-grid / scripts /smoke_tts_lang.py
unity4ar's picture
Ship Phantom Grid Docker Space
d2e6f94 verified
Raw
History Blame Contribute Delete
3.93 kB
"""Compare TTS output language across three reference-audio configurations.
The upstream MiniCPM-o docs say there is no `language` parameter; output language
is steered by (a) the system-prompt template (we already pass lang=en) and (b)
the *reference audio* used for voice cloning. This script runs the same English
prompt under three configurations and scans the worker log for CJK code points
so we can see which reference, if any, actually produces English TTS.
Configs:
1. No ref_audio_path — baseline (previous test)
2. data/voices/voice_01.wav — locally-generated
Windows-TTS English voice
3. runtime/MiniCPM-o-Demo/assets/ref_audio/
ref_en_dlc_1.wav — upstream-shipped English
reference clip
"""
from __future__ import annotations
import re
import sys
import time
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO))
from llm.omni_client import OmniClient # noqa: E402
WORKER_LOG = REPO / "runtime" / "MiniCPM-o-Demo" / "tmp" / "phantom_grid_worker.log"
LLM_TTS_LINE = re.compile(r"LLM->TTS:\s+text='([^']*)'")
CJK_RANGE = re.compile(r"[一-鿿㐀-䶿]")
CJK_ESCAPE = re.compile(r"\\u(?:[4-9][0-9a-fA-F]{3}|[a-fA-F][0-9a-fA-F]{3})")
CONFIGS = [
("no reference", None),
("local voice_01.wav", REPO / "data" / "voices" / "voice_01.wav"),
("upstream ref_en_dlc_1.wav",
REPO / "runtime" / "MiniCPM-o-Demo" / "assets" / "ref_audio" / "ref_en_dlc_1.wav"),
]
SYSTEM = (
"You are a witness in a noir detective game. Reply in one short English "
"sentence. Speak only English."
)
USER = "Did you see anything unusual at the canal last night?"
def tts_chunks_since(offset: int) -> tuple[list[str], int]:
if not WORKER_LOG.exists():
return [], offset
with WORKER_LOG.open("rb") as fh:
fh.seek(offset)
chunk = fh.read()
new_offset = fh.tell()
texts: list[str] = []
for line in chunk.decode("utf-8", errors="replace").splitlines():
match = LLM_TTS_LINE.search(line)
if match:
texts.append(match.group(1))
return texts, new_offset
def main() -> int:
client = OmniClient.from_settings()
if not client.health(timeout=5.0).get("ready"):
print("gateway not ready"); return 1
print(f"{'config':32s} {'elapsed':>9s} {'chunks':>7s} {'CJK':>5s} example")
print("-" * 110)
results = []
for label, ref_path in CONFIGS:
if ref_path is not None and not ref_path.exists():
print(f"{label:32s} SKIP (ref not found: {ref_path})")
continue
log_offset = WORKER_LOG.stat().st_size if WORKER_LOG.exists() else 0
start = time.perf_counter()
resp = client.chat(
SYSTEM, USER,
task="witness",
tts=True,
temperature=0.4,
ref_audio_path=str(ref_path) if ref_path else None,
)
elapsed = time.perf_counter() - start
chunks, _ = tts_chunks_since(log_offset)
cjk_chunks = [c for c in chunks if CJK_RANGE.search(c) or CJK_ESCAPE.search(c)]
example = (cjk_chunks[0] if cjk_chunks else (chunks[0] if chunks else ""))[:60]
print(f"{label:32s} {elapsed:8.2f}s {len(chunks):7d} {len(cjk_chunks):5d} {example}")
results.append((label, len(chunks), len(cjk_chunks)))
print()
print("verdict:")
for label, total, cjk in results:
if total == 0:
print(f" {label}: no TTS chunks produced — ambiguous")
elif cjk == 0:
print(f" {label}: PASS — English TTS")
else:
print(f" {label}: FAIL — {cjk}/{total} chunks contained CJK")
return 0
if __name__ == "__main__":
raise SystemExit(main())