Spaces:
Runtime error
Runtime error
| # File: src/bland_webhook.py | |
| # Purpose: FastAPI webhook endpoint that receives Bland AI call events and runs the pipeline | |
| from fastapi import FastAPI, Request, HTTPException | |
| from pathlib import Path | |
| import json | |
| import sys | |
| sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) | |
| from src.pipeline import run_pipeline | |
| from src.stt import transcribe_from_bland_webhook | |
| app = FastAPI(title="Bland AI Webhook Receiver", version="1.0") | |
| async def bland_webhook(request: Request): | |
| """ | |
| Receive a Bland AI call webhook. | |
| Bland AI sends a POST with: | |
| - call_id: unique call identifier | |
| - transcript: full call transcript (if available) | |
| - recording_url: URL of audio recording | |
| - status: call status (e.g., "completed", "in-progress") | |
| This endpoint: | |
| 1. Extracts the transcript (from text or audio) | |
| 2. Runs the full verification pipeline | |
| 3. Returns the verified spoken response to Bland AI | |
| """ | |
| try: | |
| payload = await request.json() | |
| except Exception: | |
| raise HTTPException(status_code=400, detail="Invalid JSON payload") | |
| call_id = payload.get("call_id", "unknown") | |
| status = payload.get("status", "") | |
| print(f"[Webhook] Received call {call_id} with status: {status}") | |
| if status != "completed": | |
| return {"status": "ignored", "reason": "Call not yet completed"} | |
| # Get transcript from Bland AI (try text first, then audio transcription) | |
| transcript = payload.get("transcript", "").strip() | |
| if not transcript: | |
| recording_url = payload.get("recording_url", "") | |
| if recording_url: | |
| print(f"[Webhook] Transcribing from recording_url: {recording_url}") | |
| transcript = transcribe_from_bland_webhook(recording_url) | |
| if not transcript: | |
| return {"status": "error", "reason": "No transcript or audio available"} | |
| # Run the anti-hallucination pipeline | |
| result = run_pipeline(transcript) | |
| # Return the verified response to be spoken back to the patient | |
| return { | |
| "call_id": call_id, | |
| "response": result["response"], | |
| "escalated": result["escalated"], | |
| "appointment_id": ( | |
| result["verification"].appointment_id | |
| if result["verification"] and result["verification"].verified | |
| else None | |
| ), | |
| } | |
| def health(): | |
| return {"status": "ok"} | |
| if __name__ == "__main__": | |
| try: | |
| uvicorn = __import__("uvicorn") | |
| except ImportError: | |
| raise RuntimeError("uvicorn must be installed to run this server") from None | |
| uvicorn.run("src.bland_webhook:app", host="0.0.0.0", port=9000, reload=True) |