The gallery

Twenty-three examples, each with the graph it compiles to

Every pattern the node catalogue can express, built as a finished package: a straight line, three kinds of loop, a mount three documents deep, a guardrail that reads opposite ways depending on which side of the agent it sits, a workflow that remembers what it was told across two separate runs, and a flagship that reads a live YouTube transcript and hands it to Claude. All twenty-three are validate-clean and were smoke-run for real — the answers on this page are recorded, not imagined.

What an example is

Four words, four jobs, no overlap

A package is a definition. A template creates one. A mount instantiates one. An example is a finished package you take a copy of.

The distinction that matters to you is not what they are made of — it is what happens when the original changes later. There are two routes and only one of them keeps a link.

RouteMechanismChange the original later
Mount a package
a workflow.subgraph node pointing at it
by reference every instance changes
Copy an example
or start from a template
by copy nothing changes; the link was severed
An example is copied, never mounted where it lies. These packages ship inside the wheel, under site-packages. A mount is by reference, so mounting one there would put your running workflow at the mercy of your next pip install -U, pointing at a directory you cannot edit. openstategraph examples copy <slug> writes the package into your own workflows root — and it is transitive: copying nested-mounts brings the two packages it mounts with it.

Every diagram here came out of the compiler

Each one is CompiledWorkflow.mermaid() on the real package — the graph LangGraph holds, with every mount opened to show the workflow inside it, relabelled with the titles the package's author gave the nodes and rendered to SVG at build time on the machine that built this page — never draw_mermaid_png(), which would post the graph to a third-party rendering API. This page makes no external request at all. Regenerate them with python3 scripts/build_gallery_diagrams.py.

The names in the boxes are the ones the author chose

Draft, Review, Last candidate — each box carries the node's title from the package's own workflow.json, the same relabelling the editor's preview does for a reader. A node its author never titled keeps its id; nothing here is invented. The ids themselves — draft1, grader1 — are what the compiler uses, what a run's outputs map is keyed by, and what you will see if you call CompiledWorkflow.mermaid() yourself, alongside LangGraph's own __start__, __end__ and __default_error_handler__, which are machinery rather than steps and are not drawn here.

No example names a vendor. Copy one and it runs on the provider integration you installed — pip install 'openstategraph[anthropic]' and Anthropic is your default, with no configuration at all. Only the flagship names one, on a single node, because that example is about mixing two. A document may still pin settings.model when it genuinely needs one vendor, written in full (anthropic:claude-opus-4-1) or as a bare provider (anthropic, ollama:), which resolves to that provider's own default. The bare prefix did not resolve when this gallery was built; it does now.
Contents

Six batches, in teaching order

Not alphabetical: the straight line first, then routing, then the loops, then composition, then the ones that reach the open world and spend the most tokens, then the one instance of a rule that reads two different ways depending on which side of the agent it sits, and last the one whose interesting moment happens between two runs rather than inside one. This is the order openstategraph examples list prints.

terminal · the two commands every section below repeats
$ openstategraph examples list
$ openstategraph examples copy chained-summarizer   # mounts come with it
Batch A

Foundations — one shape each, nothing else in the way

Five graphs with no cycle between them. Each isolates a single construct so that when you meet it inside something larger you already know what it does: a straight line, a Send fan-out, a conditional edge, the two edges that make a loop, and a plan dispatched to workers by archetype.

01

Chained Summarizer · chained-summarizer

prompt chaining

Question

Answer

Cut to one sentence

Summarise

Four nodes, one path, no decision anywhere.

The pattern

The only straight line in the gallery — no branch, no cycle, no tool, no mount. It exercises exactly one thing: an agent's prompt port accepting an upstream agent's result, which is the port-level widening every longer chain is built on.

Behind the scenes

Each agent.llm compiles to a create_agent loop added with add_node, wired by four static edges — no conditional edge is emitted because nothing decides anything. The compiler adds LangGraph's __start__/__end__ terminals and a default error handler.

True and worth knowing: the smoke run kept both stages in the run's outputs map, at 532 characters and 134 characters. A chain does not throw the intermediate away, which is what makes a chain debuggable and a single mega-prompt not.

Use it

terminal
$ openstategraph examples copy chained-summarizer
$ openstategraph run ./workflows/chained-summarizer "Summarise what a state machine is, then cut it to one sentence."
Asked
“Summarise what a state machine is, then cut it to one sentence.”
Answered
One sentence, no preamble — “A state machine is a model that defines a system's possible states and the rules for transitioning between them in response to inputs.”
run.py · the same package without the CLI
from openstategraph import load_workflow

wf = load_workflow("workflows/chained-summarizer")
print(wf.ask("Summarise what a state machine is, then cut it to one sentence."))
02

Parallel Workers Join · parallel-workers-join

parallelization

Request

Join

Planner

Report

Analyst

One worker box, run N times — the fan-out is in the edge, not the drawing.

The pattern

