Spaces:
Runtime error
Runtime error
| # File: src/verification_engine.py | |
| # Purpose: Verify booking outcomes before allowing the agent to generate any response | |
| # Note: On HF Spaces, booking logic is called in-process (no HTTP server needed) | |
| import uuid | |
| import random | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| import sys | |
| sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) | |
| from config import AVAILABLE_SLOTS, DEPARTMENTS, MAX_BOOKING_RETRIES | |
| # In-memory slot store (simulates database, lives for the session) | |
| _booked_slots: dict[str, list[str]] = {} | |
| class VerificationResult: | |
| """ | |
| Carries the verified state of a booking attempt. | |
| The response generator ONLY uses data from this object — never from LLM memory. | |
| """ | |
| verified: bool | |
| appointment_id: str | None | |
| patient_name: str | None | |
| department: str | None | |
| date: str | None | |
| slot: str | None | |
| doctor: str | None | |
| failure_reason: str | None | |
| attempts: int | |
| def _call_booking_logic(patient_name: str, department: str, date: str, slot: str) -> dict: | |
| """ | |
| In-process booking logic — replaces HTTP call for HF Spaces deployment. | |
| Simulates real constraints: slot conflicts, 80% success rate. | |
| """ | |
| if department not in DEPARTMENTS: | |
| return {"success": False, "reason": f"Unknown department: {department}"} | |
| if slot not in AVAILABLE_SLOTS: | |
| return {"success": False, "reason": f"Invalid time slot: {slot}"} | |
| key = f"{department}::{date}" | |
| taken = _booked_slots.get(key, []) | |
| if slot in taken or random.random() < 0.20: | |
| return {"success": False, "reason": "Slot unavailable. Please choose another time."} | |
| appointment_id = f"APT-{uuid.uuid4().hex[:6].upper()}" | |
| doctor = f"Dr. Sample_{random.randint(1, 5)}" | |
| _booked_slots.setdefault(key, []).append(slot) | |
| return { | |
| "success": True, | |
| "appointment_id": appointment_id, | |
| "patient_name": patient_name, | |
| "department": department, | |
| "date": date, | |
| "slot": slot, | |
| "doctor": doctor, | |
| } | |
| def verify_booking(intent: dict) -> VerificationResult: | |
| """ | |
| Attempt to book and verify the result up to MAX_BOOKING_RETRIES times. | |
| Anti-hallucination gate: | |
| - verified=True ONLY when appointment_id is present in the result. | |
| - All data in VerificationResult comes from the booking logic, not the LLM. | |
| """ | |
| attempts = 0 | |
| last_reason = "Unknown error" | |
| for attempt in range(1, MAX_BOOKING_RETRIES + 1): | |
| attempts = attempt | |
| result = _call_booking_logic( | |
| patient_name=intent.get("patient_name", "Unknown"), | |
| department=intent.get("department", ""), | |
| date=intent.get("date", ""), | |
| slot=intent.get("slot", ""), | |
| ) | |
| print(f"[Verification] Attempt {attempt}: {result}") | |
| if result.get("success") and result.get("appointment_id"): | |
| return VerificationResult( | |
| verified=True, | |
| appointment_id=result["appointment_id"], | |
| patient_name=result.get("patient_name"), | |
| department=result.get("department"), | |
| date=result.get("date"), | |
| slot=result.get("slot"), | |
| doctor=result.get("doctor"), | |
| failure_reason=None, | |
| attempts=attempts, | |
| ) | |
| else: | |
| last_reason = result.get("reason", "Slot unavailable") | |
| return VerificationResult( | |
| verified=False, | |
| appointment_id=None, | |
| patient_name=intent.get("patient_name"), | |
| department=intent.get("department"), | |
| date=intent.get("date"), | |
| slot=intent.get("slot"), | |
| doctor=None, | |
| failure_reason=last_reason, | |
| attempts=attempts, | |
| ) | |
| def fetch_available_slots(department: str, date: str) -> list[str]: | |
| """Return open slots for a department on a given date.""" | |
| key = f"{department}::{date}" | |
| taken = _booked_slots.get(key, []) | |
| return [s for s in AVAILABLE_SLOTS if s not in taken] |