Open-source project
apache/burr avatar
apache/burr

Apache Burr: A State Machine Layer for LLM Applications You Host Yourself

Build applications that make decisions (chatbots, agents, simulations, etc...). Monitor, trace, persist, and execute on your own infrastructure.

2,548 stars195 forksPythonApache-2.0

At a glance

What is it?
Burr (incubating) models an application as a graph of Python functions over a shared state, then traces and persists every transition. It is a coordination and observability layer, not an agent framework, and the README is explicit that it will not tell you how to build models or query APIs.
Who is it for?
Adopt Burr if your application already has a decision structure you can draw on a whiteboard and you need to replay, persist or inspect it, and if you are willing to run the UI and storage yourself. Do not adopt it if you want the framework to choose prompts, call providers or manage your data; the README states plainly that Burr will not do those things.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The Coordination Problem Burr Actually Addresses

Most LLM applications start as a single function that builds a prompt, calls a model, and returns text. The trouble begins when the second step depends on the first, when a human needs to approve something in the middle, or when a run fails at step four and you want to resume from step three instead of paying for steps one through three again. Burr's answer is to make that structure explicit. You express the application as a state machine, which the README describes as a graph or flowchart, and each node is a plain Python function. The README lists the target cases directly: managing state, tracking complex decisions, adding human feedback, and dictating an idempotent, self-persisting workflow. That last phrase is the real scope. Burr is for people who have already decided what their application does and now need it to be inspectable and resumable. The README's own examples span a multi-modal chatbot, a conversational RAG example, an LLM adventure game, an email assistant, plus non-LLM work such as a time-series forecasting simulation and hyperparameter tuning. The common thread is sequencing, not language modelling.

Actions, Transitions and a State Object That Gets Rewritten

The mechanism is small enough to describe in one pass. An action is a function decorated with @action, and the decorator declares which state keys it reads and which it writes. In the README's hello-world, human_input declares reads=[] and writes=["prompt", "chat_history"], while ai_response declares reads=["chat_history"] and writes=["response", "chat_history"]. Each function receives the current State and returns a new one, typically via state.update(...) and state.append(...). The append call is how chat history grows without the action having to reconstruct the whole list. Wiring happens in ApplicationBuilder: with_actions registers the functions, with_transitions takes tuples such as ("human_input", "ai_response") and ("ai_response", "human_input"), with_state seeds initial values, and with_entrypoint names the starting node. The build call returns an application object. Execution is driven by app.run, and the README's example passes halt_after=["ai_response"] along with inputs={"prompt": "Who was Aaron Burr, sir?"}. That halt_after parameter is the detail worth noticing: you can stop the graph at a named action rather than running it to completion, which is what makes the pattern usable inside a request handler or a step-by-step UI. The declared reads and writes are not just documentation. They are what lets the tracing layer record which state keys changed at each transition, and they are the reason a persister can snapshot a run at a known boundary.

Running the Telemetry UI Before You Write Any Code

The quick start is unusually front-loaded toward the UI. Install with pip install "apache-burr[start]", then run the bare command burr. That starts a telemetry server and opens a browser view which, per the README, comes loaded with default data so you can click around without a project. There is a Demos entry in the left sidebar; selecting chatbot launches a demo chat application. That demo needs the OPENAI_API_KEY environment variable to actually converse, but the README notes you can still see how it works without a key set, which means the interesting part is the trace, not the model output. To generate your own trace, the README suggests cloning the repository and running the counter example: git clone https://github.com/apache/burr && cd burr/examples/hello-world-counter followed by python application.py. The counter prints in the terminal while the same run appears in the UI. This ordering is a deliberate design statement. Burr wants you to see the telemetry surface before you commit to the API, because if the trace does not look useful to you, the rest of the library is just a graph runner you could write yourself.

Persistence and the Hooks You Have to Supply

