File size: 4,131 Bytes
1bfbac9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 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]] = {}


@dataclass
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]