Fan-out with a single worker archetype: the same role run several times in parallel, once per subtask the supervisor planned. The join is deterministic and costs nothing, because no model runs in it — contrast example 5, which fans out to different roles.

Behind the scenes

orchestrate.supervisor compiles to a conditional edge returning a list of Send objects, one per subtask, so LangGraph schedules the worker node several times in one superstep. function.format_report is a plain Python node that reads state["worker_results"] keyed by subtask id — it never looks at its incoming edges at all.

An honest finding this example produced: the supervisor splits a brief with a regex, not a model. Asked for “two arguments for and against daily standups”, it cut on the word “and”, so subtask 1 was “Give me two arguments for” and the worker asked what about. The report's shape was exactly right and its content was not — which is why this example exists at the front rather than buried in something larger.

Use it

terminal
$ openstategraph examples copy parallel-workers-join
$ openstategraph run ./workflows/parallel-workers-join "Give me two arguments for and against daily standups."
Asked
“Give me two arguments for and against daily standups.”
Answered
A # Standup arguments report with exactly two ### sections, in task-id order — the join is ordered, and it is ordered without a model.
03

Classifier Router QA · classifier-router-qa

routing

 general 

 how_to 

 world_facts 

General

How To

World Facts

Question

Reply

Steps

Fact

Intent

Three exclusive branches; the dotted edges are the conditional ones.

The pattern

The only pure-routing example: three mutually exclusive terminal branches, no grader and no loop. One router call plus exactly one agent call per run, which makes it the cheapest way to see what a conditional edge actually costs.

Behind the scenes

route.classifier compiles to add_conditional_edges with a path map from branch id to node name — the three dotted edges above. Each branch gets its own output.formatted on purpose: an input port is maxConnections: 1, so two branches converging on one output is a graph you can load but cannot redraw.

The smoke run is the demonstration: asked the time in Tokyo it routed to b-world and said it has no clock. decisions came back as {"router1": "b-world"} and outputs held one agent entry — the other two branches were never scheduled, so you pay for one model call, not three.

Use it

terminal
$ openstategraph examples copy classifier-router-qa
$ openstategraph run ./workflows/classifier-router-qa "What time is it in Tokyo?"
Asked
“What time is it in Tokyo?”
Answered
A short answer explaining it has no clock, on the world_facts branch — and answer.decisions naming the branch it took.
04

Evaluator Optimizer · evaluator-optimizer

revision loop

 revise 

 pass 

Draft

Review

Request

Release note

The revise edge going back up is the whole loop.

The pattern

The canonical two-node cycle, and the gallery's reference for the claim that a loop here is two edges rather than a different kind of tool. A drafter produces, a grader judges, and grader.revise lands on agent.feedback.

Behind the scenes

The grader compiles to a conditional edge with two destinations — the output on pass, the drafter on revise — so the cycle is a cycle in the StateGraph itself, not a wrapper around one. A loop is drawable only because revise is a feedback-typed port: the acyclic rule returns early for exactly that type, which keeps an accidental cycle inexpressible while this one is two clicks.

What the smoke run recorded is the case people forget to test: the first draft passed. attempts: 1, decisions {"grader1": "pass"} — the revise edge was available and unused. A revision loop that never revises is still the right graph.

Use it

terminal
$ openstategraph examples copy evaluator-optimizer
$ openstategraph run ./workflows/evaluator-optimizer "Write a two-sentence release note for a bug fix."
Asked
“Write a two-sentence release note for a bug fix.”
Answered
Two sentences — the defect, then the fix — with no marketing language, passed on the first attempt.
05

Archetype Orchestrator Report · archetype-orchestrator-report

orchestrator-worker

Brief

Join

Planner

Report

Analyst

Writer

Two worker archetypes under one supervisor, joining into one report node.

The pattern

Heterogeneous workers: the supervisor labels each subtask with an archetype and the Send dispatch picks the matching worker node. A subtask it leaves unlabelled degrades to the default worker — and that degradation is part of the demo, not an accident.

Behind the scenes

One conditional edge emits a list of Sends addressed to different node names; both worker nodes write into the same worker_results state key, which is why that key carries a named reducer rather than being a bare field. Two writers in one superstep is precisely the situation a plain LastValue field cannot survive.

The most useful thing this example produced is a gap, recorded rather than hidden: the run came back with decisions {}. Which archetype handled which subtask is not surfaced anywhere in a run result — on a graph like this one, that is the difference between an answer from the researcher and one from the writer, and you currently cannot tell.

Use it

terminal
$ openstategraph examples copy archetype-orchestrator-report
$ openstategraph run ./workflows/archetype-orchestrator-report "Plan a 30-minute onboarding session for a new engineer."
Asked
“Plan a 30-minute onboarding session for a new engineer.”
Answered
A usable # Onboarding plan — but from a single subtask, because the brief carried no separator for the splitter to cut on. Recorded as it happened.
Batch B

