Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
032eeb5
docs(plans): LangGraph pilot (PR 2) — eight pages teach through their…
blove Sep 6, 2026
f3e289d
test(website): browser proof that ExampleCode renders, highlights, an…
blove Sep 6, 2026
014a9d2
docs(langgraph): streaming teaches through the running example
blove Sep 6, 2026
e7f6c3a
docs(langgraph): persistence teaches through the running example
blove Sep 6, 2026
ee9ba3b
test(website): ExampleCode e2e asserts the copy source exactly
blove Sep 6, 2026
0af4e9c
docs(langgraph): streaming page — facts checked against the example, …
blove Sep 6, 2026
76f5c77
docs(langgraph): interrupts teaches through the running example
blove Sep 6, 2026
7248509
docs(langgraph): persistence page — drop the broken fork snippet; one…
blove Sep 6, 2026
f2d1c03
docs(langgraph): memory teaches through the running example
blove Sep 6, 2026
6411c13
docs(langgraph): streaming page — compile-safe retry snippet, honest …
blove Sep 6, 2026
9de0add
docs(langgraph): interrupts page — checkpointer facts per platform va…
blove Sep 6, 2026
4d50b54
docs(langgraph): durable execution teaches through the running example
blove Sep 6, 2026
27f8b6f
docs(langgraph): memory page — the demo's transcript does not accumul…
blove Sep 6, 2026
8e8ddeb
docs(langgraph): subgraphs teaches through the running example
blove Sep 6, 2026
7526331
docs(langgraph): durable execution page — what the last node really r…
blove Sep 6, 2026
3de4fb4
docs(langgraph): time travel teaches through the running example
blove Sep 6, 2026
ab9aefe
docs(langgraph): subgraphs page — correct subagent types, explain the…
blove Sep 6, 2026
4cda0ba
docs(langgraph): deployment teaches through the running example
blove Sep 6, 2026
5c1337b
docs(langgraph): streaming page — error fields and retry() per the ag…
blove Sep 6, 2026
c92007f
docs(langgraph): time travel — the example's docstring and prompt sto…
blove Sep 6, 2026
213da21
docs(langgraph): one checkpointer story across the pilot pages; resum…
blove Sep 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 147 additions & 96 deletions apps/website/content/docs/langgraph/guides/deployment.mdx

Large diffs are not rendered by default.

136 changes: 135 additions & 1 deletion apps/website/content/docs/langgraph/guides/durable-execution.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,138 @@ description: Resume a LangGraph run after a crash, restart, or redeploy from its

Persistence keeps a thread's history; durable execution keeps a _run_ alive through failure. When a step crashes, the process restarts, or a deployment rolls, the checkpointer lets the run resume from its last completed super-step instead of starting over, and the UI can offer a retry rather than a blank transcript.

This page is the live example for durable execution over LangGraph. Use **Run** to interrupt and retry a run, **Code** to read the Angular and Python sources, and **API** for the extracted reference. The checkpointer configuration it relies on is written up in the [Persistence guide](/docs/langgraph/guides/persistence).
This page is the live example for durable execution over LangGraph. Use **Run** to watch one request move through a three-node graph that commits its state after every node, **Code** to read the Angular and Python sources, and **API** for the extracted reference. The checkpointer configuration it relies on is written up in the [Persistence guide](/docs/langgraph/guides/persistence).

## What the demo does

The Run tab shows the prebuilt `<chat>` composition beside a sidebar titled Pipeline, listing three steps: Analyze, Plan, and Generate. Send any question and the sidebar tracks the run. Each step shows the spinner once its own node has committed and the next one is running, and turns green when a later node commits. The graph writes the name of the node it just finished, so the indicator trails execution by one step, and nothing spins while the first node runs. The component reads that field back out of state.

Two things are worth watching. The first is how long each step holds: the graph makes a separate model call per node, so the answer you eventually read is the third of three passes rather than one long generation. The second is where the sidebar stops. The final step stays marked active rather than complete when the run ends, because `generate` is the last name the graph ever writes and there is no fourth step for the indicator to advance to.

