Level 2: Choices
Goal: the help desk handles a delivery question, a refund request and everything else in three different ways.
New moves: conditionalEdge, targets.
The map
After read the path splits. Only one of the three dotted arrows is taken in a run.
The code
level2/Level2.kt (Ticket and topicOf are the same as in level 1)
kotlin
fun helpDesk(): CompiledGraph<Ticket> =
StateGraph<Ticket> {
val read = node("read") { ticket -> ticket.copy(topic = topicOf(ticket.message)) }
val track = node("track") { ticket -> ticket.copy(reply = "Your pizza left the oven and is on its way.") }
val refund = node("refund") { ticket -> ticket.copy(reply = "We are sorry. Your money is on its way back.") }
val answer = node("answer") { ticket -> ticket.copy(reply = "Thanks for your message. A human will reply soon.") }
START then read
conditionalEdge(read, targets = setOf(track, refund, answer)) { ticket ->
when (ticket.topic) {
"delivery" -> track
"refund" -> refund
else -> answer
}
}
track then END
refund then END
answer then END
}.compile()
suspend fun main() {
val graph = helpDesk()
for (message in listOf("Where is my pizza?", "I want a refund", "Do you sell salad?")) {
val result = graph.invoke(Ticket(customer = "Ana", message = message))
println("$message -> ${result.state.reply}")
}
}Run it
bash
./gradlew :samples:runLevel2Where is my pizza? -> Your pizza left the oven and is on its way.
I want a refund -> We are sorry. Your money is on its way back.
Do you sell salad? -> Thanks for your message. A human will reply soon.What happened
then draws an arrow that is always followed. conditionalEdge draws an arrow that decides. It has three parts:
kotlin
conditionalEdge(read, targets = setOf(track, refund, answer)) { ticket -> ... }
// ^ ^ ^
// | the places this arrow may lead to the function that picks one
// the node the arrow starts from- The function runs after
readhas finished. It receives the state and returns the node to run next: one of the handles thatnode(...)gave you. This function is often called a router. Because it returns a handle and not a text, the compiler catches a misspelled node. To finish the run, a router returnsNodeRef.END(level 3 does that). targetslists every node the router may return. With it,compile()checks that every node can be reached, and during a run the library stops with a clear error if the router returns something that is not on the list.
Two rules:
- The router only decides. It does not change the state. Work that changes the state belongs in a node, which is why
readstores the topic and the router just looks at it. - A node has either ordinary arrows or one conditional edge, not both.
Your turn
- Add a fourth path: when the message contains "menu", go to a new
menunode that replies with today's pizzas. You have to touch four places:topicOf, a new node, the router andtargets. - Now remove
menufromtargetsand run it again.compile()refuses the graph withNode 'menu' is not reachable from START, because as far as the map says, no arrow leads there. The mistake is found before a single customer is answered.
Level complete
You can now:
- send a run down different paths depending on the state,
- say what a router is and why it returns a name,
- explain what
targetsprotects you from.
Back to level 1 · All levels · Next: Level 3, loops