Spaces:
Running
Running
| from __future__ import annotations | |
| import argparse | |
| import contextlib | |
| import json | |
| import tempfile | |
| import wave | |
| from pathlib import Path | |
| import httpx | |
| def parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser( | |
| description="Send an audio clip through the running CarePath API." | |
| ) | |
| parser.add_argument("--url", default="http://127.0.0.1:8000") | |
| parser.add_argument("--audio", type=Path, default=None) | |
| parser.add_argument("--output", type=Path, default=None) | |
| parser.add_argument("--preview-chars", type=int, default=500) | |
| parser.add_argument("--timeout", type=float, default=300.0) | |
| parser.add_argument( | |
| "--encounter-context", | |
| default="Phong kham noi tong quat", | |
| ) | |
| return parser.parse_args() | |
| def main() -> None: | |
| args = parse_args() | |
| health_url = args.url.rstrip("/") + "/api/v1/health" | |
| soap_url = args.url.rstrip("/") + "/api/v1/soap-notes" | |
| with httpx.Client(timeout=args.timeout) as client: | |
| health = client.get(health_url) | |
| health.raise_for_status() | |
| health_json = health.json() | |
| if health_json.get("asr_provider") != "gipformer": | |
| raise SystemExit( | |
| "API is not in Gipformer mode. Set ASR_PROVIDER=gipformer and restart." | |
| ) | |
| with _audio_path(args.audio) as audio_path: | |
| with audio_path.open("rb") as handle: | |
| response = client.post( | |
| soap_url, | |
| files={"audio": (audio_path.name, handle, "audio/wav")}, | |
| data={"encounter_context": args.encounter_context}, | |
| ) | |
| if response.status_code >= 400: | |
| raise SystemExit( | |
| f"SOAP request failed: HTTP {response.status_code} {response.text}" | |
| ) | |
| payload = response.json() | |
| if args.output is not None: | |
| args.output.parent.mkdir(parents=True, exist_ok=True) | |
| args.output.write_text( | |
| json.dumps(payload, ensure_ascii=False, indent=2), | |
| encoding="utf-8", | |
| ) | |
| print( | |
| json.dumps( | |
| { | |
| "asr_provider": health_json["asr_provider"], | |
| "asr_ready": health_json["asr_ready"], | |
| "raw_transcript_chars": len(payload["raw_transcript"]), | |
| "corrected_transcript_chars": len(payload["corrected_transcript"]), | |
| "raw_transcript_preview": _preview( | |
| payload["raw_transcript"], args.preview_chars | |
| ), | |
| "corrected_transcript_preview": _preview( | |
| payload["corrected_transcript"], args.preview_chars | |
| ), | |
| "retrieved_terms": payload["retrieved_terms"], | |
| "soap_review_required": payload["soap"]["review_required"], | |
| "soap": payload["soap"], | |
| "metadata": payload["metadata"], | |
| "output": str(args.output) if args.output else None, | |
| }, | |
| ensure_ascii=True, | |
| indent=2, | |
| ) | |
| ) | |
| def _audio_path(audio: Path | None): | |
| if audio is not None: | |
| yield audio | |
| return | |
| with tempfile.TemporaryDirectory(prefix="carepath_real_asr_") as temp_dir: | |
| path = Path(temp_dir) / "silence.wav" | |
| _write_silent_wav(path) | |
| yield path | |
| def _write_silent_wav(path: Path) -> None: | |
| with contextlib.closing(wave.open(str(path), "wb")) as writer: | |
| writer.setnchannels(1) | |
| writer.setsampwidth(2) | |
| writer.setframerate(16000) | |
| writer.writeframes(b"\x00\x00" * 16000) | |
| def _preview(text: str, max_chars: int) -> str: | |
| if len(text) <= max_chars: | |
| return text | |
| return text[:max_chars].rstrip() + "..." | |
| if __name__ == "__main__": | |
| main() | |