Nothing in the demo fails on purpose. What durability buys you here is visible in the shape of the run rather than in a staged crash: three separately committed steps instead of one all-or-nothing call.

## How it is built

Three files carry the feature: a graph that splits one answer into three checkpointed nodes, an application config that registers the agent, and a component that derives the sidebar from graph state. Open the Code tab to read them in place.

### The state the run carries

The state schema is short on purpose. Alongside the message list it keeps a single `step` string, which is the entire progress protocol between the graph and the browser.

<ExampleCode file="graph.py" region="state" title="graph.py — the state" />

Neither field declares a reducer, so whatever a node returns for a key replaces that key outright.

### One node of the pipeline

Each node loads the shared system prompt, appends a line telling the model which phase it is in, calls the model, and returns the conversation with its own response appended plus the name of the step it just completed. `analyze` is the first of the three and `plan` is the same code with a different instruction.

<ExampleCode file="graph.py" region="analyze" title="graph.py — the analyze node" />

The return value is the commit: LangGraph saves the state a node returns before the next node starts, which is what makes the run recoverable partway through.

### The node that ends the run

`generate` is where the shape changes. The first two nodes append to the message list they received, so the intermediate analysis and outline accumulate in state. The last node returns two messages, and because `messages` has no reducer that return replaces the list. Note what it takes as the first of the two: `state["messages"][-1]` is the plan node's response, not the original question, so the thread the reader is left with is the outline re-labeled as a user turn plus the final answer.

<ExampleCode file="graph.py" region="generate" title="graph.py — the generate node" />

The scratch work exists for exactly as long as the run needs it: once `generate` commits, the thread holds those two messages and nothing else.

### Wiring the three nodes

The graph is a straight line. Three nodes, three edges, and an entry point.

<ExampleCode file="graph.py" region="graph" title="graph.py — the graph" />

`compile()` is called with no checkpointer. That is not a gap: the LangGraph API server provides persistence for every thread it serves and either rejects or ignores a graph that brings its own, depending on whether it is `langgraph dev` or a deployment. The [Persistence guide](/docs/langgraph/guides/persistence) covers the checkpointer choices for a graph you run inside your own process.

### The agent provider

`provideAgent()` registers the agent once for the whole application, and `provideChat({})` registers the configuration the `<chat>` composition reads, here left at its defaults. The example passes a factory because it resolves its connection details at runtime from the host that serves the demo.

<ExampleCode file="app.config.ts" />

Your own application does not need the factory. Pass the two values directly:

```typescript
provideAgent({
apiUrl: 'https://your-deployment.langgraph.app',
assistantId: 'durable-execution',
});
```

`assistantId` must match the graph name in `langgraph.json`.

<Callout type="warning" title="Keep the API key on the server">
Never expose a LangSmith API key in client-side code. Point `apiUrl` at a deployment that authenticates the browser another way, or proxy the requests through your own server and attach the key there.
</Callout>

### Deriving the pipeline from state

The component holds the node order and the labels it renders for them. This list is the contract with the graph: the strings are the node names the Python side writes into `state.step`.

<ExampleCode file="durable-execution.component.ts" region="steps-model" title="durable-execution.component.ts — the step model" />

`injectAgent()` returns the agent registered above, and `value()` is the graph state as a signal. The `steps` computed reads `step` out of it, finds that name in the ordered list, and marks everything before it complete, that entry active, and everything after it pending. An unknown or missing `step` leaves every entry pending, which is the state before the first submission, and a second question in the same thread starts from the stale `generate` value, because nothing clears the field.

<ExampleCode file="durable-execution.component.ts" region="agent" title="durable-execution.component.ts — the agent and the derived steps" />

No polling and no subscription: the `step` field arrives with the state updates the run is already streaming, so the sidebar recomputes as each node commits.

### The layout

`<chat>` takes the agent in the main slot and renders the conversation, the input, the loading state, and errors. The sidebar slot is the pipeline, rendered straight from the `steps` signal with a spinner, a check, or a pending circle per entry.