Loops — five cycles, and every one of them stopped

A revision loop is a cycle across nodes: a grader's revise edge back into an agent's feedback port, ending when the grader passes or the budget runs out. Not to be confused with the tool-calling loop inside a single agent, and not measured in “iterations”: the step budget counts LangGraph supersteps, and one lap with fan-out costs several.

06

Two Stage Double Loop · two-stage-double-loop

two loops, two rubrics

 revise 

 pass 

 pass 

 revise 

Changelog

Accuracy review

Readability review

Request

Release note

Customer rewrite

Two cycles in series: accuracy first, then readability.

The pattern

Two independent cycles in series, each grader with its own criteria and its own ceiling — an engineer's changelog graded on accuracy, then a customer rewrite graded on readability. Serial by necessity rather than taste: agent.feedback takes one link, so two revise edges onto one agent would swap rather than stack.

Behind the scenes

Two conditional edges, two cycles, and the second stage's entry is the first grader's pass destination — so “stage one is finished” and “stage two begins” are the same edge. Nothing sequences them but the graph.

This example falsified its own design note, which is why it is worth reading. It was written expecting two counters advancing independently; the run returned attempts: 4 from one graph-wide counter. So two maxAttempts values are two ceilings on a single number, not two budgets — a distinction that only shows up when both loops actually run.

Use it

terminal
$ openstategraph examples copy two-stage-double-loop
$ openstategraph run ./workflows/two-stage-double-loop "Draft a changelog entry, then make it customer-readable."
Asked
“Draft a changelog entry, then make it customer-readable.”
Answered
Two customer-facing sentences with no component name and no use of the word “bug”; both graders passed, each cycle having run once.
07

Budget Exhaustion · budget-exhaustion

a loop that stops without passing

 revise 

 pass 

Answer

Impossible rubric

Question

Last candidate

Shaped like example 4 — with criteria that can never be met.

The pattern

The only example whose grader never passes: its criteria contradict each other on purpose. It terminates on the graph's own attempt counter and ships the last candidate anyway, which is what a loop must do when the work is not good enough and stopping is still the right answer.

Behind the scenes

The same two-destination conditional edge as example 4. What stops the run is the attempt counter in graph state, not LangGraph's recursion_limit — which is the point. Revisions and supersteps are different units, and only one of them is yours to set.

It terminated cleanly with attempts: 2 == maxAttempts and no GraphRecursionError. It also exposed something worth knowing before you trust a verdict: the forced stop records decisions {"grader1": "pass"} with warnings: [] — indistinguishable, from the outside, from a draft that genuinely passed.

Use it

terminal
$ openstategraph examples copy budget-exhaustion
$ openstategraph run ./workflows/budget-exhaustion "Answer in exactly seven words: why is version control useful?"
Asked
“Answer in exactly seven words: why is version control useful?”
Answered
“Tracks changes, enables collaboration, prevents data loss.” — the last candidate, shipped after the budget ran out rather than after a pass.
08

Agentic RAG Rewrite · agentic-rag-rewrite

grade the context, rewrite the question

 pass 

 revise 

Grounding review

Vague question

Grounded answer

Retriever

Rewriter

The revise edge lands on the rewriter, not on the answer's producer.

The pattern

The one loop whose revise edge reshapes the question instead of the answer, so the cycle re-enters retrieval rather than generation. Wiring order is load-bearing: the rewriter must sit upstream of the retriever, because an agent's prompt port takes exactly one edge.

Behind the scenes

A knowledge-lookup tool atom binds to the retriever's tools bus, and the grader's conditional edge points back two nodes rather than one. Nothing in the compiler asks whether the target of a revise edge produced the candidate — which is what makes this shape legal.

It was an open design question whether that should be legal, and the live run answered it: yes. Lap one asked a question aimed at a topic that does not hold the figure and got “the handbook does not”; the rewrite re-aimed it, and lap two came back grounded in the escalation-paths document with a quoted value. Feeding the feedback to the answer's producer would have re-run the same lookups against the same framing.

Use it

terminal
$ openstategraph examples copy agentic-rag-rewrite
$ openstategraph run ./workflows/agentic-rag-rewrite "How do I get something fixed fast?"
Asked
“How do I get something fixed fast?” — deliberately vague, and unanswerable as phrased.
Answered
Two laps, one rewrite: an answer grounded in the store's escalation-paths topic, quoting a value that exists only in the package's own knowledge/ directory.
09

Fanout In A Loop · fanout-in-a-loop

a lap that costs several supersteps

 revise 

 pass 

Report review

Brief

Join

Planner

Report

Analyst

The only cycle in the gallery that contains a Send.

The pattern

A grader that rejects the joined report sends the whole plan back to the supervisor, not the prose back to a writer. It is the teaching example for the superstep-versus-lap distinction, and the only one that uses the supervisor's feedback input.

Behind the scenes

