Skip to content

Commit 44fdbf5

Browse files
committed
Merge remote-tracking branch 'origin/main' into blove/angular-22-support
2 parents b7bab9a + d1f77d1 commit 44fdbf5

14 files changed

Lines changed: 735 additions & 374 deletions

apps/website/content/blog/2026-05-17-build-a-streaming-chat-ui-in-angular-with-langgraph.mdx

Lines changed: 101 additions & 39 deletions
Large diffs are not rendered by default.

apps/website/content/blog/2026-05-21-build-fullstack-agentic-angular-apps-using-ag-ui.mdx

Lines changed: 99 additions & 41 deletions
Large diffs are not rendered by default.

apps/website/content/blog/2026-05-28-human-in-the-loop-langgraph-agents-in-angular.mdx

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ This is how to pause a LangGraph agent in Angular for human approval before it r
1111

1212
The example is a refund agent: it drafts a refund, then stops and asks an operator to approve, edit, or cancel before any charge is reversed.
1313

14-
Everything below is running code from the cockpit example at `cockpit/langgraph/interrupts`. Clone the repo, run `nx serve cockpit-langgraph-interrupts-angular`, and follow along.
14+
Everything below is running code from the cockpit example at `cockpit/langgraph/interrupts`.
15+
Clone the repo, run `nx serve cockpit-langgraph-interrupts-angular`, and follow along.
1516

1617
<div style={{ marginTop: 40, marginBottom: 44 }}>
1718
<CardGroup cols={2}>
@@ -32,9 +33,13 @@ Everything below is running code from the cockpit example at `cockpit/langgraph/
3233

3334
## When should you use an interrupt?
3435

35-
Most tool calls don't need approval. Reads, searches, and lookups can run unattended. Reach for an interrupt when a tool does something the operator wouldn't want to undo by hand: moves money, sends a customer-facing message, deletes a record, or triggers a deploy.
36+
Most tool calls do not need approval.
37+
Reads, searches, and lookups can run unattended.
38+
Reach for an interrupt when a tool does something the operator would not want to undo by hand: moves money, sends a customer-facing message, deletes a record, or triggers a deploy.
3639

37-
Two practical reasons: it caps the cost of a misfiring agent looping over a write API, and it gives the operator a checkpoint to catch a wrong action before it lands.
40+
For me, there are two practical reasons.
41+
It caps the cost of a misfiring agent looping over a write API.
42+
And it gives the operator a checkpoint to catch a wrong action before it lands.
3843

3944
## The architecture
4045

@@ -44,7 +49,8 @@ Three pieces:
4449
- **`@threadplane/langgraph` adapter.** Surfaces the pending interrupt on an `agent.interrupt()` signal. `agent.submit({ resume })` writes a value back to the paused graph.
4550
- **`@threadplane/chat` UI.** `<chat-approval-card>` reads the pending interrupt, opens a native `<dialog>` modal, and emits `'approve' | 'edit' | 'cancel'`.
4651

47-
The LangGraph node doesn't know how the UI renders, and the Angular component doesn't know which graph it's paused inside. That lets you reuse one approval dialog across multiple agents.
52+
The LangGraph node does not know how the UI renders, and the Angular component does not know which graph it is paused inside.
53+
That lets you reuse one approval dialog across multiple agents.
4854

4955
<figure>
5056
<img src="/blog/2026-05-28-human-in-the-loop-langgraph-agents-in-angular/1.png" alt="The refund agent's welcome screen with two suggestion chips: 'Refund a duplicate charge' and 'Refund a chargeback.'" width="1280" height="800" />
@@ -58,7 +64,8 @@ Three files.
5864
<Steps>
5965
<Step title="The LangGraph node">
6066

61-
A structured-output call populates the fields the approval card displays. Then `request_approval` pauses with `interrupt()`:
67+
A structured-output call populates the fields the approval card displays.
68+
Then `request_approval` pauses with `interrupt()`:
6269

6370
```python
6471
# graph.py — cockpit/langgraph/interrupts/python/src/graph.py
@@ -100,7 +107,9 @@ def request_approval(state: RefundState) -> dict:
100107
}
101108
```
102109

103-
`interrupt()` pauses the graph and persists its payload. The graph stays paused until `agent.submit({ resume })` runs against the same thread — and that value becomes the return value of `interrupt()` when the node re-executes. That's why `request_approval` can branch on `decision["approved"]` and pick up an edited `amount`.
110+
`interrupt()` pauses the graph and persists its payload.
111+
The graph stays paused until `agent.submit({ resume })` runs against the same thread — and that value becomes the return value of `interrupt()` when the node re-executes.
112+
That is why `request_approval` can branch on `decision["approved"]` and pick up an edited `amount`.
104113

105114
</Step>
106115
<Step title="Wire the providers">
@@ -167,9 +176,13 @@ export class InterruptsComponent {
167176
}
168177
```
169178

