"""Demo preflight: warm up ASR and probe the live LLM before going on stage. Unlike ``smoke_backend.py`` (which forces mock/offline), this uses your real ``.env`` so it validates the actual demo path: 1. Loads settings and prints the active providers. 2. Reports pipeline health. 3. Warms up ASR (downloads + loads Gipformer ONNX so the first real request is fast). 4. Probes the *primary* LLM directly (bypassing the offline fallback) so a broken CKey link is surfaced, not silently masked. 5. Runs a full text round-trip to confirm an end-to-end SOAP draft is produced. Exit code is non-zero only for failures that would break the live demo (ASR warmup or the end-to-end round-trip). A failed LLM probe is a warning, because the offline fallback keeps the demo serving notes. Usage: python scripts/preflight.py python scripts/preflight.py --skip-asr # skip the model download python scripts/preflight.py --no-llm-probe # do not call the live LLM """ from __future__ import annotations import argparse import sys import time from pathlib import Path def _status(ok: bool, warn: bool = False) -> str: if ok: return "[OK] " return "[WARN]" if warn else "[FAIL]" def main() -> int: parser = argparse.ArgumentParser(description="CarePath demo preflight") parser.add_argument("--skip-asr", action="store_true", help="Skip ASR model warmup") parser.add_argument( "--no-llm-probe", action="store_true", help="Do not call the live LLM" ) args = parser.parse_args() # Vietnamese text (and provider error bodies) break the default Windows # cp1252 console; force UTF-8 so prints and stderr logs never crash. for stream in (sys.stdout, sys.stderr): try: stream.reconfigure(encoding="utf-8", errors="replace") except Exception: # noqa: BLE001 - older/odd streams have no reconfigure pass repo_root = Path(__file__).resolve().parents[1] sys.path.insert(0, str(repo_root / "scribe")) from carepath.config import load_settings from carepath.logging_config import configure_logging from carepath.services.asr import ASRError from carepath.services.pipeline import CarePathPipeline configure_logging() settings = load_settings() print("CarePath preflight") print("------------------") print(f" app_env : {settings.app_env}") print(f" asr_provider : {settings.asr_provider}") print(f" llm_provider : {settings.llm_provider}") print(f" llm_model : {settings.llm_model}") print(f" llm_base_url : {settings.llm_base_url}") print(f" offline_fallback: {settings.llm_fallback_offline}") print() pipeline = CarePathPipeline(settings) hard_fail = False health = pipeline.health() print(f"{_status(health['status'] == 'ok')} health: {health['status']}") print(f" asr_ready={health['asr_ready']} llm_ready={health['llm_ready']}") if not args.skip_asr: start = time.perf_counter() try: report = pipeline.warmup(probe_llm=not args.no_llm_probe) elapsed = time.perf_counter() - start print(f"{_status(True)} asr warmup: {report['asr']} ({elapsed:.1f}s)") if "llm" in report: llm = report["llm"] ok = llm.get("probe") == "ok" print(f"{_status(ok, warn=not ok)} llm probe: {llm}") except ASRError as exc: hard_fail = True print(f"{_status(False)} asr warmup failed: {exc}") elif not args.no_llm_probe: # ASR skipped, but still probe the LLM directly (no model download). llm = pipeline.probe_llm() ok = llm.get("probe") == "ok" print(f"{_status(ok, warn=not ok)} llm probe: {llm}") # End-to-end text round-trip (uses fallback, so it must always succeed). try: output = pipeline.process_text( "benh nhan dau nguc spo2 98 % huyet ap 120 tren 80 mmhg", encounter_context="Phong kham noi tong quat", ) print( f"{_status(True)} round-trip: gec_mode={output.metadata.get('gec_mode')} " f"soap_mode={output.metadata.get('soap_mode')} " f"terms={output.retrieved_terms and [t.term for t in output.retrieved_terms]}" ) except Exception as exc: # noqa: BLE001 - report any failure, do not crash hard_fail = True print(f"{_status(False)} round-trip failed: {exc}") print() if hard_fail: print("PREFLIGHT FAILED - fix the items above before the demo.") return 1 print("PREFLIGHT OK - demo path is ready.") return 0 if __name__ == "__main__": raise SystemExit(main())