Two conditional edges in one cycle: the supervisor's Send dispatch and the grader's verdict. The join and the grader are separate nodes on purpose — the join has no model in it, so a rejected lap re-plans and re-runs the workers without re-paying for the assembly.

Measured, not estimated: 14 supersteps for two laps. That is the four-per-lap arithmetic the example exists to show, and the reason the step budget must never be labelled “max iterations” — you would set it to 2 and it would stop halfway through the first lap.

Use it

terminal
$ openstategraph examples copy fanout-in-a-loop
$ openstategraph run ./workflows/fanout-in-a-loop "Compare two ways to deploy a Python service, then judge which is safer."
Asked
“Compare two ways to deploy a Python service, then judge which is safer.”
Answered
A # Deployment comparison with three task-id-ordered sections, the last opening “Managed container platform is safer to operate.” — one re-plan, then a genuine pass.
10

Approval In The Loop · approval-in-the-loop

the human closes the cycle

 rejected 

 approved 

Draft

Send it?

Request

Approved message

Same shape as a grader loop — with a person where the grader was.

The pattern

The only cycle a person closes. human.approval.rejected is a feedback-typed output, so a rejection routes to agent.feedback exactly as a grader's revise would — the port type is the whole reason the two are interchangeable here.

Behind the scenes

The approval node compiles to LangGraph's interrupt(), so a paused run is a durable checkpoint rather than a thread parked in memory. The resume payload is approve or reject plus optional feedback, and the graph continues from the checkpoint — including across a process restart.

Smoked through the HTTP API rather than the CLI, because a pause needs an answer: POST /api/runs refused with the documented 409, /api/runs/stream paused with an interrupt frame carrying {message, candidate}, a resume with reject plus feedback produced a redraft and paused again, and a resume with approve shipped it. Today the CLI can watch an approval but not answer one.

Use it

terminal · the CLI shows the pause; answer it in the editor or over HTTP
$ openstategraph examples copy approval-in-the-loop
$ openstategraph run ./workflows/approval-in-the-loop "Draft a one-line apology to a customer whose order was late."
Asked
“Draft a one-line apology to a customer whose order was late.”
Answered
The run pauses with the candidate in the interrupt payload. Reject with a note and the drafter tries again; approve and the answer ships.
Batch C

Composition — a whole workflow as one step

A mount is another package run as one isolated step: a task goes in, an answer comes back, and the child never sees the parent's state or message history. Each dashed panel in the three diagrams below is a mount, opened — a whole other document, drawn inside the one that runs it.

LangGraph cannot open a mount, so we open it ourselves. xray=True expands a LangGraph subgraph — a compiled graph added with add_node. A mount is not assembled that way: the compiler compiles the child and adds a closure that invokes it, doing the state mapping, the isolation boundary and the child's own asset and memory namespacing on the way. What the parent's StateGraph holds is an ordinary Python function, and LangGraph cannot see through one. So the dashed panels below are spliced in by CompiledWorkflow.mermaid() from what the compiler recorded while it built each child, to any depth — still every node the compiler actually produced, never a hand-drawn approximation. Ask for mermaid(xray=False) and each mount goes back to being the single opaque box LangGraph itself holds, which is the honest picture when a mount is the suspect.
11

Nested Mounts · nested-mounts + nested-mounts-mid

composition depth
Level 1 · nested-mounts

Nested Mounts (middle)

Chained Summarizer

Question

Answer

Question

Answer

Question

Answer

Cut to one sentence

Summarise

Level 2 · nested-mounts-mid

Chained Summarizer

Question

Answer

Question

Answer

Cut to one sentence

Summarise

Three documents, and the first diagram contains all three: mount-mid opens to the second, and mount-inner inside it opens to the third, one dashed panel nested in the other. The middle document is drawn beside it because it is a package in its own right — what ?w=nested-mounts/mount-mid resolves to, and a workflow you can run on its own. The third level is not drawn a second time; it is example 1, and you are already looking at it twice.

The pattern

Depth for its own sake: a document that mounts a document that mounts a document. One question goes down three levels and one answer comes back, which is the smallest honest test of whether composition composes.

Behind the scenes

Each workflow.subgraph node compiles to a closure that loads the child package, compiles it, maps the parent's question into the child's input and the child's answer back out. Drill-in addressing follows the same path in the editor: ?w=nested-mounts/mount-mid resolves to the middle document and ?w=nested-mounts/mount-mid/mount-inner walks on to the third, warnings empty at both depths.

Two facts the live run pinned down. The parent's outputs map holds the parent's nodes only — the isolation boundary is real. And attempts came back as 2: the graph-wide counter crosses the mount boundary and counted two model calls made three levels down.

Pointing a mount at its own package is refused before a token is spent, by load_workflow rather than by a rule you can forget to run: “Workflow 'nested-mounts' mounts itself (nested-mounts -> nested-mounts); a mount cycle can never terminate.” Worth knowing that openstategraph validate still reports VALID for that document — the zero-token gate cannot dereference a mount.

