Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,15 @@ yarn add @threadplane/chat @threadplane/langgraph marked
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideAgent } from '@threadplane/langgraph';
import { provideChat } from '@threadplane/chat';

export const appConfig: ApplicationConfig = {
providers: [
provideAgent({ apiUrl: 'http://localhost:2024' }),
provideChat({ assistantName: 'Assistant' }),
],
};
```

`provideAgent` is the transport. `provideChat` is the UI configuration. They are independent on purpose — you can use one without the other.
`provideAgent` is the transport, and it is the only provider the chat components need. The UI is configured entirely through component inputs, so the two layers stay independent on purpose.

</Step>
<Step title="Create the agent in your component">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,21 +115,19 @@ It is a peer dep so you can swap it.
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideAgent } from '@threadplane/ag-ui';
import { provideChat } from '@threadplane/chat';

export const appConfig: ApplicationConfig = {
providers: [
provideAgent({ url: 'http://localhost:8000/agent' }),
provideChat({ assistantName: 'Astra' }),
],
};
```

That is the whole bootstrap.
`provideAgent` is the AG-UI transport. It wraps the official `@ag-ui/client` `HttpAgent` and exposes the signal-shaped contract via DI.
`provideChat` is the chat UI's configuration.
It is the only provider the chat components need.

Notice they are independent.
Notice how little the two layers know about each other.
`@threadplane/chat` does not know it is talking to an AG-UI backend.
It just reads from the `Agent` contract.
We will lean on that boundary later.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,10 @@ That is why `request_approval` can branch on `decision["approved"]` and pick up
```ts
// app.config.ts
import { provideAgent } from '@threadplane/langgraph';
import { provideChat } from '@threadplane/chat';

export const appConfig: ApplicationConfig = {
providers: [
provideAgent({ apiUrl: environment.langGraphApiUrl }),
provideChat({}),
],
};
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,10 @@ Two details worth knowing:
// app.config.ts — cockpit/ag-ui/interrupts/angular/src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideAgent } from '@threadplane/ag-ui';
import { provideChat } from '@threadplane/chat';

export const appConfig: ApplicationConfig = {
providers: [
provideAgent({ url: '/agent' }),
provideChat({}),
],
};
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,14 +230,12 @@ Wire both packages into `app.config.ts`:
```ts
import { ApplicationConfig } from '@angular/core';
import { provideAgent } from '@threadplane/ag-ui';
import { provideChat } from '@threadplane/chat';

export const appConfig: ApplicationConfig = {
providers: [
provideAgent({
url: 'http://localhost:8080/invocations',
}),
provideChat({ assistantName: 'Strands Assistant' }),
],
};
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,12 @@ npm install @threadplane/chat @threadplane/ag-ui @ag-ui/client @ag-ui/core marke
The provider is one line, because AG-UI's connection surface is one URL:

