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
97 changes: 97 additions & 0 deletions apps/website/content/docs/ag-ui/getting-started/installation.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 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

```bash
npm install @ngaf/chat @ngaf/ag-ui
```

`@ngaf/chat` provides the chat UI primitives. `@ngaf/ag-ui` provides the adapter that wires an AG-UI backend into the `Agent` contract those primitives consume.

## Configure the provider

In your app config:

```ts
import { ApplicationConfig } from '@angular/core';
import { provideAgUiAgent } from '@ngaf/ag-ui';

export const appConfig: ApplicationConfig = {
providers: [
provideAgUiAgent({
url: 'http://localhost:3000/agent', // your AG-UI backend
}),
],
};
```

`provideAgUiAgent` 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). |

## Use in a component

```ts
import { Component, inject } from '@angular/core';
import { ChatComponent } from '@ngaf/chat';
import { AG_UI_AGENT } from '@ngaf/ag-ui';

@Component({
selector: 'app-streaming',
standalone: true,
imports: [ChatComponent],
template: `<chat [agent]="agent" />`,
})
export class StreamingComponent {
protected readonly agent = inject(AG_UI_AGENT);
}
```

## No backend yet?

Use the `FakeAgent` for offline demos:

```ts
import { provideFakeAgUiAgent } from '@ngaf/ag-ui';

export const appConfig: ApplicationConfig = {
providers: [
provideFakeAgUiAgent({
tokens: ['Hello', ' from', ' a', ' fake', ' agent.'],
delayMs: 60,
}),
],
};
```

`FakeAgent` extends `AbstractAgent` and emits a canned `RUN_STARTED → TEXT_MESSAGE_START → TEXT_MESSAGE_CONTENT × N → TEXT_MESSAGE_END → RUN_FINISHED` sequence. Drop-in replacement for `provideAgUiAgent({ url })` while you're prototyping.

## Custom transport

If you have a backend that speaks AG-UI but not over HTTP, subclass `AbstractAgent` directly and feed it to `toAgent`:

```ts
import { AbstractAgent, type RunAgentInput, type BaseEvent } from '@ag-ui/client';
import { Observable } from 'rxjs';
import { toAgent } from '@ngaf/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());
```
52 changes: 52 additions & 0 deletions apps/website/content/docs/ag-ui/getting-started/introduction.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Introduction