Use it

terminal · the copy is transitive — three packages arrive
$ openstategraph examples copy nested-mounts
$ openstategraph run ./workflows/nested-mounts "Explain what a compiler does, briefly."
Asked
“Explain what a compiler does, briefly.”
Answered
One sentence, in about eight seconds — example 1's contract holding through two layers of indirection.
12

Same Package Twice · same-package-twice

one definition, two instances
Parent · same-package-twice

Instance A — terse

Instance B — analogy

Question

Answer

Question

Answer

Answer

Question

Cut to one sentence

Summarise

Cut to one sentence

Summarise

mount-terse and mount-analogy are two instances of one definition, and the diagram shows it rather than asserting it: two dashed panels, the same four nodes inside each. They differ only by their data.overrides, which live on the parent — the package's own bytes are never touched.

The pattern

The instance demo: two mounts of one package, each carrying its own overrides, and the definition on disk unchanged. Chained rather than joined — there is no unlimited fan-in port to merge two mount results with, so instance B reads instance A's answer.

Behind the scenes

Two closures over the same child package, each applying its own override map to a child node's fields at compile time. An override that names a child node id which does not exist warns and runs the package default rather than failing silently — a misspelled id produced exactly the documented warning in a control run.

The claim is checkable and it was checked: chained-summarizer/workflow.json hashed sha256 6fe426d961cd6bd9… before the run and 6fe426d961cd6bd9… after. Byte-identical, while its two instances answered differently in the same run.

Use it

terminal
$ openstategraph examples copy same-package-twice
$ openstategraph run ./workflows/same-package-twice "Describe a linked list."
Asked
“Describe a linked list.”
Answered
Instance A, whose override says twelve words at most: “A linked list is nodes pointing to the next, enabling sequential access.” Instance B, asked for an analogy, returned a forty-three-word treasure hunt.
13

Delegate by Mount · delegate-by-mount

delegation
Parent · delegate-by-mount

Web Research Digest

SQL QA

 database 

 web 

 revise 

 pass 

Request

Database answer

Web digest

Which package answers this

Answer

Digest

Question

Research question

Analyst

Researcher

Did it actually read something

A supervisor of packages rather than of agents. The router's two dotted branches each enter a different document; note the revise edge inside mount-web and its absence inside mount-sql — one child loops and the other does not, which the parent cannot see and does not need to. Both children are also examples 17 and 18, standing alone.

The pattern

Heterogeneous mounts as exclusive branches: a classifier picks one of two whole packages and that package answers. It is delegation as far as today's vocabulary reaches — a mount cannot be a tool, because workflow.subgraph emits a result and nothing but a tool atom emits a tool.

Behind the scenes

One conditional edge, two closures. Each mount may declare an outcome describing what it guarantees, and the compiler checks that claim against the child: the web mount states one and compiles clean because that package really does route a revise edge; the SQL mount states none, because it would have nothing to back it.

The run routed to b-database, answered 3503 with the query under it, and the other package was never loaded — not compiled, not read from disk. A child's own decisions do not surface in the parent either, which is the isolation boundary being consistent rather than leaky.

Use it

terminal · brings sql-qa and web-research-digest with it
$ openstategraph examples copy delegate-by-mount
$ openstategraph run ./workflows/delegate-by-mount "How many tracks are in the database?"
Asked
“How many tracks are in the database?”
Answered
3503, from the SQL package alone, with decisions {"router1": "b-database"} naming the branch that got there.
14

Skill-driven Rubric · skill-driven-rubric

rules from a file

 pass 

 revise 

Check it against the same skill

Request

Commit message

Write the message

One skill file, two readers — the writer and the grader.

The pattern

The only example where neither the agent's rules nor the grader's criteria are typed on a card: both arrive over the skill port from one Markdown file. One input.skill output feeding two skill inputs is the shape that proves the skill layer is a layer rather than an agent field.

Behind the scenes

The skill contributes the rules section of each node's composed prompt — the only editable section. The preamble that says what the node is, the generated context, and the output contract are the base's, and the contract goes last so a rule like “explain your reasoning” cannot countermand the shape of the answer. Both nodes are in replace mode, so the file is the source and nothing else contributes.

The control run is the real evidence, and it points the other way: the same question against a copy with the skill node deleted came back with bullets, a **Title:** label, <module/component> placeholders and a Closes: #<issue> footer — four of the skill's explicit prohibitions — and the grader passed it. Unwiring the rules removes the check in the same move.

Use it

terminal
$ openstategraph examples copy skill-driven-rubric
$ openstategraph run ./workflows/skill-driven-rubric "Write a commit message for a fix to a null-pointer crash."
Asked
“Write a commit message for a fix to a null-pointer crash.”
Answered
A message obeying rules that exist nowhere but the skill file — an imperative subject line under fifty characters with no full stop, three prose sentences, no bullets, no footer.
15

