Files
mission-control-v2/client/lucy-tts/lucy_ready.py
T
Hitonabi 09a1c98514 Lucy-TTS/F5: Skripte + Batches versionieren, schwere Assets ignoriert
- pocket_server.py (Produktions-TTS mit Stimmen-Waechter), text_norm, Bench-/Diag-Skripte
- lucy-f5: f5_server/f5_test/bench_dml (DirectML-Experiment, Phase C/D offen)
- .gitignore: venvs/Modelle/Audio/Logs der beiden Ordner + box_recon/gemma_swap-Scratch

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 10:29:33 +02:00

61 lines
2.9 KiB
Python

# -*- coding: utf-8 -*-
"""lucy_ready.py — Startklar-Check + finale Samples in EINEM Lauf.
Bootet den ECHTEN Server (FastAPI-TestClient triggert lifespan: Modell-Load, A1-Cache, STATE),
prüft /health, und rendert die finalen Hörproben über die REALEN Endpoints /tts und /tts/stream
(exakt das, was die Lucy-App auf :8130 aufruft) — inkl. Live-TTFB der Streaming-Antwort.
Legt die Samples auf den Desktop, schreibt lucy_ready_report.json.
"""
import os, time, json
import numpy as np, soundfile as sf
from fastapi.testclient import TestClient
import pocket_server as ps
DESK = os.path.join(os.path.expanduser("~"), "Desktop", "lucy_samples")
os.makedirs(DESK, exist_ok=True)
SENT = {
"kurz": "Hallo Commander, ich höre dich.",
"mittel": "Guten Morgen, Commander. Das Backup ist sauber durchgelaufen und es gab keine Fehler.",
"lang": "Natürlich kümmere ich mich darum, Commander. Ich starte den Dienst neu, prüfe die "
"Protokolle und melde mich, sobald alles wieder läuft.",
}
LONG = ("Guten Morgen, Commander. "
"Das nächtliche Backup ist sauber durchgelaufen und es gab keine Fehler. "
"Der Dienst läuft stabil und die Engine antwortet zügig. "
"Wenn du möchtest, fasse ich die offenen Punkte für heute zusammen.")
report = {}
t0 = time.time()
with TestClient(ps.app) as c: # <-- triggert lifespan (echter Boot)
report["boot_s"] = round(time.time() - t0, 1)
h = c.get("/health").json()
report["health"] = h
print(f"BOOT in {report['boot_s']}s health={h}")
for name, text in SENT.items(): # /tts (ganze Datei) -> Desktop
t = time.time(); r = c.post("/tts", json={"text": text}); dt = time.time() - t
assert r.status_code == 200, f"/tts {name} -> {r.status_code}"
open(os.path.join(DESK, f"lucy_final_{name}.wav"), "wb").write(r.content)
print(f"/tts {name:6}: {dt:4.2f}s audio={r.headers.get('X-Audio-Seconds')}s")
# /tts/stream: lange Antwort wie Lucy LIVE spricht -> TTFB messen + PCM zu WAV
sr = int(h.get("sr") or 24000)
t = time.time(); first = None; chunks = []
with c.stream("POST", "/tts/stream", json={"text": LONG}) as s:
sr = int(s.headers.get("X-Sample-Rate", sr))
for ch in s.iter_bytes():
if ch:
if first is None:
first = time.time() - t
chunks.append(ch)
a = np.frombuffer(b"".join(chunks), dtype="<i2").astype(np.float32) / 32767.0
sf.write(os.path.join(DESK, "lucy_final_antwort.wav"), a, sr)
total = time.time() - t
report["stream_ttfb_s"] = round(first or 0, 2)
report["stream_total_s"] = round(total, 2)
report["stream_audio_s"] = round(len(a) / sr, 1)
print(f"/tts/stream: TTFB={first:.2f}s total={total:.2f}s audio={len(a)/sr:.1f}s")
json.dump(report, open(os.path.join(ps.BASE, "lucy_ready_report.json"), "w"), indent=2)
print("LUCY_READY" if h.get("status") == "ok" else "NOT_READY")