Filter and surface results

Raw catalog rows are not useful to a traveler. The propose_options node takes the candidates, multiplies price by passengers, and formats a short reply with numbered choices. The LLM is not involved here because formatting is deterministic.

booking_graph.py
python
async def propose_options(state: BookingState) -> BookingState:
    candidates = state.get("candidates") or []
    if not candidates:
        return state

    pax = int(state.get("passengers") or 1)
    lines = []
    for i, c in enumerate(candidates, start=1):
        total = c["price_usd"] * max(1, pax)
        lines.append(
            f"  {i}. {c['carrier']} {c['id']} - depart {c['depart_time']}, "
            f"arrive {c['arrive_time']} ({c['duration_minutes']} min) - "
            f"${c['price_usd']}/pax (total ${total})"
        )
    body = "\n".join(lines)
    depart = state.get("depart_date") or "the requested date"
    state["assistant_reply"] = (
        f"Here are 3 flight options from {state.get('origin')} to {state.get('destination')} "
        f"on {depart} for {pax} passenger(s):\n{body}\n"
        "Reply with the option number (1, 2, or 3) to confirm, or say 'cancel' to start over."
    )
    state["stage"] = "proposing"
    return state

The node computes totals, builds numbered lines, and sets stage to "proposing" so the router knows the next turn is a choice or a change, not a new booking.

Quiz: Quiz

Loading practice…

Checkpoint: Catalog and options checkpoint

Loading practice…