diff --git a/apps/website/content/docs/ag-ui/getting-started/installation.mdx b/apps/website/content/docs/ag-ui/getting-started/installation.mdx new file mode 100644 index 000000000..9eec4990b --- /dev/null +++ b/apps/website/content/docs/ag-ui/getting-started/installation.mdx @@ -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` | 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: ``, +}) +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 { + // Your custom transport (WebSocket, in-process worker, etc.) + // emits BaseEvent events. + } +} + +const agent = toAgent(new MyCustomAgent()); +``` diff --git a/apps/website/content/docs/ag-ui/getting-started/introduction.mdx b/apps/website/content/docs/ag-ui/getting-started/introduction.mdx new file mode 100644 index 000000000..c75891d14 --- /dev/null +++ b/apps/website/content/docs/ag-ui/getting-started/introduction.mdx @@ -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. + + +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. + + +## How it fits + +``` +┌─────────────────────────────────────────────────────────────┐ +│ @ngaf/chat (UI primitives — runtime-neutral) │ +│ , , , , … │ +└─────────────────────────┬───────────────────────────────────┘ + │ 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 `` to an AG-UI backend in 5 minutes. +- [Installation](/docs/ag-ui/getting-started/installation) — npm install + provider setup. diff --git a/apps/website/content/docs/ag-ui/getting-started/quickstart.mdx b/apps/website/content/docs/ag-ui/getting-started/quickstart.mdx new file mode 100644 index 000000000..ad2035668 --- /dev/null +++ b/apps/website/content/docs/ag-ui/getting-started/quickstart.mdx @@ -0,0 +1,71 @@ +# Quick Start + +Bind `` from `@ngaf/chat` to an AG-UI backend in 5 minutes. + + +Angular 20+ project with Node.js 22+. If you need setup help, see the [Installation](/docs/ag-ui/getting-started/installation) guide. + + + + + +```bash +npm install @ngaf/chat @ngaf/ag-ui +``` + + + + +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. + + + + +```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: ``, +}) +export class StreamingComponent { + protected readonly agent = inject(AG_UI_AGENT); +} +``` + +That's it. The `` composition handles streaming messages, tool calls, errors, and submit — all bound to the AG-UI backend through the `Agent` contract. + + + + +## What to read next + +- The [`@ngaf/chat` Components](/docs/chat/components/chat) reference covers the primitives the `` 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 +``` diff --git a/apps/website/e2e/website.spec.ts b/apps/website/e2e/website.spec.ts index b0f79690a..d4d20142a 100644 --- a/apps/website/e2e/website.spec.ts +++ b/apps/website/e2e/website.spec.ts @@ -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(); }); @@ -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 }) => { diff --git a/apps/website/src/app/api/email-preview/route.ts b/apps/website/src/app/api/email-preview/route.ts index ca2f5f712..f0a51e98f 100644 --- a/apps/website/src/app/api/email-preview/route.ts +++ b/apps/website/src/app/api/email-preview/route.ts @@ -22,9 +22,9 @@ const TEMPLATES: Record { 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(), }), diff --git a/apps/website/src/app/llms-full.txt/route.ts b/apps/website/src/app/llms-full.txt/route.ts index 08de188b1..a324347b5 100644 --- a/apps/website/src/app/llms-full.txt/route.ts +++ b/apps/website/src/app/llms-full.txt/route.ts @@ -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'), { diff --git a/apps/website/src/app/llms.txt/route.ts b/apps/website/src/app/llms.txt/route.ts index 50b3f1b8f..a0f2a63bf 100644 --- a/apps/website/src/app/llms.txt/route.ts +++ b/apps/website/src/app/llms.txt/route.ts @@ -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 — updates token by token", - '- AgentRef.submit(values): Promise — 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) {

