Installation
Prerequisites
- Angular 20 or later
- Node.js 22+
- An AG-UI-compatible backend running locally or remotely (CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit runtime, or your own subclass of
AbstractAgent)
Install packages
npm install @threadplane/chat @threadplane/ag-ui @ag-ui/client @ag-ui/core marked@threadplane/chat provides the chat UI primitives. @threadplane/ag-ui provides the adapter that wires an AG-UI backend into the Agent contract those primitives consume.
marked is the required markdown parser peer used by @threadplane/chat when assistant messages render through <chat>.
Peer Dependencies
@threadplane/ag-ui declares the following peer dependencies:
| Package | Version |
|---|---|
@threadplane/chat | * |
@angular/core | ^20.0.0 || ^21.0.0 |
@ag-ui/client | * |
@ag-ui/core | * |
rxjs | ~7.8.0 |
Configure the provider
In your app config:
import { ApplicationConfig } from '@angular/core';
import { provideAgent } from '@threadplane/ag-ui';
export const appConfig: ApplicationConfig = {
providers: [
provideAgent({
url: 'http://localhost:3000/agent', // your AG-UI backend
}),
],
};provideAgent accepts:
| Option | Type | Description |
|---|---|---|
url | string | Required. AG-UI backend HTTP/SSE endpoint. |
agentId | string | Optional. Identifies a specific agent on the backend. |
threadId | string | Optional. Resume an existing conversation thread. |
headers | Record<string, string> | Optional. Custom request headers (auth, tracing). |
telemetry | AgentRuntimeTelemetrySink | false | Optional. App-owned telemetry sink — opt-in, emits nothing unless supplied. See @threadplane/telemetry. |
Use in a component
import { Component, inject } 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();
}No backend yet?
Use the FakeAgent for offline demos:
import { provideFakeAgent } from '@threadplane/ag-ui';
export const appConfig: ApplicationConfig = {
providers: [
provideFakeAgent({
tokens: ['Hello', ' from', ' a', ' fake', ' agent.'],
reasoningTokens: ['Thinking', ' it', ' over...'],
delayMs: 60,
}),
],
};FakeAgent extends AbstractAgent and emits a canned RUN_STARTED -> TEXT_MESSAGE_START -> TEXT_MESSAGE_CONTENT x N -> TEXT_MESSAGE_END -> RUN_FINISHED sequence. It's a drop-in replacement for provideAgent({ url }) while you're prototyping.
provideFakeAgent accepts:
| Option | Type | Description |
|---|---|---|
tokens | string[] | Optional. Tokens streamed back as the assistant reply. |
reasoningTokens | string[] | Optional. Reasoning chunks emitted before the text reply (defaults to []). |
delayMs | number | Optional. Milliseconds between successive token emissions (defaults to 60). |
Custom transport
If you have a backend that speaks AG-UI but not over HTTP, subclass AbstractAgent directly and feed it to toAgent:
import { AbstractAgent, type RunAgentInput, type BaseEvent } from '@ag-ui/client';
import { Observable } from 'rxjs';
import { toAgent } from '@threadplane/ag-ui';
class MyCustomAgent extends AbstractAgent {
protected run(input: RunAgentInput): Observable<BaseEvent> {
// Your custom transport (WebSocket, in-process worker, etc.)
// emits BaseEvent events.
}
}
const agent = toAgent(new MyCustomAgent());