Knowledge Lookup QA · knowledge-lookup-qa

retrieval, no loop

Vault Registrar

Question

Answer

The tool binds to the agent; it is not a step in the graph.

The pattern

Retrieval as a plain tool binding, with the package's own knowledge/ directory as the store and no loop around it. It pairs with example 8, which grades what this one merely fetches.

Behind the scenes

A tool atom is not a node in the compiled graph — it binds to the agent, so the whole retrieval happens inside one create_agent loop and the diagram stays four boxes. Deliberately not vector search: the store is three-tier progressive disclosure over Markdown topic docs, one index line per topic, the full document fetched only once the agent commits to that direction.

The store is fictional on purpose — a seed vault that does not exist — so no figure can arrive from the model's weights. It answered “every 20 years” and “below 70 %”, both correct and both unguessable, and threads show replays the two tool calls behind them without calling a model to reconstruct the story.

Use it

terminal
$ openstategraph examples copy knowledge-lookup-qa
$ openstategraph run ./workflows/knowledge-lookup-qa "How often is seed viability tested, and what triggers a regeneration?"
Asked
A question only the package's own topic docs can answer.
Answered
“every 20 years” and “below 70 %”, naming the topic it read them from — with the tool calls visible in the run rather than a confident recollection.
Batch D

Real world — tools that leave the machine, and one that costs money

The last five spend the most and are the only ones whose acceptance test involves something outside this repository: a live database, the open web, a YouTube transcript, and a second model provider. Where one of them failed, it is written down here as it happened.

16

YouTube Trend Digest · youtube-trend-digest

the flagship

Question

Digest

Transcript reader

Synthesis

Trend finder

Three agents, four tool atoms, two providers — and still a straight line.

The pattern

Find what is trending right now, read the top video's actual transcript, and hand both to a different provider for a three-sentence synthesis. The only multi-provider example, and the only one whose acceptance test is external reachability rather than a shape.

Behind the scenes

A chain of three create_agent loops with different tool bindings — search and fetch on the first, the transcript atom on the second, none on the third — and a model string on one node that names Anthropic while the graph's setting names Ollama. Provider is a field on a node, so a two-provider graph needs no second runtime, no adapter and no special node type.

End to end, measured with an in-process usage callback rather than estimated: a live trend found, a 1 140-character transcript fetched, and a Claude synthesis that quoted two phrases from the captions and named a character who appears nowhere but them. 9 221 tokens on gpt-oss:120b plus 1 627 on claude-haiku-4-5 — $0.0022, in 23.8 seconds.

And the honest half: an earlier run found nothing, because the single search backend was answering every request with a challenge, and two of the three rungs of this workflow are that tool. It burned 61 832 tokens producing no digest. That is recorded as a gap with an evidenced fallback, not smoothed over with a stub.

Use it

terminal · needs ANTHROPIC_API_KEY for the final node
$ openstategraph examples copy youtube-trend-digest
$ openstategraph run ./workflows/youtube-trend-digest "What is trending on YouTube right now?"
Asked
“What is trending on YouTube right now?”
Answered
Three sentences about a specific video that was two hours old, grounded in phrases from its transcript — not a generic essay about the topic.
17

SQL QA · sql-qa

text-to-SQL

Analyst

Question

Answer

Three read-only tools on one agent; the database ships with the package.

The pattern

Natural language to SQL over a real database, using the generic prebuilt SQL atoms: list the tables, read one schema, run one read-only SELECT. Nothing in it is specific to the sample database except the file the three nodes point at.

Behind the scenes

Three tool atoms bind to a single agent, so the list-then-schema-then-query sequence is the agent's own tool-calling loop rather than three graph steps — the compiled graph is four boxes for a job people usually draw as a pipeline. The package carries its own copy of the database under data/, and paths are resolved relative to whichever workflows root holds the package, so the copy works wherever you put it.

This is the one example a machine grades. Its evals/ directory holds five cases scored by execution accuracy — the answer's result set compared with a gold query's, not its wording — and it scored 100.0%: four of four on execution accuracy and exact set match, one of one on a question it was right to refuse, p50 5.04 seconds. The hard two-join case returned the gold row USA, 523.06.

Grading a run and grading a dataset are different jobs done by the same machinery — a grader's verdict is an edge, an eval's verdict is a destination. The difference is written up in docs/evaluation.md.

Use it

terminal
$ openstategraph examples copy sql-qa
$ openstategraph run ./workflows/sql-qa "How many customers are in the database?"
$ python -m pytest ./workflows/sql-qa
Asked
“How many customers are in the database?”
Answered
59, with SELECT COUNT(*) … FROM Customer shown underneath it.
18

Web Research Digest · web-research-digest

search, fetch, report

 revise 

 pass 

Researcher

Did it actually read something

Research question

Digest

One agent, two web tools, and a grader that knows what “read it” means.

The pattern