{{ msg.content }}

}', - "// 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: ', + '', + '## 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: ', + '', + '## License', + 'MIT — free for any use, commercial or noncommercial.', '', '## Full reference', 'https://cacheplane.ai/llms-full.txt', diff --git a/apps/website/src/components/landing/HeroTwoCol.tsx b/apps/website/src/components/landing/HeroTwoCol.tsx index 7a8c0d26f..9e9289a12 100644 --- a/apps/website/src/components/landing/HeroTwoCol.tsx +++ b/apps/website/src/components/landing/HeroTwoCol.tsx @@ -2,7 +2,7 @@ import { GenerativeUIFrame } from './GenerativeUIFrame'; import { CopyPromptButton } from '../docs/CopyPromptButton'; import { tokens } from '@ngaf/design-tokens'; -const SETUP_SNIPPET = 'npm install @ngaf/langgraph\n\n// app.config.ts\nprovideAgent({ apiUrl: \'http://localhost:2024\' })'; +const SETUP_SNIPPET = 'npm install @ngaf/chat @ngaf/langgraph\n\n// app.config.ts\nprovideAgent({ apiUrl: \'http://localhost:2024\' })\n\n// or with @ngaf/ag-ui:\nprovideAgUiAgent({ url: \'http://localhost:3000/agent\' })'; function LangChainBadge() { return ( @@ -25,6 +25,27 @@ function LangChainBadge() { ); } +function AgUiBadge() { + return ( + + AG + AG-UI + + ); +} + function AngularBadge() { return (
+
@@ -86,7 +108,7 @@ export async function HeroTwoCol() { margin: 0, marginBottom: 20, }}> - The Enterprise Agent Framework for LangChain and{' '} + The Enterprise Agent Framework for{' '} Angular @@ -100,7 +122,7 @@ export async function HeroTwoCol() { margin: 0, marginBottom: 32, }}> - Signal-native streaming for LangGraph — production patterns your Angular team can own. + Signal-native chat UI for any agent runtime — LangGraph, AG-UI, or your own backend.

@@ -110,7 +132,7 @@ export async function HeroTwoCol() { fontSize: 12, color: tokens.colors.textMuted, }}> - npm install @ngaf/langgraph + npm install @ngaf/chat @ngaf/langgraph
diff --git a/apps/website/src/components/landing/TheStack.tsx b/apps/website/src/components/landing/TheStack.tsx index aacb4a9ec..cffae0d76 100644 --- a/apps/website/src/components/landing/TheStack.tsx +++ b/apps/website/src/components/landing/TheStack.tsx @@ -40,6 +40,18 @@ const LIBRARIES = [ href: '/chat', ctaLabel: 'Explore Chat', }, + { + id: 'ag-ui', + tag: 'AG-UI', + pkg: '@ngaf/ag-ui', + color: tokens.colors.accent, + rgb: '0,64,144', + headline: 'Run any AG-UI compatible backend', + description: "AG-UI is the open agent-to-UI protocol used by CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, and the CopilotKit runtime. The @ngaf/ag-ui adapter wraps an AbstractAgent into the same Agent contract @ngaf/chat consumes — same UI, different runtime.", + pills: ['CrewAI', 'Mastra', 'Microsoft AF', 'CopilotKit'], + href: 'https://github.com/cacheplane/angular-agent-framework/tree/main/libs/ag-ui', + ctaLabel: 'Explore AG-UI', + }, ]; function Connector({ fromRgb, toRgb }: { fromRgb: string; toRgb: string }) { @@ -90,7 +102,7 @@ export function TheStack() { color: tokens.colors.textPrimary, marginBottom: 10, }}> - Three libraries. One architecture.
+ Four libraries. One architecture.
Every layer you need.

- Your LangGraph agent already works. These three libraries ship it. + Whatever your agent runtime — LangGraph, AG-UI, or your own backend — these libraries ship it.

diff --git a/apps/website/src/components/landing/render/RenderStackSiblings.tsx b/apps/website/src/components/landing/render/RenderStackSiblings.tsx index a5d5f5dad..fac88f25e 100644 --- a/apps/website/src/components/landing/render/RenderStackSiblings.tsx +++ b/apps/website/src/components/landing/render/RenderStackSiblings.tsx @@ -22,6 +22,15 @@ const SIBLINGS = [ href: '/chat', ctaLabel: 'Explore Chat', }, + { + tag: 'AG-UI', + pkg: '@ngaf/ag-ui', + color: tokens.colors.accent, + rgb: '0,64,144', + headline: 'Run any AG-UI compatible backend', + href: 'https://github.com/cacheplane/angular-agent-framework/tree/main/libs/ag-ui', + ctaLabel: 'Explore AG-UI', + }, ]; export function RenderStackSiblings() { diff --git a/apps/website/src/lib/docs-config.ts b/apps/website/src/lib/docs-config.ts index ab778e469..7e52925c0 100644 --- a/apps/website/src/lib/docs-config.ts +++ b/apps/website/src/lib/docs-config.ts @@ -1,4 +1,4 @@ -export type LibraryId = 'agent' | 'render' | 'chat'; +export type LibraryId = 'agent' | 'render' | 'chat' | 'ag-ui'; export interface DocsPage { title: string; @@ -180,6 +180,23 @@ export const docsConfig: DocsLibrary[] = [ }, ], }, + { + id: 'ag-ui', + title: 'AG-UI', + description: 'Adapter for any AG-UI-compatible backend (CrewAI, Mastra, Microsoft AF, AG2, Pydantic AI, AWS Strands, CopilotKit runtime)', + sections: [ + { + title: 'Getting Started', + id: 'getting-started', + color: 'blue', + pages: [ + { title: 'Introduction', slug: 'introduction', section: 'getting-started' }, + { title: 'Quick Start', slug: 'quickstart', section: 'getting-started' }, + { title: 'Installation', slug: 'installation', section: 'getting-started' }, + ], + }, + ], + }, ]; export function getLibraryConfig(libraryId: string): DocsLibrary | undefined {