Write the record

The user comes back with "2" or "the morning one" or "let's do Tuesday at 9". The confirm node needs to map all of those to a specific slot option and write the record. An LLM call handles the natural language interpretation. A strict JSON contract keeps the output parseable.

booking_graph.py
python
async def confirm_booking(state: BookingState) -> BookingState:
    options = state.get("slot_options") or []
    if not options:
        state["assistant_reply"] = "Let's pick a doctor first. What symptom are you experiencing?"
        state["status"] = "gathering"
        return state

    system = (
        "You interpret which slot a patient chose from a numbered list. "
        "Return JSON: {\"choice_index\": 1-based int or null, \"declined\": bool}."
    )
    prompt = (
        "Slots:\n"
        + "\n".join(f"{i+1}. {s}" for i, s in enumerate(options))
        + f"\nPatient reply: {state.get('user_message', '')}"
    )
    data = await _llm_json(prompt, system)

    if data.get("declined"):
        state["assistant_reply"] = "No problem. Tell me when you'd like to try again."
        state["status"] = "ended"
        return state

    idx = data.get("choice_index")
    if not isinstance(idx, int) or idx < 1 or idx > len(options):
        state["assistant_reply"] = "Which slot works for you? Reply with 1, 2, or 3."
        state["status"] = "ready_to_confirm"
        return state

Defensive first. If slot_options is empty, the node reroutes the user to describe a symptom. If the LLM says they declined, end the turn gracefully. If the choice index is bogus, ask again without losing the context.

booking_graph.py
python
    chosen = options[idx - 1]
    doctor = state.get("doctor") or {}
    patient_id = state.get("patient_id") or state.get("thread_id") or "anonymous"
    appt = create_appointment(
        patient_id=patient_id,
        doctor_id=doctor.get("id", 0),
        doctor_name=doctor.get("name", "Unknown"),
        specialty=doctor.get("specialty", "general_medicine"),
        slot=chosen,
    )
    state["slot"] = chosen
    state["status"] = "confirmed"
    state["assistant_reply"] = (
        f"Booked! Appointment #{appt['id']} with {appt['doctor_name']} on {chosen}. "
        "You'll get a reminder by email."
    )
    _record_event(state, "confirm_booking", {"appointment_id": appt["id"]})
    return state

Once validated, write the record. create_appointment is a db_utils helper that returns the persisted row. The node writes the slot into state, sets status = confirmed, and produces the confirmation reply.

db_utils.py
python
def create_appointment(
    patient_id: str,
    doctor_id: int,
    doctor_name: str,
    specialty: str,
    slot: str,
) -> Dict[str, Any]:
    init_db()
    conn = _connect()
    try:
        now = datetime.utcnow().isoformat()
        cur = conn.execute(
            """
            INSERT INTO appointments
                (patient_id, doctor_id, doctor_name, specialty, slot, status, created_at)
            VALUES (?, ?, ?, ?, ?, 'confirmed', ?)
            """,
            (patient_id, doctor_id, doctor_name, specialty, slot, now),
        )
        conn.commit()
        appt_id = cur.lastrowid
        return {
            "id": appt_id,
            "patient_id": patient_id,
            "doctor_id": doctor_id,
            "doctor_name": doctor_name,
            "specialty": specialty,
            "slot": slot,
            "status": "confirmed",
            "created_at": now,
        }
    finally:
        conn.close()

A parameterized INSERT returns the persisted row with its auto-generated id. The connection is closed in a finally block so a mid-write exception does not leak handles.

Quiz: Quiz

Loading practiceโ€ฆ