```ts
import { provideChat } from '@threadplane/chat';
import { provideAgent } from '@threadplane/ag-ui';

export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideAgent({ url: 'http://localhost:8000/agent' }),
provideChat({ assistantName: 'Librarian' }),
],
};
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ import {
signal,
} from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideChat } from '@threadplane/chat';
import { LANGGRAPH_THREADS_CONFIG, provideAgent } from '@threadplane/langgraph';

import { routes } from './app.routes';
Expand All @@ -195,7 +194,6 @@ export const appConfig: ApplicationConfig = {
onThreadId: (id) => ACTIVE_THREAD.set(id),
}),
{ provide: LANGGRAPH_THREADS_CONFIG, useValue: { apiUrl: API_URL } },
provideChat({ assistantName: 'Assistant' }),
],
};
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ Nothing in this file is specific to A2UI; the surfaces travel as assistant messa

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

`provideChat({})` registers the chat composition defaults alongside it.

### Giving the chat composition a catalog

Expand Down
2 changes: 1 addition & 1 deletion apps/website/content/docs/ag-ui/guides/client-tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The server is a FastAPI application. `LangGraphAgent` wraps the compiled graph a

### Providing the agent

`provideAgent()` from `@threadplane/ag-ui` registers the agent at the application root, and `provideChat({})` registers the chat defaults. The example resolves its URL at runtime because the host that serves the demo decides which runtime is attached; your own application passes a `url` directly.
`provideAgent()` from `@threadplane/ag-ui` registers the agent at the application root, and it is the only provider the `<chat>` composition requires. The example resolves its URL at runtime because the host that serves the demo decides which runtime is attached; your own application passes a `url` directly.

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

Expand Down
2 changes: 1 addition & 1 deletion apps/website/content/docs/ag-ui/guides/interrupts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ That wrapper is what turns a LangGraph pause into the AG-UI event described belo

### 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 endpoint at runtime from the host that serves the demo.
`provideAgent()` registers the agent once for the whole application, and it is the only provider the `<chat>` composition requires. The example passes a factory because it resolves its endpoint at runtime from the host that serves the demo.

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

Expand Down
1 change: 0 additions & 1 deletion apps/website/content/docs/ag-ui/guides/json-render.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ Nothing in this file is specific to generative UI.

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

`provideChat({})` registers the chat composition defaults alongside it.

### The view registry and the shared store

Expand Down
2 changes: 1 addition & 1 deletion apps/website/content/docs/ag-ui/guides/subagents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ The AG-UI encoder serializes pydantic `ag_ui.core` event classes, so the subclas

### 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. Nothing in either provider is subagent-specific. The example passes a factory because it resolves its endpoint at runtime from the host that serves the demo. Your own application does not need the factory.
`provideAgent()` registers the agent once for the whole application, and it is the only provider the `<chat>` composition requires. Nothing about it is subagent-specific. The example passes a factory because it resolves its endpoint at runtime from the host that serves the demo. Your own application does not need the factory.

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

Expand Down
2 changes: 1 addition & 1 deletion apps/website/content/docs/ag-ui/guides/tool-views.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ The server is a FastAPI application. `add_langgraph_fastapi_endpoint` mounts the

### Providing the agent

`provideAgent()` from `@threadplane/ag-ui` registers the agent at the application root, and `provideChat({})` registers the chat defaults. The example resolves its URL at runtime because the host that serves the demo decides which runtime is attached; your own application passes a `url` directly.
`provideAgent()` from `@threadplane/ag-ui` registers the agent at the application root, and it is the only provider the `<chat>` composition requires. The example resolves its URL at runtime because the host that serves the demo decides which runtime is attached; your own application passes a `url` directly.

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ That wrapper is the piece that translates LangGraph's own stream into the protoc

### Providing the agent

`provideAgent()` from `@threadplane/ag-ui` registers the agent once for the whole application, and `provideChat({})` registers the chat configuration, here left at its defaults. Your own application passes `{ url: 'https://your-backend.example.com/agent' }` directly; this example passes a factory because it resolves its endpoint at runtime from the host that serves the demo.
`provideAgent()` from `@threadplane/ag-ui` registers the agent once for the whole application, and it is the only provider the `<chat>` composition requires. Your own application passes `{ url: 'https://your-backend.example.com/agent' }` directly; this example passes a factory because it resolves its endpoint at runtime from the host that serves the demo.

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

Expand Down
1 change: 0 additions & 1 deletion apps/website/content/docs/chat/a2ui/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ The graph is served by the LangGraph API server, which supplies persistence, so

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

`provideChat({})` registers the chat composition defaults alongside it.

### Giving the chat composition a catalog

Expand Down
76 changes: 11 additions & 65 deletions apps/website/content/docs/chat/api/api-docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1925,7 +1925,7 @@
},
{
"name": "messageContent",
"type": "(message: BaseMessage<>) => string",
"type": "(message: object) => string",
"description": "",
"optional": false
},
Expand Down Expand Up @@ -2072,12 +2072,12 @@
},
{
"name": "humanContent",
"signature": "humanContent(message: unknown): string",
"signature": "humanContent(message: object): string",
"description": "Renderable content for a human-role message bubble. Most human\nmessages are typed prompts and pass through `messageContent`\nunchanged. A2UI action messages (e.g. form submits, button clicks\non a rendered surface) flow through the same submit channel and\nland in the message stream as a HumanMessage whose content is a\nJSON-serialized `A2uiActionMessage`. Showing the raw JSON as if\nthe user typed it leaks the protocol; per the A2UI spec\nthose events resemble tool calls more than user utterances.\n\n`a2uiActionLabel` returns a short human-readable label for\nrecognized action shapes (\"Search flights\", \"Selected flight UA123\",\netc.) — or null for any non-action content, in which case we fall\nback to the original text.",
"params": [
{
"name": "message",
"type": "unknown",
"type": "object",
"description": "",
"optional": false
}
Expand Down Expand Up @@ -4002,7 +4002,7 @@
{
"name": "resolvedRegistry",
"type": "Signal<Readonly<Record<string, Type<unknown> | RenderViewEntry>>>",
"description": "",
"description": "Most specific wins: the `[viewRegistry]` input, then a registry provided by\nan ancestor injector, then the built-in markdown views.",
"optional": false
},
{
Expand Down Expand Up @@ -6467,32 +6467,6 @@
],
"examples": []
},
{
"name": "ChatConfig",
"kind": "interface",
"description": "Application-wide options for provideChat. Every field is optional;\nthe values are exposed to all chat components in the tree via the\n`CHAT_CONFIG` injection token, so you set them once at bootstrap instead of\nthreading props through every component.",
"properties": [
{
"name": "assistantName",
"type": "string",
"description": "Shared assistant display name for consumers that read CHAT_CONFIG (default: \"Assistant\").",
"optional": true
},
{
"name": "avatarLabel",
"type": "string",
"description": "Shared AI avatar label for consumers that read CHAT_CONFIG (default: \"A\").",
"optional": true
},
{
"name": "renderRegistry",
"type": "AngularRegistry",
"description": "Shared render registry for consumers that read CHAT_CONFIG.",
"optional": true
}
],
"examples": []
},
{
"name": "ChatLifecycle",
"kind": "interface",
Expand Down Expand Up @@ -8468,7 +8442,7 @@
"name": "ContentType",
"kind": "type",
"description": "",
"signature": "\"pending\" | \"markdown\" | \"json-render\" | \"a2ui\" | \"mixed\"",
"signature": "\"pending\" | \"markdown\" | \"json-render\" | \"a2ui\"",
"examples": []
},
{
Expand Down Expand Up @@ -8590,13 +8564,6 @@
"signature": "ViewRegistry",
"examples": []
},
{
"name": "CHAT_CONFIG",
"kind": "const",
"description": "",
"signature": "InjectionToken<ChatConfig>",
"examples": []
},
{
"name": "CHAT_LIFECYCLE",
"kind": "const",
Expand All @@ -8614,7 +8581,7 @@
{
"name": "MARKDOWN_VIEW_REGISTRY",
"kind": "const",
"description": "DI token for the markdown view registry consumed by <chat-streaming-md>\nand <md-children>. Maps MarkdownNode.type strings (e.g. \"paragraph\",\n\"heading\") to Angular components that render that node type.\n\n`<chat-streaming-md>` provides the runtime registry on its component-level\ninjector — either the consumer-supplied [viewRegistry] input, or\n`cacheplaneMarkdownViews` (the default) — so descendant <md-children>\ncomponents resolve the right components for each node.",
"description": "DI token for the markdown view registry consumed by <chat-streaming-md>\nand <md-children>. Maps MarkdownNode.type strings (e.g. \"paragraph\",\n\"heading\") to Angular components that render that node type.\n\n`<chat-streaming-md>` provides the resolved registry on its component-level\ninjector so descendant <md-children> components resolve the right component\nfor each node. It resolves most-specific-first: the `[viewRegistry]` input,\nthen a registry provided by an ancestor injector (application root or route),\nthen `cacheplaneMarkdownViews` (the default). Providing this token at the\napplication root is therefore a supported app-wide override.",
"signature": "InjectionToken<Readonly<Record<string, Type<unknown> | RenderViewEntry>>>",
"examples": []
},
Expand Down Expand Up @@ -9418,13 +9385,13 @@
{
"name": "messageContent",
"kind": "function",
"description": "Extracts a human-readable string from a message's content.\n\n`BaseMessage.content` is `string | MessageContentComplex[]`. Reasoning-\ncapable models (OpenAI gpt-5/o-series, Anthropic) emit complex arrays of\ntyped blocks: `{type:'text',text}`, `{type:'reasoning',...}`, tool-use\nblocks, etc. We render only the visible text portions and skip anything\nelse. Stringifying the whole array would dump raw JSON like\n`[{\"type\":\"text\",...}]` into the chat bubble.",
"signature": "messageContent(message: BaseMessage<>): string",
"description": "Extracts a human-readable string from a message's content.\n\nMessage content is either a plain string or an array of typed blocks.\nReasoning-capable models (OpenAI gpt-5/o-series, Anthropic) emit complex\narrays: `{type:'text',text}`, `{type:'reasoning',...}`, tool-use blocks, etc.\nOnly the visible text portions are rendered and anything else is skipped.\nStringifying the whole array would dump raw JSON like `[{\"type\":\"text\",...}]`\ninto the chat bubble.\n\nThe parameter is structural on purpose. This function reads nothing but\n`.content`, and callers hold either the runtime-neutral `Message` from\n`agent.messages()` or a LangChain `BaseMessage` depending on where the\nmessage came from. Both satisfy `{ content: unknown }`, so neither has to\ncast.",
"signature": "messageContent(message: object): string",
"params": [
{
"name": "message",
"type": "BaseMessage<>",
"description": "",
"type": "object",
"description": "Any object carrying a `content` field.",
"optional": false
}
],
Expand Down Expand Up @@ -9531,27 +9498,6 @@
},
"examples": []
},
{
"name": "provideChat",
"kind": "function",
"description": "Bootstrap `@threadplane/chat` in an Angular application or standalone\ncomponent tree.\n\nCall this once inside `bootstrapApplication` (or the `providers` array of a\nroot `ApplicationConfig`). It registers the shared ChatConfig token\nso every chat component in the tree can read the render registry, avatar\nlabel, and assistant display name without explicit prop threading.",
"signature": "provideChat(config: ChatConfig): EnvironmentProviders",
"params": [
{
"name": "config",
"type": "ChatConfig",
"description": "Options bag that controls the chat feature set:\n - `renderRegistry` — shared AngularRegistry wiring tool-view\n components to their names; pass the value returned by\n `defineAngularRegistry` from `\\@threadplane/render`.\n - `avatarLabel` — short label shown in the AI avatar bubble (default `\"A\"`).\n - `assistantName` — display name shown above assistant messages\n (default `\"Assistant\"`).",
"optional": false
}
],
"returns": {
"type": "EnvironmentProviders",
"description": ""
},
"examples": [
"```ts\n// main.ts\nimport { bootstrapApplication } from '@angular/platform-browser';\nimport { provideChat } from '@threadplane/chat';\nimport { defineAngularRegistry, provideRender } from '@threadplane/render';\nimport { DayCardComponent } from './day-card.component';\n\nconst registry = defineAngularRegistry({ day_card: DayCardComponent });\n\nbootstrapApplication(AppComponent, {\n providers: [\n provideChat({ renderRegistry: registry, avatarLabel: 'AI' }),\n provideRender({ registry }),\n ],\n});\n```"
]
},
{
"name": "renderMarkdown",
"kind": "function",
Expand Down Expand Up @@ -9771,7 +9717,7 @@
{
"name": "tools",
"kind": "function",
"description": "Collect named client tools into a frozen, name-keyed registry.\n\nThe overload is generic over the entire map (`const M`) so that each tool's\nprecise type (FunctionToolDef`<S,R>`, ViewToolDef`<S,C>`, or\nAskToolDef`<S,C>`) and every literal key are preserved in the\nClientToolRegistry passed to `provideChat`. This lets downstream\nconsumers look up individual tools without losing generic information.",
"description": "Collect named client tools into a frozen, name-keyed registry.\n\nThe overload is generic over the entire map (`const M`) so that each tool's\nprecise type (FunctionToolDef`<S,R>`, ViewToolDef`<S,C>`, or\nAskToolDef`<S,C>`) and every literal key are preserved in the\nClientToolRegistry passed to the `clientTools` input. This lets downstream\nconsumers look up individual tools without losing generic information.",
"signature": "tools(map: M): Readonly<M>",
"params": [
{
Expand Down
Loading
Loading