Quick Start
Connect the trip planner UI to a backend that emits AG-UI events. The adapter translates those events into the same Angular Agent contract that <chat> reads; the backend remains responsible for executing its agent and tools.
Angular 20–22 project using a Node.js version supported by that Angular major. If you need setup help, see the Installation guide.
Want to see the finished result before you build?
Try without a backend renders the same <chat> with a fake agent — no server, no key.
npm install @threadplane/chat @threadplane/ag-ui @ag-ui/client @ag-ui/core markedAdd provideAgent() to your application config with your AG-UI backend URL.
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideAgent } from '@threadplane/ag-ui';
export const appConfig: ApplicationConfig = {
providers: [
provideAgent({ url: 'http://localhost:3000/agent' }),
],
};No backend yet? Swap to provideFakeAgent({}) - it serves canned streaming responses so you can build UI offline.
import { Component } from '@angular/core';
import { ChatComponent } from '@threadplane/chat';
import { injectAgent } from '@threadplane/ag-ui';
@Component({
selector: 'app-streaming',
standalone: true,
imports: [ChatComponent],
template: `<chat [agent]="agent" />`,
})
export class StreamingComponent {
protected readonly agent = injectAgent();
}That is all. The <chat> composition handles streaming messages, tool calls, errors, and submit - all bound to the AG-UI backend through the Agent contract.
Follow one request
When the traveler sends a question, the adapter submits the conversation to the configured endpoint. Message events build the answer, tool events describe work, and state events update the observed application data. The terminal event tells the adapter that this run has finished; a message ending alone is not enough to establish that.
- Person → ApplicationAsk for a three-day trip plan.This starts one user turn.
- Application → RuntimeSubmit a message on a confirmed thread.The adapter starts an agent run.
- Runtime → Model / toolsPlan, look up information and continue.One turn can contain several model and tool calls.
- Runtime → Threadplane → UIStream message and state updates.The response is built incrementally; a chunk is not a new turn.
- Runtime → StorageSave state when the backend supports it.LangGraph checkpoints belong to the thread.
- Threadplane → ApplicationShow the final answer and settled status.The next user message starts the next turn.
Read diagram description
Read top to bottom. A person asks for a trip plan. The application submits their message to a confirmed thread. An agent run can invoke a model and tools more than once. Threadplane observes streamed message and state events and the UI updates as they arrive. Persistence depends on the backend: LangGraph can save checkpoints during execution; the AG-UI protocol alone does not provide storage. When the run settles, the UI shows the answer. The next user message starts another turn; a tool result or streamed token does not.
AG-UI defines the event exchange. Durable threads, replay, and approval behavior depend on the backend integration. Read Follow a conversation for the shared vocabulary, then Event mapping for the concrete protocol.
What to read next
- Architecture explains how
toAgent(),provideAgent(), andinjectAgent()fit together. - Event Mapping is the backend compatibility checklist for emitted AG-UI events.
- Fake Agent covers offline demos and frontend tests.
- Troubleshooting is the fastest place to start when a stream does not render.
- The
@threadplane/chatComponents reference covers the primitives the<chat>composition uses internally - handy if you want to compose your own layout. - Looking for LangGraph instead of AG-UI? See
@threadplane/langgraph.
Switching backends without changing UI
The runtime-neutral Agent contract is the payoff: your component never learns which protocol it is talking to. The cost is a thin translation layer per adapter, paid once, after which the UI code stops caring.
So swapping backends is a one-line change in app.config.ts, and the component code stays the same:
- import { provideAgent } from '@threadplane/langgraph';
- providers: [provideAgent({ apiUrl: '...', assistantId: '...' })], // LangGraph
+ import { provideAgent } from '@threadplane/ag-ui';
+ providers: [provideAgent({ url: '...' })], // AG-UIThese are two different provideAgent functions from two different packages — the import source changes along with the config key (apiUrl for LangGraph, url for AG-UI). It is not one symbol that accepts both.