Slot validation

The LLM is flexible, which also means it is inconsistent. "lhr", "LHR", and "London" might all refer to the same airport. Before a slot enters state, we normalize it. That way the catalog lookup, option proposal, and confirmation nodes never have to guess what the value means.

booking_graph.py
python
# Inside intent_router, after merging slots from the LLM response:

# Uppercase IATA codes when they look like codes.
for key in ("origin", "destination"):
    v = state.get(key)
    if isinstance(v, str) and len(v) == 3:
        state[key] = v.upper()

A tiny normalization step right after extraction. Three-character strings look like IATA codes, so we uppercase them. Everything downstream can now assume LHR, not lhr.

booking_graph.py
python
async def search_flights_node(state: BookingState) -> BookingState:
    origin = state.get("origin") or ""
    dest = state.get("destination") or ""
    depart = state.get("depart_date") or ""

    candidates = search_flights(origin, dest, depart, limit=3)
    state["candidates"] = candidates
    if not candidates:
        state["assistant_reply"] = (
            f"I couldn't find flights from {origin} to {dest}. "
            "Would you like to try a different route?"
        )
        state["stage"] = "collecting"
        # Reset route so the user can retry
        state["origin"] = None
        state["destination"] = None
    return state

Validation also means handling the empty case. If the catalog returned nothing, the agent resets origin and destination, sets stage back to collecting, and invites the user to try again.

Fill in the blanks: Fill in the normalization guard

Loading practice…