`@ngaf/ag-ui` is the runtime adapter that wraps an [AG-UI](https://github.com/ag-ui-protocol/ag-ui) `AbstractAgent` into the runtime-neutral `Agent` contract from `@ngaf/chat`. The chat UI primitives consume the Agent contract; the AG-UI adapter translates between the contract and the AG-UI event protocol.

<Callout type="info" title="What is AG-UI?">
AG-UI is the open agent-to-UI protocol from the CopilotKit ecosystem. It standardizes how agent runtimes stream events (messages, tool calls, state updates) to a frontend. Used by **CrewAI**, **Mastra**, **Microsoft Agent Framework**, **AG2**, **Pydantic AI**, **AWS Strands**, and the **CopilotKit runtime**. One adapter unlocks all of them.
</Callout>

## How it fits

```
┌─────────────────────────────────────────────────────────────┐
│ @ngaf/chat (UI primitives — runtime-neutral) │
│ <chat>, <chat-messages>, <chat-input>, <chat-debug>, … │
└─────────────────────────┬───────────────────────────────────┘
│ Agent contract (signals + events$)
┌───────────┴───────────┐
▼ ▼
@ngaf/langgraph @ngaf/ag-ui
(toAgent: AgentRef→Agent) (toAgent: AbstractAgent→Agent)
│ │
▼ ▼
LangGraph Platform Any AG-UI backend
(CrewAI, Mastra, MS AF,
CopilotKit runtime, …)
```

## What you get

- **`toAgent(source: AbstractAgent): Agent`** — wraps any `AbstractAgent` subclass (custom transports, mocks).
- **`provideAgUiAgent({ url })`** — DI convenience that instantiates `HttpAgent` under the hood for the common SSE/HTTP case.
- **`FakeAgent`** — in-process `AbstractAgent` subclass that emits canned streaming events for offline demos and tests.

## What's covered

Scope of the first release:
- `messages` (streaming token deltas via `TEXT_MESSAGE_*` events)
- `status` / `isLoading` / `error` (lifecycle via `RUN_STARTED/FINISHED/ERROR`)
- `toolCalls` (streaming tool calls via `TOOL_CALL_*` events)
- `state` (snapshots and JSON-Patch deltas)
- `events$` (custom events; discriminates `state_update`)

Out of scope for now (use `@ngaf/langgraph` if you need these):
- Interrupts
- Subagents
- History / time-travel

## Next steps

- [Quick Start](/docs/ag-ui/getting-started/quickstart) — bind `<chat>` to an AG-UI backend in 5 minutes.
- [Installation](/docs/ag-ui/getting-started/installation) — npm install + provider setup.
71 changes: 71 additions & 0 deletions apps/website/content/docs/ag-ui/getting-started/quickstart.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Quick Start

Bind `<chat>` from `@ngaf/chat` to an AG-UI backend in 5 minutes.

<Callout type="info" title="Prerequisites">
Angular 20+ project with Node.js 22+. If you need setup help, see the [Installation](/docs/ag-ui/getting-started/installation) guide.
</Callout>

<Steps>
<Step title="Install the packages">

```bash
npm install @ngaf/chat @ngaf/ag-ui
```

</Step>
<Step title="Configure the provider">

Add `provideAgUiAgent()` to your application config with your AG-UI backend URL.

```ts
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideAgUiAgent } from '@ngaf/ag-ui';

export const appConfig: ApplicationConfig = {
providers: [
provideAgUiAgent({ url: 'http://localhost:3000/agent' }),
],
};
```

For offline development without a backend, swap to `provideFakeAgUiAgent({})` — it serves canned streaming responses for UI work.

</Step>
<Step title="Use in a component">

```ts
import { Component, inject } from '@angular/core';
import { ChatComponent } from '@ngaf/chat';
import { AG_UI_AGENT } from '@ngaf/ag-ui';

@Component({
selector: 'app-streaming',
standalone: true,
imports: [ChatComponent],
template: `<chat [agent]="agent" />`,
})
export class StreamingComponent {
protected readonly agent = inject(AG_UI_AGENT);
}
```

That's it. The `<chat>` composition handles streaming messages, tool calls, errors, and submit — all bound to the AG-UI backend through the `Agent` contract.

</Step>
</Steps>

## What to read next

- The [`@ngaf/chat` Components](/docs/chat/components/chat) 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 [`@ngaf/langgraph`](/docs/agent/getting-started/quickstart).

## Switching backends without changing UI

The point of the runtime-neutral `Agent` contract is that swapping backends is a one-line change in `app.config.ts`. The component code stays the same:

```diff
- providers: [provideAgent({ apiUrl: '...' })], // LangGraph
+ providers: [provideAgUiAgent({ url: '...' })], // AG-UI
```
9 changes: 4 additions & 5 deletions apps/website/e2e/website.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@ test('landing page renders architecture section', async ({ page }) => {

test('landing page renders libraries section', async ({ page }) => {
await page.goto('/');
await expect(page.getByText('Three libraries. One architecture.').first()).toBeVisible();
await expect(page.getByText('Four libraries. One architecture.').first()).toBeVisible();
});

test('pricing page shows 4 plan cards', async ({ page }) => {
test('pricing page shows plan cards', async ({ page }) => {
await page.goto('/pricing');
await expect(page.getByText('Community').first()).toBeVisible();
await expect(page.getByText('Developer Seat').first()).toBeVisible();
await expect(page.getByText('App Deployment').first()).toBeVisible();
await expect(page.getByText('Open Source').first()).toBeVisible();
await expect(page.getByText('Enterprise').first()).toBeVisible();
});

Expand All @@ -42,6 +40,7 @@ test('docs landing page shows library cards', async ({ page }) => {
await expect(page.getByText('Agent').first()).toBeVisible();
await expect(page.getByText('Render').first()).toBeVisible();
await expect(page.getByText('Chat').first()).toBeVisible();
await expect(page.getByText('AG-UI').first()).toBeVisible();
});

test('api reference renders in docs', async ({ page }) => {
Expand Down
6 changes: 3 additions & 3 deletions apps/website/src/app/api/email-preview/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ const TEMPLATES: Record<string, () => { subject: string; html: string }> = {
'lead-notification': () => ({
subject: 'New lead: Brian at Cacheplane',
html: leadNotificationHtml({
name: 'Brian Love',
email: 'brian@cacheplane.io',
company: 'Cacheplane',
name: 'Sample Lead',
email: 'demo@example.com',
company: 'Example Corp',
message: 'Interested in the pilot program for our Angular + LangGraph project.',
ts: new Date().toISOString(),
}),
Expand Down
6 changes: 0 additions & 6 deletions apps/website/src/app/llms-full.txt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ export async function GET() {
'Do not mock agent() in tests — use MockAgentTransport.',
'RxJS is an internal implementation detail — do not import rxjs in consumer code.',
].join('\n'),
[
'## MCP server',
'',
'npx @ngaf/langgraph-mcp',
'Add to Claude Code settings.json, Cursor .cursor/mcp.json, or any MCP-compatible agent.',
].join('\n'),
];

return new NextResponse(sections.join('\n\n---\n\n'), {
Expand Down
62 changes: 40 additions & 22 deletions apps/website/src/app/llms.txt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,51 @@ import { NextResponse } from 'next/server';

// Build compact LLMs summary at request time so version is always current
function buildLlmsTxt(): string {
// Inline version — updated by publish workflow
const version = '0.1.0';
// Inline version — bump on each release
const version = '0.0.1';
return [
`# Angular Agent Framework v${version}`,
'',
"Angular Agent Framework — the enterprise streaming library for LangChain/LangGraph. Provides agent() — full parity with React's useStream() hook, built on Angular Signals.",
"Angular Agent Framework is a runtime-neutral chat UI SDK for Angular. The @ngaf/chat library provides streaming chat primitives bound to a runtime-neutral 'Agent' contract; runtime adapters (@ngaf/langgraph, @ngaf/ag-ui) translate between the contract and the actual backend.",
'',
'## Packages',
'- @ngaf/chat — chat UI primitives (messages, input, tool calls, interrupt, debug, etc.) consuming the Agent contract',
'- @ngaf/langgraph — adapter for LangGraph / LangGraph Platform',
'- @ngaf/ag-ui — adapter for any AG-UI-compatible backend (CrewAI, Mastra, Microsoft AF, AG2, Pydantic AI, AWS Strands, CopilotKit runtime)',
'- @ngaf/render — generative UI runtime (Vercel json-render + Google A2UI)',
'- @ngaf/a2ui — A2UI catalog components',
'- @ngaf/partial-json — streaming JSON parser',
'- @ngaf/licensing — license verification client',
'',
'## Install',
'npm install @ngaf/langgraph',
'',
'## Key API',
'- agent(options): AgentRef — call in Angular injection context (constructor or field initializer)',
'- provideAgent(config): Provider — register in app.config.ts for global defaults',
"- AgentRef.messages(): Signal<BaseMessage[]> — updates token by token",
'- AgentRef.submit(values): Promise<void> — send a message / trigger a run',
"- AgentRef.status(): Signal<'idle'|'loading'|'resolved'|'error'>",
'- AgentRef.threadId signal + onThreadId callback — thread persistence across refreshes',
'- MockAgentTransport — deterministic unit testing without a real server',
'',
'## Minimal example',
"import { agent } from '@ngaf/langgraph';",
"const chat = agent({ assistantId: 'chat_agent', apiUrl: 'http://localhost:2024' });",
'// Template: @for (msg of chat.messages(); track $index) { <p>{{ msg.content }}</p> }',
"// Submit: chat.submit({ messages: [{ role: 'human', content: input }] })",
'',
'## MCP server',
'npx @ngaf/langgraph-mcp',
'# LangGraph backend:',
'npm install @ngaf/chat @ngaf/langgraph',
'# AG-UI backend:',
'npm install @ngaf/chat @ngaf/ag-ui',
'',
'## Key API (chat library)',
'- Agent — runtime-neutral contract with messages/status/isLoading/error/toolCalls/state signals + events$ observable + submit/stop methods',
'- ChatComponent, ChatMessagesComponent, ChatInputComponent — composable Angular components consuming Agent',
'- mockAgent — testing utility with a writable signal-backed Agent',
'- runAgentConformance / runAgentWithHistoryConformance — conformance suites for adapter authors',
'',
'## Minimal LangGraph example',
"import { agent, toAgent } from '@ngaf/langgraph';",
"import { ChatComponent } from '@ngaf/chat';",
'// In a component:',
"stream = agent({ apiUrl: 'http://localhost:2024', assistantId: 'chat_agent' });",
'chatAgent = toAgent(this.stream);',
'// Template: <chat [agent]="chatAgent" />',
'',
'## Minimal AG-UI example',
"import { provideAgUiAgent, AG_UI_AGENT } from '@ngaf/ag-ui';",
"import { ChatComponent } from '@ngaf/chat';",
"// app.config.ts: providers: [provideAgUiAgent({ url: 'https://your.endpoint' })]",
"// Component: agent = inject(AG_UI_AGENT);",
'// Template: <chat [agent]="agent" />',
'',
'## License',
'MIT — free for any use, commercial or noncommercial.',
'',
'## Full reference',
'https://cacheplane.ai/llms-full.txt',
Expand Down
Loading
Loading