170-
`<chat-approval-card>` reads `agent.interrupt()`, matches the `kind` you pass to `matchKind`, opens a native `<dialog>`, and emits an action on each button click. The body is a content-projected template — render whatever fits the payload.
179+
`<chat-approval-card>` reads `agent.interrupt()`, matches the `kind` you pass to `matchKind`, opens a native `<dialog>`, and emits an action on each button click.
180+
The body is a content-projected template — render whatever fits the payload.
171181

172-
Approve and Cancel are terminal: they resolve the interrupt and close the dialog. Edit is not. Clicking Edit leaves the dialog open so you can reveal an inline editor and submit the resume yourself. That distinction lives in the composition.
182+
Approve and Cancel are terminal: they resolve the interrupt and close the dialog.
183+
Edit is not.
184+
Clicking Edit leaves the dialog open so you can reveal an inline editor and submit the resume yourself.
185+
That distinction lives in the composition.
173186

174187
</Step>
175188
</Steps>
@@ -191,7 +204,8 @@ Approve and Cancel are terminal: they resolve the interrupt and close the dialog
191204
8. `request_approval` re-runs; `interrupt()` returns `{ approved: true }` instead of pausing.
192205
9. The graph continues to `issue_refund` and finishes.
193206

194-
It's one thread and one persisted state. If the operator closes the tab and returns later, the interrupt is still pending.
207+
It is one thread and one persisted state.
208+
If the operator closes the tab and returns later, the interrupt is still pending.
195209

196210
<figure>
197211
<img src="/blog/2026-05-28-human-in-the-loop-langgraph-agents-in-angular/3.png" alt="The chat after approval, showing the agent's draft summary and a confirmation: 'Refund of $47.50 issued to cus_a8x2k. Refund ID: re_demo__a8x2k.'" width="1280" height="800" />
@@ -202,7 +216,9 @@ It's one thread and one persisted state. If the operator closes the tab and retu
202216

203217
### Idempotency
204218

205-
`interrupt()` re-executes the node on resume. Side effects before the call have already run; side effects after run again on resume. Put the write (the Stripe `refund.create`) on the resumed side, and pass an idempotency key through state so a double-click doesn't issue two refunds.
219+
`interrupt()` re-executes the node on resume.
220+
Side effects before the call have already run; side effects after run again on resume.
221+
Put the write (the Stripe `refund.create`) on the resumed side, and pass an idempotency key through state so a double-click does not issue two refunds.
206222

207223
### Audit trail
208224

@@ -222,7 +238,8 @@ protected async onAction(action: ChatApprovalAction): Promise<void> {
222238

223239
### What not to interrupt
224240

225-
Interrupt on writes the operator wouldn't want to undo by hand — money movement, customer-facing messages, destructive deletes. If you can undo it with a script in under a minute, let the agent run.
241+
My rule is simple: interrupt on writes the operator would not want to undo by hand — money movement, customer-facing messages, destructive deletes.
242+
If you can undo it with a script in under a minute, let the agent run.
226243

227244
## Next
228245

0 commit comments

Comments
 (0)