<ExampleCode file="durable-execution.component.ts" region="layout" title="durable-execution.component.ts — the template" />

The `views` and `store` inputs register a generative-UI catalog with the chat composition; this graph emits no view specs, so the catalog goes unused here and the [Generative UI guide](/docs/chat/guides/generative-ui) is where it earns its place.

## What durability covers

Splitting one answer across three nodes is what makes the run durable. Each node's return is committed before the next one starts, so a failure in `generate` does not throw away the analysis and the outline, and the thread you retry against is not empty.

What it does not cover is the request in the browser. A dropped connection, a restarted server, or an error from the model ends the run in the client with an error, and something has to start a new one. That is the UI's job, and it is one call.

### Retrying a failed run

The `<chat>` composition already renders the retry. Its error banner shows a Retry button whenever the error it received is marked retryable — dropped streams and connection failures are — and the button calls `agent.retry()`.

`retry()` clears the error and re-submits the last payload the agent sent, with the same options, against the same thread. Two guards matter when you call it yourself:

- It returns immediately while a run is in flight, so a double click cannot start a second run.
- It does nothing when there is no last payload: before the first submission, and after `switchThread()`, which clears it. A Retry button on a thread the user just switched to is a button that does nothing.

`reload()` performs the same re-submission without clearing the error first. Reach for `retry()` in a UI and `reload()` when you want the previous error to stay on screen while the new attempt runs.

<Callout type="info" title="A retry is a new run, not a rewind">
Re-submitting starts a fresh run against the thread, and the thread still holds everything the failed attempt committed. That is the point — the completed work survives — but it also means a retry does not undo those messages. When a failed attempt leaves state the next one should not see, `regenerate(index)` is the call that rolls back first: it removes everything after the user message that preceded that index — the assistant turn and any tool messages with it — then re-runs against the trimmed thread; it throws rather than no-ops while a run is loading or when the index is not an assistant message.
</Callout>

### submit(null) resumes; it does not retry

`submit(null)` starts a run with no new input. LangGraph advances whatever the thread still has pending, which is why it is the resume call for a graph paused at an `interrupt()` — see the [Interrupts guide](/docs/langgraph/guides/interrupts).

It is not the retry. Once a run has reached the end of the graph the thread has nothing pending, and `submit(null)` does nothing at all: the run completes immediately without executing a node, the thread is unchanged and no error is raised. `regenerate()` is the one path that resumes a finished thread this way, and it can only do so because it first repositions the thread to the entry node — LangGraph's `as_node` parameter, applied through the transport's `updateState` — before submitting null.

<Callout type="warning" title="Guard the retry affordance">
Both `retry()` and `reload()` are silent no-ops when the agent has nothing to re-submit. Render the button from `error()` rather than unconditionally, as `<chat>` does, so the user is never offered a control that cannot do anything.
</Callout>

### Where checkpointing comes from

Nothing in this example configures durability. The graph compiles without a checkpointer and the component asks for no recovery options, because the LangGraph API server checkpoints every thread it runs. Move the same graph into a process you host and the checkpointer becomes yours to choose — an in-memory saver for tests, Postgres for anything that has to survive a restart. The [Persistence guide](/docs/langgraph/guides/persistence) covers the choice and the warning that comes with it: a graph served by `langgraph dev` or LangGraph Platform must not compile a checkpointer of its own.

## What's Next

<CardGroup cols={2}>
<Card title="Persistence" href="/docs/langgraph/guides/persistence">
Choose a checkpointer and resume conversations across page reloads using thread IDs.
</Card>
<Card title="Time Travel" href="/docs/langgraph/guides/time-travel">
Browse the checkpoints a run leaves behind and fork the thread from any of them.
</Card>
<Card title="Interrupts" href="/docs/langgraph/guides/interrupts">
Pause a run for human input and resume it with `submit({ resume })`.
</Card>
</CardGroup>
Loading
Loading