09a1c98514
- 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>
61 lines
2.9 KiB
Python
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")
|