The only example whose tools leave the machine, and therefore the only one with a documented network-failure shape. One agent loops over search and fetch until it has a source it actually read; a grader that can tell a digest from a recollection sends it back when it has not.

Behind the scenes

Two tool atoms on one agent plus one grader cycle — contrast example 20, which spends more calls to reach several sources at once. The interesting compile detail is what is not here: retry and timeout are parameters of graph assembly, available to every node of every family, so a flaky fetch is not a reason for a tool base class to grow a retry field.

Two runs, two things proven. The first named Python 3.14.7, released 8 August 2026, with two fetched URLs and a count of 499 bug fixes — a figure that exists on the page and in no model's weights — after one revise lap. The second was the real test: asked to read an unresolvable host, it quoted “That host is not reachable from here” and answered nothing. A readable tool error, never a silent stub.

Use it

terminal
$ openstategraph examples copy web-research-digest
$ openstategraph run ./workflows/web-research-digest "What changed in the most recent Python release?"
Asked
“What changed in the most recent Python release?”
Answered
A short digest naming the release, its date and its bug-fix count, with the URLs it fetched — and attempts: 2, so the first draft was sent back.
19

Support Triage · support-triage

the long control chain

 rejected 

 approved 

 pass 

 revise 

 account 

 billing 

 technical 

Account desk

Billing desk

Technical desk

Send to the customer?

Is this sendable

Held ticket note

Ticket

Held for review

Approved reply

Which desk

Classify, answer, grade, gate — every control molecule in one graph.

The pattern

The longest control chain the vocabulary can express: a classifier picks a desk, the desk answers, a grader checks the reply, and a person decides whether it is sent. Nothing reaches a customer without the gate.

Behind the scenes

Two conditional edges and an interrupt(), with two terminal outputs — one for sent, one for held. It is also as close as the current vocabulary gets to a handoff, and the boundary is worth naming: a router picks a branch and the branch runs; an agent cannot hand a live conversation to another agent.

One limit this example cannot design around, and one it exposed. Three desks fanning into one grader means the grader has no expressible revision loop — agent.feedback takes one link, so a revise verdict falls through to the first declared destination. The second is fixed: the person at the gate used to be shown the draft alone, so the interrupt payload now carries verdict and reason from the grader that produced the candidate — the judgement it reached, not the branch it routed down.

Use it

terminal · the gate is answered in the editor or over HTTP
$ openstategraph examples copy support-triage
$ openstategraph run ./workflows/support-triage "My invoice is wrong and nobody has replied for a week."
Asked
“My invoice is wrong and nobody has replied for a week.”
Answered
Routed to billing, drafted, graded, and stopped at the gate. Rejecting with a note produced the held-ticket record; approving on a fresh thread sent it.
20

Morning Brief · morning-brief

many sources, one report

Brief request

Assemble

Editor

Brief

Handbook reader

Platform inspector

Web researcher

Three workers, three different tools, one join — the most expensive example, deliberately last.

The pattern

The only fan-out whose workers carry tools: the open web, this package's own handbook, and the platform's own workflow list. Distinct from example 5, where the archetypes differ by role rather than capability, and from example 18, where one agent loops over tools instead of several agents each owning one.

Behind the scenes

A Send dispatch to three differently-equipped workers, each also reading its rules from one shared input.skill — a worker has no authorable prompt of its own, so a single skill file steers all three. They land in one worker_results key and a model-free join assembles the brief.

The smoke question is a bare numbered list, and that is not a stylistic choice: the planner splits on a regex, so the question's shape decides the plan. All three workers ran with their own tool. The section that stayed honest when a tool misbehaved was the section the skill was steering.

Use it

terminal
$ openstategraph examples copy morning-brief
$ openstategraph run ./workflows/morning-brief "1. What changed in Python recently? 2. What is our release checklist? 3. What workflows are installed?"
Asked
Three numbered questions, one per source.
Answered
A # Morning brief with three task-id sections, each visibly produced by its own tool — web, handbook, platform.
Batch E

Guarded — one rule, read two ways

The only example whose unit of policy is entity × direction: the same card type reaches opposite verdicts depending on which side of the agent it sits, and a block is a wire rather than an exception.

21

Guarded Lookup · guarded-lookup

one policy, two directions

 allowed 

 blocked 

 allowed 

 blocked 

Support desk

At the door

On the way out

Question

Answer

Refusal

Two guard cards, same node type, opposite verdicts — inbound passes an email, outbound redacts it.

The pattern

A user gives an email address so a customer can be looked up. Inbound, that address must pass — the tool matches on the exact string, and a redacted one finds nothing. Outbound, the same entity is redacted, because what comes back is data the user never supplied. Nothing on either guard.policy card knows which direction it is; the wire does. A credit_card → block row on the inbound card is the other half: a refusal leaves by its own blocked port and reaches a dedicated Output, and no model is called at all.

Behind the scenes

