Capture confirmation and generate record

Most of the time the router will have extracted a choice_index already. But users say things like "the Virgin one" or "the cheapest". For those cases, the confirm node falls back to a second LLM call that maps the freeform reply onto a numbered option.

booking_graph.py
python
async def confirm_booking(state: BookingState) -> BookingState:
    options = state.get("candidates") or []
    if not options:
        state["assistant_reply"] = "Let's start over - where would you like to fly from?"
        state["stage"] = "collecting"
        return state

    idx = state.get("_choice_index")
    # If the router didn't extract a choice, ask the LLM explicitly.
    if not isinstance(idx, int):
        system = (
            "You interpret which numbered option the user chose. Return JSON: "
            '{"choice_index": 1-based int or null, "declined": bool}.'
        )
        option_list = "\n".join(
            f"{i+1}. {o['carrier']} {o['id']} at {o['depart_time']}"
            for i, o in enumerate(options)
        )
        prompt = f"Options:\n{option_list}\nUser 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["stage"] = "routing"
            state["candidates"] = []
            return state
        idx = data.get("choice_index")

    if not isinstance(idx, int) or idx < 1 or idx > len(options):
        state["assistant_reply"] = f"Which option works for you? Reply with 1, 2, or {len(options)}."
        state["stage"] = "proposing"
        return state

The confirm node covers the no-candidates case (reset), the missing-index case (ask the LLM to interpret), and the bad-index case (re-prompt). Only after a valid index arrives does the node actually generate the record.

booking_graph.py
python
chosen = options[idx - 1]
pax = int(state.get("passengers") or 1)
booking = create_booking(
    thread_id=state.get("thread_id", "anonymous"),
    flight=chosen,
    depart_date=state.get("depart_date") or "",
    return_date=state.get("return_date"),
    passengers=pax,
)
state["chosen_flight"] = chosen
state["booking_id"] = booking["id"]
state["stage"] = "confirmed"
state["assistant_reply"] = (
    f"Booked! Confirmation {booking['id']} - {chosen['carrier']} {chosen['id']} "
    f"from {chosen['origin']} to {chosen['destination']} on "
    f"{state.get('depart_date')} for {pax} passenger(s). "
    f"Total ${booking['total_price_usd']}."
)

With a valid choice in hand, the node writes the booking, records the chosen flight and booking id in state, and sets stage to "confirmed" so future turns do not accidentally re-book.

AI prompt: Try it with your own domain

Loading practice…

Quiz: Quiz

Loading practice…