The README names pluggable persisters and gives memory as the example. That is the honest summary and also the gap. Burr defines the seam where state is saved and loaded, and ships integrations for storage and telemetry, but the README does not enumerate which backends exist or what their durability characteristics are. So the practical question for an adopter is not whether Burr can persist state, it is whether the persister you need already exists or whether you are writing one. The same applies to the hook system mentioned in the README, which is described as the route to integrating LLM observability vendors and storage. If you already run a tracing stack, the integration path exists but the work of mapping Burr's state transitions onto it is yours. The compensating detail is that Burr is dependency-free at the core, per the README's description of the low-abstraction library. A state machine that does not drag in a provider SDK is a real property if your deployment has to pass a dependency review.

The Python Version Split in the 0.43.0 CLI

Version 0.43.0-incubating, released 2026-08-29, carries a note that matters more than most release notes. The optional CLI bundled with the [start], [learn] and [cli] extras requires Python 3.10 or newer. Core library usage remains compatible with Python 3.9. Read that carefully, because the quick start tells you to install apache-burr[start] and then run burr. On Python 3.9 that combination is the one configuration the release note warns about. If you are pinned to 3.9 for other reasons, you can still use the library and drive it from your own code, but you lose the UI that the README treats as the primary reason to look at Burr in the first place. The release cadence is also worth noting for planning: the repository shows v0.43.0-incubating in August 2026, v0.42.0-incubating in May 2026, and burr-0.40.2 back in May 2025. The gap between 0.40.2 and 0.42.0 is roughly a year, so anyone tracking the project should expect long stretches without a tagged release rather than continuous churn.

State Machines Versus Graph Orchestration Frameworks

The obvious comparison is to graph orchestration libraries such as LangGraph, and the difference is where the abstraction sits. Those frameworks generally hand you a graph runtime with agent-oriented primitives built in, so the graph is the application. Burr takes the narrower position. The README states that Burr will not tell you how to build your models, how to query APIs, or how to manage your data, and that it exists to tie those pieces together. The action signature reflects that: ai_response in the README's example calls a placeholder _query_llm and the comment says Burr does not care how you use LLMs. If you want a framework that owns the agent loop and the provider calls, Burr will feel like it is missing pieces. If you already have those pieces and want a state machine and a trace around them, the narrower scope is the point. The README also mentions that actions can delegate to other libraries, naming Apache Hamilton as an example, so Burr is positioned as one layer in a stack rather than a replacement for the stack. A second, less obvious alternative is writing the state machine yourself. For a two-node chatbot that is a reasonable choice, and it is why the README's hello-world is short. Burr starts paying for itself when the graph has branches, human checkpoints, or a persistence requirement that you would otherwise reimplement.

Licence, Incubation Status and What to Verify Before Adopting

Burr is licensed under Apache-2.0 and is an Apache Software Foundation incubating project, which the release names make visible in the -incubating suffix. Incubation is a governance stage, not a quality grade, but it does mean the project's community and release process are still being established under ASF rules. The NOTICE and LICENSE headers in the source are standard ASF boilerplate. For licence implications in your own product, read the LICENSE file and the NOTICE file rather than relying on a summary; this article is not legal advice. On the operational side, three things are worth confirming against your own environment before you build on Burr. First, your Python version, because of the 3.10+ CLI constraint in 0.43.0. Second, whether a persister exists for the storage you actually run, since the README only names memory. Third, whether the UI is something you intend to host internally, because running the burr command opens a server and the README frames the whole workflow around that server being available. If all three line up, the library is small enough to evaluate in an afternoon: the counter example is a clone and a single python command away.

Editorial conclusion

Adopt Burr if your application already has a decision structure you can draw on a whiteboard and you need to replay, persist or inspect it, and if you are willing to run the UI and storage yourself. Do not adopt it if you want the framework to choose prompts, call providers or manage your data; the README states plainly that Burr will not do those things. Before committing, verify two things on your own machine: whether your Python version satisfies the 3.10+ requirement that the 0.43.0 CLI carries, and whether an existing persister backend matches your storage layer, since the README only names memory as an example.

Official sources

  1. apache/burr on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Community notes

Community notes