Detection is borrowed, never written: LangChain's own RedactionRule shapes — email, credit_card (Luhn-checked), url — plus a hand-written regex for phone, which LangChain does not ship. pass is the one strategy the library has no opinion about, added so an inbound entity the product needs can be named rather than silently unhandled. A guard's verdict is a value, not an exception: the compiler turns it into a conditional edge, so a block short-circuits before the agent ever runs.

True and worth knowing: the redact run took 2.4 s and left outputs["in1"] holding the address exactly as typed, while outputs["agent1"] shows [REDACTED_EMAIL] in its place — proof the tool saw the real address and only the agent's answer was scrubbed. The block run took 0.0 s: no model runs, and the card number is gone from every outputs entry, including in1, which a redaction would have left alone.

Use it

terminal
$ openstategraph examples copy guarded-lookup
$ openstategraph run ./workflows/guarded-lookup "What plan is bjorn.hansen@yahoo.no on, and how do I reach him?"
Asked
“…full contact details on file for ftremblay@gmail.com, including their email address.”
Answered
“François Tremblay lives in Montréal, Canada, is on the Premium plan, and can be reached at **** 721‑4711. Their support ticket is [REDACTED_URL] and their email is [REDACTED_EMAIL].”
Batch F

Memory — the one whose point is between two runs

Every other example on this page is interesting inside a single run. This one is interesting between two: a node drawn on the wire records what crosses it and hands the ledger to whatever comes next, so a second run in a brand-new thread already knows what the first one was told.

22

Remembers Across Runs · remembers-across-runs

one ledger, many runs

Concierge

Question

What you have told me

Answer

Four nodes in a straight line — and the memory node is on the line, which is the whole mechanism.

The pattern

A memory.segment node sits between the question and the agent. Every time flow reaches it, it does three things in this order: furnishes the entries the segment already holds into what flows onward, records what just arrived, verbatim, and passes both on. Furnish before record, deliberately — the other order hands the agent its own question back inside the block it is reading, which looks like a duplicate and is really a lie about when the line was written. Wire the same node off to one side and it records nothing, because nothing crosses it. Position is the mechanism.

Behind the scenes

The ledger is keyed by the segment's name (what-you-told-me) and by the workflow, in the durable Store — not the checkpointer, which is the thing that carries a conversation and is exactly what this example does without. Two tollbooths carrying the same name are one ledger, read and appended at both positions. retention is 20: bounded on purpose, because this package gets copied into somebody's own workflows root and grows in their Store. Blank means unbounded and is perfectly legal; a shipped example is the wrong place to demonstrate it.

True and worth knowing: no model is reachable from the write path, so a crossing costs zero tokens — by construction, not by discipline. And the card deliberately shows no entry count: the ledger is server-side, so a card cannot know its size before a run. It shows the name and the retention, both true with nothing having happened, and the count is furnished by the run itself.

Use it

terminal · two runs, two different threads
$ openstategraph examples copy remembers-across-runs
$ openstategraph run ./workflows/remembers-across-runs "My favourite composer is Sibelius, and I always take my coffee black." --thread-id run-one
$ openstategraph run ./workflows/remembers-across-runs "Who is my favourite composer, and how do I take my coffee?" --thread-id a-completely-different-thread
Run 1 · empty ledger
“Thanks for sharing!”
Run 2 · a thread that has never seen run 1
“Your favourite composer is Sibelius, and you always take your coffee black.”

The thread ids differ, so the checkpointer holds nothing from run 1. Everything the second answer knows arrived in the block the memory node furnished. Recorded 2026-08-15 on ollama:gpt-oss:120b-cloud.

After you copy one

It is your package from that moment

The copy is severed: editing it changes nothing upstream, and upgrading OpenStateGraph changes nothing in it. It arrives as a draft, exactly like anything openstategraph new writes — publishing it is the one deliberate human action that puts it in front of end users in /chat.

Read the graph without running it

terminal · costs nothing, calls no model
$ openstategraph validate ./workflows/sql-qa
$ openstategraph graph ./workflows/sql-qa   # the Mermaid on this page

graph prints text and makes no network call — the same reason every diagram above was rendered locally. Exit codes are fixed, so validate works as a CI gate.

Or skip the CLI entirely

run.py
from openstategraph import load_workflow

wf = load_workflow("workflows/sql-qa")
if wf.warnings:                 # what did not wire, never silenced
    print("degraded:", wf.warnings)

answer = wf.ask("How many customers are in the database?")
print(answer, answer.decisions)

# .graph is an ordinary LangGraph object — the escape hatch:
print(wf.graph.get_graph(xray=True).draw_mermaid())
These twenty-three are permanent. They are not demo scaffolding to be tidied away — they are the regression surface for every pattern the catalogue can express, and a test asserts that every package shipped in the wheel validates, compiles warning-free and has an entry and an exit.

Documentation links point at the repository named in backend/pyproject.toml ([project.urls] Homepage); while that repository is private, the link above 404s for anyone who is not a collaborator.