Skip to content

Genie Flow state machine principles

The backbone of the Genie Flow framework is defined by the State Machine. The Genie Flow State Machine is based on the package Python State Machine and the GenieStateMachine is a direct subclass of the StateMachine class that is defined there.

The GenieStateMachine class implements the base logic of the Genie Flow. A State Machine defines the different states (nodes in a graph) and the transitions (edges in the graph) that can be made between these states.

Question and Answer example

A simple Question and Answer dialogue flow would look like this:

stateDiagram-v2
    direction LR
    [*] --> intro
    intro --> ai_creates_response: user_input
    ai_creates_response --> user_enters_query: processing_done
    user_enters_query --> ai_creates_response: user_input

    intro: Intro
    ai_creates_response: AI Creates Response
    user_enters_query: User Enters Query
Here, it becomes apparent that a dialogue is played between an "AI" and a human actor. The dialogue starts with the initial state (colour coded) called Intro. From that initial state, the only transition that can be made is the one called user_input, which transitions into Ai creates response. From there, a transition called ai_extraction brings the state machine into the state called User enters query. From there, the only transition is again a user_input transition, back to the Ai creates response.

This example shows a number of foundational elements:

states
A state machine contains a number of states between which the state machine can transition. A machine can only be in one state at any single time. The transitions that can be made from one state to another are predefined and fixed. Only one initial state is defined. It is the state where a newly instantiated state machine starts.
transitions
From any state, there can be zero, one or more transitions into other states. If there are zero states, that state is a final state, of which there can be multiple, or none, as in the Q and A example. Every transition has the name of an event, which is the event that will trigger that particular transition.
events
An event is what makes the state machine transition from one state to the next. In Genie Flow, events are either sent through the API or received from the internal workings, for instance, when an LLM has finished rendering, and its output is ready for processing.

Question and Answer example with conditions

This first example gives a good impression of the elements that are relevant for creating a Genie Flow application. The dialogue, however, is simplistic and never-ending. Now, consider the following flow diagram:

stateDiagram-v2
    direction LR
    [*] --> intro
    intro --> ai_creates_response: user_input
    ai_creates_response --> user_enters_query: processing_done
    user_enters_query --> ai_creates_response: user_input

    intro: Intro
    ai_creates_response: AI Creates Response
    user_enters_query: User Enters Query

It is almost the same Question and Answer flow, except that we have now introduced conditions. There are now two ai_extraction event transitions from the state Ai creates response. One that has the connotation user_wants_to_quit and the other with !user_wants_to_quit. Indicating that the user wants to quit, or the user does not want to quit, indicated by the ! mark, pronounced as "not".

So, when the LLM determines the user wants to quit, we want the flow to go to the state called "Outro". If not, we follow the normal path towards the state "User enters query".

A similar thing has been done with the user_input events out of the state "User enters query". Here we have another condition called user_says_stop that tells the state machine to either go to the "Ai creates response", in the case where "Not user says stop" (!user_says_stop), or to the state "Outro" in the case where "user says stop" (user_says_stop).

These conditions are a first step towards creating more complex dialogues. It enables us to make different paths through the dialogue.

Question and Answer example with data capture

So far, we have not yet seen how data that is entered by the user is captured and stored. Now imagine the following Genie Flow:

state diagram of Q and A with data capture

In this flow, the user is asked for their name. That username is extracted and stored in the data model. The extraction is done through an LLM. The response of that LLM should be either the name of the user or the term UNDEFINED. In the latter case, the user is asked again to state their name (state Need to retry). The condition name_is_defined ensures the state machine directs the user towards the Welcome message or to retry.

One new element on the state Ai extracts name is the exit / on_exit_ai_extracts_name method. This is the method that is called when the state machine exists the state Ai extracts name. So this is when the LLM has conducted its extraction, and the response is available. This is the moment during the conversation that the programmer has control to change values in the data model attached to the state machine.

actions
When entering or exiting states, the programmer has control over what happens. Typically, these moments are used to capture responses from the LLM or users and adapt the content of an attached data model object.

Genie Flow templating

The final concept that is introduced by Genie Flow is the idea that every state maintains a template. That could be a template to construct the text that should be sent to the user or the template that is used to construct the text that is sent to the LLM.

The flow is as follows: flow of actions of rendering the template

When the state machine traverses from one state to the next, Genie Flow takes the template that is attached to the target state, renders the template with the data model object and provides it to the actor that needs it. If the event triggering the transition is a user_input event, then the next actor is an LLM. In case the event for the transition is ai_extraction, then the next actor is the user.

For templating, Genie Flow uses Jinja2, a powerful templating engine.

When rendering the template, all attributes of the data model are available. Additional attributes that are available are:

parsed_actor_input
If the actor_input field of the GenieModel happens to be JSON, then this will contain a JSON-parsed version of the actor_input.
state_id
The id of the state. This is the class property name that is given to the state.
current_date_time
The current date and time, in iso 8601 format, in the UTC time zone
chat_history
This is a serialisation of the complete dialogue history, formatted using the DialogueFormat.YAML format.

conclusion

In the previous examples, we have seen how, through states, transitions, events, conditions, actions and templates, the programmer has complete control over how a dialogue flows and how data is captured and presented along the way.

In the next chapter, we will go through these same three examples, but then with the actual Genie Flow code.