Skip to content

Genie Model

This is essentially a Pydantic model that can have as many fields as one needs. A developer would subclass the GenieModel class, which adds a number of required fields and methods. That GenieModel class also implements all functionality to persist it in a key/value store (such as Redis) in serialised form.

Data object class fields

state
This is the current state the accompanying State Machine is in. It is a string or an integer. Best not to be touched by the developer.
session_id
The unique id of a session that this Data Object belongs to. Best not to be touched by the developer.
dialogue
A list of DialogueElements that is the sequence of utterances by the different actors involved in the dialogue.

actor The name of the actor who has most recently uttered a statement. By default, this is USER for a Human actor and LLM for an LLM.

actor_input
The input that was last uttered by the most recent actor.
secondary_storage
A dict-like structure to persist "write-once-read-many" objects. These are (normally larger) objects that are compiled as part of the flow, but once saved, are not mutated at all. Whereas changes to any direct property of a GenieModel are always persisted, and objects stored under the secondary_storage field will only be persisted when they are brand new. After that, any changes (accidentally) made to the content of objects in there will not be persisted.

Data object methods and properties

The developer needs to implement the class method get_state_machine_class, which should return the class that implements the State Machine that accompanies this Data Object. A new State Machine is then created by model.get_state_machine_class()(model=model).

Some convenience methods exist:

current_response
A property that returns the most recent utterance of an actor. Or None if there is none.
format_dialogue
Returns a string representation of the dialogue, using some pre-defined formats. See the DialogueFormat class for more details.

If a developed class needs to maintain a relation to other objects that are not base classes, the class of that other object also needs to be persistable. Since we are persisting the GenieModel with all their properties, subclassing the Pydantic BaseModel is fine. This means you can create smart objects such as Managers and Stores that take away some of the management tasks that you want to centralise around these classes, rather than implement these inside your flow.

Objects that are going to be stored inside the secondary store are persisted on their own. This means that they need to inherit, not from BaseModel, but from the VersionedModel class. That is just a direct subclass of BaseModel with some additional functionality around serialisation and deserialisation.

Classes for objects in the secondary storage need to be subclasses from VersionedModel

VersionedModel

This abstract class implements everything that is required to serialise and de-serialise a Pydantic model. It also implements a schema version number. These are implemented as follows:

from pydantic import BaseModel, ConfigDict

class VersionedModel(BaseModel):

    model_config = ConfigDict(json_schema_extra={"schema_version": 42})

    @classmethod
    def upgrade_schema(cls, from_version: int, model_data: dict) -> dict:
        ...

Two things are important here:

  • The version of the schema needs to be stated. The base class VersionedModel implements this by specifying version 0 - but it is good practice to assign a new version model yourself.
  • The class method upgrade_schema may be implemented to upgrade data that is obtained from an older version of a model into the data that is required for the current version of the model. These would typically involve:
  • Mapping state_id's to their new state_id's
  • Dropping fields that no longer exist
  • Create (initial) data for new fields that have been added

This method should return a dictionary that will be used to instantiate a fresh instance of the class.

The base implementation of VersionedModel raises a ValueError when it is asked to deserialise data from a different version number.