lingo-bridge / build_examples.py
Iona401's picture
Publish Lingo Bridge
7135dbd verified
Raw
History Blame Contribute Delete
3.63 kB
"""Build the precomputed example cache.
Inputs:
example_decomps.json β€” phrase decompositions authored by a LARGE model
(so the examples are reliably correct, unlike the
small on-device model).
Pipeline:
decomposition -> translate.build_layers() (deterministic 7 layers)
-> Qwen3-TTS audio per layer, fetched from the deployed Modal
endpoint, downloaded and saved under static/example_audio/
Outputs:
examples_cache.py β€” full results (layers/links + per-layer `audio` URL)
static/example_audio/ β€” committed .wav files (instant, no model at runtime)
Run: python build_examples.py
"""
import hashlib
import json
import time
import urllib.request
import config
import examples
import translate
REMOTE = config.TTS_REMOTE_URL.rstrip("/")
AUDIO_DIR = config.STATIC_DIR / "example_audio"
def fetch_tts(text: str, lang: str) -> bytes:
"""Fetch one utterance from the deployed Qwen3-TTS endpoint (retry for the
L4 cold-start blip; reject fallback beeps)."""
payload = json.dumps({"text": text, "lang": lang}).encode("utf-8")
last = None
for attempt in range(4):
try:
req = urllib.request.Request(
REMOTE + "/api/tts", data=payload,
headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=300) as r:
info = json.load(r)
with urllib.request.urlopen(REMOTE + info["url"], timeout=120) as r:
data = r.read()
if len(data) < 25000:
raise RuntimeError("remote returned fallback audio (beep)")
return data
except Exception as e:
last = e
print(f" retry {attempt+1} ({str(e)[:60]})")
time.sleep(5)
raise last
def main():
decomps = json.load(open("example_decomps.json", encoding="utf-8"))
AUDIO_DIR.mkdir(parents=True, exist_ok=True)
cache = {}
seen_audio = set()
for text, src, tgt in examples.EXAMPLES:
k = f"{src}|{tgt}|{text}"
if k not in decomps:
print("!! missing decomposition:", k)
continue
d = decomps[k]
decomp = {"final": d["final"], "units": d["units"], "source_text": text}
result = translate.build_layers(decomp, src, tgt)
for layer in result["layers"]:
idx = layer["index"]
lang = src if idx == 0 else tgt # matches frontend langForLayer
ltext = layer["text"]
h = hashlib.sha1(f"{lang}|{ltext}".encode("utf-8")).hexdigest()[:16]
fpath = AUDIO_DIR / f"{h}.wav"
if h not in seen_audio and not fpath.exists():
print(f" {src}->{tgt} L{idx} [{lang}]: {ltext[:34]}")
fpath.write_bytes(fetch_tts(ltext, lang))
seen_audio.add(h)
layer["audio"] = f"/static/example_audio/{h}.wav"
cache[k] = result
print("done:", k)
blob = json.dumps(cache, ensure_ascii=False)
with open("examples_cache.py", "w", encoding="utf-8") as f:
f.write('"""Auto-generated by build_examples.py β€” do not edit by hand.\n')
f.write("Precomputed example results (layers + per-layer audio) so the\n")
f.write('Surprise-me button needs no LLM and no TTS at runtime."""\n')
f.write("import json\n\nCACHE = json.loads(%r)\n" % blob)
print(f"\nwrote examples_cache.py: {len(cache)} examples, "
f"{len(seen_audio)} audio clips in {AUDIO_DIR}")
if __name__ == "__main__":
main()