Skip to content

Commit

Permalink
Documentation Updates
Browse files Browse the repository at this point in the history
#5

A variety of miscelleneous fixes for docs -- clarity + rendering
  • Loading branch information
elijahbenizzy committed Feb 13, 2024
1 parent ae6cf8e commit 29121ec
Show file tree
Hide file tree
Showing 9 changed files with 33 additions and 22 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Burr is a state machine for data/AI projects. You can (and should!) use it for anything where managing state can be hard. Hint: managing state
is always hard!

You can find the documentation [here](https://studious-spork-n8kznlw.pages.github.io/).
Link to [documentation](https://studious-spork-n8kznlw.pages.github.io/).

## What can you do with Burr?

Expand Down
18 changes: 11 additions & 7 deletions docs/concepts/actions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,23 @@ asynchronous context (and thus require one of the async application functions).

Actions have two primary responsibilities:

1. Compute a result from the state
2. Update the state with the result
1. ``run`` -- compute a result
2. ``update`` -- modify the state

We call (1) a ``Function`` and (2) a ``Reducer`` (similar to Redux). The ``run`` method is the function and the ``update``
method is the reducer. The `run` method should return a dictionary of the result and the ``update`` method should return
The ``run`` method should return a dictionary of the result and the ``update`` method should return
the updated state. They declare their dependencies so the framework knows *which* state variables they read and write. This allows the
framework to optimize the execution of the workflow.
framework to optimize the execution of the workflow. We call (1) a ``Function`` and (2) a ``Reducer`` (similar to `Redux <https://redux.js.org/>`_, if you're familiar with frontend UI technology).

In the case of a function-based action, the function returns both at the same time.
There are two APIs for defining actions: class-based and function-based. They are largely equivalent, but differ in use.

- use the function-based API when you want to write something quick and terse that reads from a fixed set of state variables
- use the class-based API when you want to leverage inheritance or parameterize the action in more powerful ways

-------------------
Class-based actions
-------------------

You can define an action by implementing the `Action` class:
You can define an action by implementing the ``Action`` class:

.. code-block:: python
Expand Down Expand Up @@ -90,6 +92,8 @@ Function-based actions can take in parameters which are akin to passing in const
This is the same as ``functools.partial``, but it is more explicit and easier to read.

Note that these combine the `reduce` and `run` methods into a single function, and they're both returned at the same time.

----------------------
Results
----------------------
Expand Down
6 changes: 4 additions & 2 deletions docs/concepts/hooks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ Hooks

.. _hooks:

Burr has a system of lifecycle adapters (adapted from [Hamilton's](https://github.com/dagworks-inc/hamilton) similar concept, which allow you to run tooling before and after
various places in a node's execution. For instance, you could (many of these are yet to be implemented):
Burr has a system of lifecycle adapters (adapted from the similar `Hamilton <https://github.com/dagworks-inc/hamilton>`_ concept), which allow you to run tooling before and after
various places in a node's execution. For instance, you could:

1. Log every step as a trace in datadog
2. Add a time-delay to your steps to allow for rendering
3. Add a print statement to every step to see what happened (E.G. implement the printline in cowsay above)
4. Synchronize state/updates to an external database
5. Put results on a queue to feed to some monitoring system

Note some of the above are yet to be implemented.

To implement hooks, you subclass any number of the :ref:`available lifecycle hooks <hooksref>`.
These have synchronous and asynchronous versions, and your hook can subclass as many as you want
(as long as it doesn't do both the synchronous and asynchronous versions of the same hook).
Expand Down
1 change: 1 addition & 0 deletions docs/concepts/state-machine.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ There are three APIs for executing an application.
------------------

Returns the tuple of the action, the result of that action, and the new state. Call this if you want to run the application step-by-step.

.. code-block:: python
action, result, state = application.step()
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/state.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface, but there are a few extra:
.. code-block:: python
state.subset(["foo", "bar"]) # return a new state with only the keys "foo" and "bar"
state.get_all() # return a dictionary of all the state
state.get_all() # return a dictionary with every key/value of the state
When an update action is run, the state is first subsetted to get just the keys that are being read from,
then the action is run, and a new state is written to. This state is merged back into the original state
Expand Down
5 changes: 3 additions & 2 deletions docs/concepts/transitions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Transitions

.. _transitions:

Transitions move between actions. You can think of them as edges in a graph.
Transitions define explicitly how actions are connected and which action is available next for any given state.
You can think of them as edges in a graph.

They have three main components:
- The ``from`` state
Expand Down Expand Up @@ -32,4 +33,4 @@ when determining which action to run next.
Note that if no condition evaluates to ``True``, the application execution will stop early.
See the :ref: `transitions docs <transitionref>` for more information on the transition API.
See the :ref:`transition docs <transitionref>` for more information on the transition API.
5 changes: 3 additions & 2 deletions docs/getting_started/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
Installing
==========

Burr requires almost no dependencies. Every "extra"/"plugin" is an additional install target. Note, if you're using ``zsh``,
you'll need to add quotes around the install target, like `pip install "burr[graphviz]"`.
Burr requires almost no dependencies. Every "extra"/"plugin" is an additional install target.

.. code-block:: bash
Expand All @@ -22,3 +21,5 @@ And to visualize your state machines on streamlit, you can install the `burr[str
pip install burr[streamlit]
Don't worry, you can always install these extras later if you need them.

Note, if you're using ``zsh``, you'll need to add quotes around the install target, like `pip install "burr[graphviz]"`.
14 changes: 8 additions & 6 deletions docs/getting_started/why-burr.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ Why do you need a state machine for your applications? Won't the normal programm
Yes, until a point. Let's look at a chatbot as an example. Here's a simple design of something gpt-like:

#. Accept a prompt from the user
#. Does some simple checks/validations on that prompt (is it safe/within the terms of service)
#. If (2) it then decides the mode to which to respond to that prompt from a set of capabilities. Else respond accordingly:
#. Perform some simple checks/validations on that prompt (is it safe/within the terms of service)
#. If (2) then decide the mode to which to respond to that prompt from a set of capabilities. Else responds accordingly:
* Generate an image
* Answer a question
* Write some code
* ...
#. It then queries the appropriate model with the prompt, formatted as expected
#. If this fails, we present an error message
#. If this succeeds, we present the response to the user
#. Query the appropriate model with the prompt, formatted as expected
* On failure, present an error message
* On success, present the response to the user
#. Await a new prompt, GOTO (1)

Visually, we might have an implementation/spec that looks like this:
Expand All @@ -29,7 +29,9 @@ Visually, we might have an implementation/spec that looks like this:


While this involves multiple API calls, error-handling, etc... it is definitely possible to get a prototype
that looks slick out without too much abstraction. Let's get this to production, however. We need to:
that looks slick out without too much abstraction.

Now, let's get this to production. We need to:

#. Add monitoring to figure out if/why any of our API calls return strange results
#. Understand the decisions made by the application -- E.G. why it chose certain modes, why it formatted a response correctly. This involves:
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/conditions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Conditions/Transitions
=======================

.. transitionref:
.. _transitionref:

Conditions represent choices to move between actions -- these are read by the application builder when executing the graph.
Note that these will always be specified in order -- the first condition that evaluates to ``True`` will be the selected action.
Expand Down

0 comments on commit 29121ec

Please sign in to comment.