Skip to content

Commit

Permalink
Improves persistence documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
elijahbenizzy committed May 24, 2024
1 parent 2a181b3 commit 509326f
Showing 1 changed file with 54 additions and 5 deletions.
59 changes: 54 additions & 5 deletions docs/concepts/state-persistence.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ State Persistence
to save and load state automatically from a database. This enables you to launch an application,
pause it, and restart where you left off. The API is customizable, and works with any database you want.

TL;DR
-----

Burr provides an API to save and load state from a database. This enables you to pause and restart applications where you left off.
You can implement a custom state persister or use one of the pre-built ones. You can fork state from a previous run to enable debugging/loading.


Persisting State
----------------

The key to writing a real life ``burr`` application is state persistence. For example, say you're building a chat bot and you
want to store the conversation history and then reload it when you restart. Or, you have a long running process/series of agents,
Expand All @@ -19,7 +28,7 @@ and you want to store the state of the process after each action, and then reloa

State Keys
----------
Burr `applications` are, by defult, keyed on two entities:
Burr `applications` are, by default, keyed on two entities:

1. ``app_uid`` - A unique identifier for the application. This is used to identify the application in the persistence layer.
2. ``partition_key`` - An identifier for grouping (partitioning) applications
Expand Down Expand Up @@ -47,13 +56,15 @@ This action takes in an initializer (an implementation of :py:class:`StateInitia


Note (1): that you cannot use this in conjunction with :py:meth:`with_state <burr.core.application.ApplicationBuilder.with_state>`
or :py:meth:`with_entrypoint <burr.core.application.ApplicationBuilder.with_entrypoint>` -- these are mutually exclusive.
or :py:meth:`with_entrypoint <burr.core.application.ApplicationBuilder.with_entrypoint>` -- these are mutually exclusive. If you're
switching to use state persistence, all you need to do is remove the ``with_state`` and ``with_entrypoint`` calls and replace them with
the default state and entrypoint in the ``initialize_from`` call.

Note (2): The loader will not error if no state is found, it will use the default state in this case.

Forking State
_____________
Here's an explicit section on forking state. When loading you can also fork state from a previous application. This is useful if you want to start from a previous application's state,
When loading you can also fork state from a previous application. This is useful if you want to start from a previous application's state,
but don't want to add to it. The ``fork_from_app_id`` and ``fork_from_partition_key`` are used to identify the application to fork from, while
``fork_from_sequence_id`` is used to identify the sequence_id to use. This is useful if you want to fork from a specific point in the application,
rather than the latest state. This is especially useful for debugging, or building an application that enables you
Expand Down Expand Up @@ -91,8 +102,8 @@ To make the above more concrete, let's look at a basic chatbot:
("human_converse", "terminal", expr("'exit' in question")),
("human_converse", "ai_converse", default),
)
.initialize(
initializer=state_persister,
.initialize_from(
state_persister,
resume_at_next_action=True,
default_state={"chat_history" : []},
default_entrypoint="human_converse
Expand All @@ -106,6 +117,8 @@ In this case, we both read and write using the ``SQLLitePersistor``. Note that t
However, if you want to just read (E.G. for debugging), or you want to just write (if you're always creating a new app),
you can leave out ``initialize`` or ``with_state_persister`` respectively.



Supported Persistence Backends
______________________________
See :ref:`available persisters here <persistersref>`.
Expand All @@ -117,3 +130,39 @@ Customizing State Persistence

Burr exposes the :py:class:`BaseStatePersister <burr.core.persistence.BaseStatePersister>` API for custom state persistence. Implement,
pass into the above functions, and you can write to whatever database you want! Please contribute back to the community if you do so.


Loading from the Tracker
------------------------

You can use the tracking feature additionally as a persister. This enables you to load from prior
tracked runs. This is useful for debugging, or building an application that enables you to rewind state and make different choices.

.. code-block:: python
tracker = LocalTrackingClient(project=project_name)
app = (
ApplicationBuilder()
.with_actions(
ai_converse=ai_converse,
human_converse=human_converse,
terminal=burr.core.Result("chat_history"),
)
.with_transitions(
("ai_converse", "human_converse", default),
("human_converse", "terminal", expr("'exit' in question")),
("human_converse", "ai_converse", default),
)
.initialize_from(
tracker,
resume_at_next_action=True,
default_state={"chat_history" : []},
default_entrypoint="human_converse
)
.with_tracker(tracker)
.with_identifiers(app_id=app_id)
.build()
)
In this case the ``LocalTrackingClient`` is used both as a persister and a loader. It will persist as it is
running (by tracking), and then load from the tracker if the application is restarted. This is useful for local development.

0 comments on commit 509326f

Please sign in to comment.