Download scripts/serve_eval.py from ehabnegm/s2pro-egy: direct link, hf CLI and curl.
- Browser
- Download file 4.68 kB
-
https://huggingface.co/ehabnegm/s2pro-egy/resolve/main/scripts/serve_eval.py
- Command line
-
hf download hf://ehabnegm/s2pro-egy/scripts/serve_eval.py
-
curl -L -o serve_eval.py https://huggingface.co/ehabnegm/s2pro-egy/resolve/main/scripts/serve_eval.py
4.68 kB
| #!/usr/bin/env python3 | |
| """Tiny eval-comparison server: http://<box>:8080 | |
| Auto-discovers /opt/work/eval/<system>/ dirs (timing.jsonl present), shows each | |
| eval sentence with side-by-side audio players + Soniox WER badges. Refresh to | |
| pick up newly evaluated checkpoints. | |
| """ | |
| import json | |
| import re | |
| from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer | |
| from pathlib import Path | |
| EVAL = Path("/opt/work/eval") | |
| SENTS = Path("/opt/work/scripts/eval_sentences.jsonl") | |
| def discover(): | |
| systems = [] | |
| for d in sorted(EVAL.iterdir()): | |
| if d.is_dir() and any(d.glob("*.wav")) and d.name != "baseline": | |
| systems.append(d.name) | |
| # baseline first, then stepN numerically | |
| def key(n): | |
| if n.startswith("baseline"): | |
| return (0, 0) | |
| m = re.search(r"(\d+)", n) | |
| return (1, int(m.group(1)) if m else 9e9) | |
| return sorted(systems, key=key) | |
| def reports(systems): | |
| out = {} | |
| for s in systems: | |
| for fn in ("asr_report_soniox.json", "asr_report.json"): | |
| p = EVAL / s / fn | |
| if p.exists(): | |
| try: | |
| rep = json.loads(p.read_text(encoding="utf-8")) | |
| out[s] = { | |
| "rows": {r["id"]: r for r in rep.get("rows", []) if "id" in r}, | |
| "summary": rep.get("summary", {}), | |
| "engine": rep.get("summary", {}).get("engine", "whisper"), | |
| } | |
| except Exception: | |
| pass | |
| break | |
| return out | |
| def render(): | |
| systems = discover() | |
| reps = reports(systems) | |
| sents = [json.loads(l) for l in SENTS.read_text(encoding="utf-8").splitlines() if l.strip()] | |
| # include ids that only exist in system timing files (e.g. mega paragraph) | |
| known = {s["id"] for s in sents} | |
| for s in systems: | |
| tj = EVAL / s / "timing.jsonl" | |
| if tj.exists(): | |
| for line in tj.read_text(encoding="utf-8").splitlines(): | |
| if line.strip(): | |
| r = json.loads(line) | |
| if r.get("id") and r["id"] not in known and r.get("text"): | |
| sents.insert(0, {"id": r["id"], "text": r["text"]}) | |
| known.add(r["id"]) | |
| head = "".join( | |
| f"<th>{s}<br><small>{('WER %.0f%%' % (100*reps[s]['summary'].get('mean_wer',0))) if s in reps else 'scoring...'}</small></th>" | |
| for s in systems) | |
| rows_html = [] | |
| for sent in sents: | |
| sid = sent["id"] | |
| cells = [] | |
| for s in systems: | |
| wav = EVAL / s / f"{sid}.wav" | |
| if wav.exists(): | |
| badge = "" | |
| r = reps.get(s, {}).get("rows", {}).get(sid) | |
| if r and "wer" in r: | |
| c = "#2a4" if r["wer"] < 0.2 else ("#a82" if r["wer"] < 0.5 else "#a33") | |
| badge = f'<span class="b" style="background:{c}">WER {r["wer"]:.2f}</span>' | |
| cells.append( | |
| f'<td><audio controls preload="none" src="/{s}/{sid}.wav"></audio>{badge}</td>') | |
| else: | |
| cells.append("<td>—</td>") | |
| rows_html.append( | |
| f'<tr><td class="t"><b>{sid}</b><div dir="rtl">{sent["text"]}</div></td>' | |
| + "".join(cells) + "</tr>") | |
| return f"""<!doctype html><html><head><meta charset="utf-8"> | |
| <title>s2-pro Egyptian eval</title> | |
| <style> | |
| body{{font-family:system-ui;background:#111;color:#eee;margin:20px}} | |
| table{{border-collapse:collapse;width:100%}} | |
| td,th{{border:1px solid #333;padding:8px;vertical-align:top}} | |
| th{{background:#222;position:sticky;top:0}} | |
| .t{{max-width:420px}} .t div{{font-size:15px;line-height:1.7}} | |
| audio{{width:230px;display:block}} | |
| .b{{font-size:11px;padding:2px 6px;border-radius:4px;display:inline-block;margin-top:4px}} | |
| h1{{font-size:18px}} | |
| </style></head><body> | |
| <h1>s2-pro Egyptian fine-tune — A/B eval (refresh for new checkpoints)</h1> | |
| <table><tr><th class="t">sentence</th>{head}</tr> | |
| {''.join(rows_html)} | |
| </table></body></html>""" | |
| class H(SimpleHTTPRequestHandler): | |
| def __init__(self, *a, **kw): | |
| super().__init__(*a, directory=str(EVAL), **kw) | |
| def do_GET(self): | |
| if self.path in ("/", "/index.html"): | |
| body = render().encode("utf-8") | |
| self.send_response(200) | |
| self.send_header("Content-Type", "text/html; charset=utf-8") | |
| self.send_header("Content-Length", str(len(body))) | |
| self.end_headers() | |
| self.wfile.write(body) | |
| else: | |
| super().do_GET() | |
| def log_message(self, *a): | |
| pass | |
| if __name__ == "__main__": | |
| ThreadingHTTPServer(("0.0.0.0", 8080), H).serve_forever() | |