diff --git a/apps/cockpit/src/app/layout.tsx b/apps/cockpit/src/app/layout.tsx index 58ed30a4..e09ddaca 100644 --- a/apps/cockpit/src/app/layout.tsx +++ b/apps/cockpit/src/app/layout.tsx @@ -3,8 +3,19 @@ import { cssVars } from '@ngaf/ui-react'; import './cockpit.css'; export const metadata = { - title: 'Cockpit', - description: 'Integrated cockpit for manifest-driven developer reference demos.', + title: 'Cockpit — Angular Agent Framework', + description: 'The live reference app for the Angular Agent Framework. Real LangGraph + AG-UI agents through the Angular surface you’ll ship.', + openGraph: { + title: 'Cockpit — Angular Agent Framework', + description: 'The live reference app for the framework. Real LangGraph + AG-UI agents through the same Angular surface you’ll ship.', + type: 'website', + siteName: 'Cockpit', + }, + twitter: { + card: 'summary_large_image', + title: 'Cockpit — Angular Agent Framework', + description: 'The live reference app for the framework. Real LangGraph + AG-UI agents through the Angular surface you’ll ship.', + }, }; interface RootLayoutProps { diff --git a/apps/cockpit/src/app/opengraph-image.tsx b/apps/cockpit/src/app/opengraph-image.tsx new file mode 100644 index 00000000..6e398551 --- /dev/null +++ b/apps/cockpit/src/app/opengraph-image.tsx @@ -0,0 +1,168 @@ +/** + * Default OpenGraph + Twitter share card for the cockpit reference app. + * + * Renders a 1200×630 PNG at request time via Next.js ImageResponse. + * Per-route overrides can be added by dropping an `opengraph-image.tsx` + * file in any route folder (e.g. per-product or per-topic cards). + * + * Cockpit's chrome is Linear-style devtools (Phase 8 spec) — Inter Bold + * for the headline rather than the marketing site's EB Garamond, so we + * don't need to bundle a serif TTF. + */ +import { ImageResponse } from 'next/og'; + +export const runtime = 'edge'; +export const alt = 'Cockpit — the live reference app for the Angular Agent Framework'; +export const size = { width: 1200, height: 630 }; +export const contentType = 'image/png'; + +async function loadFont(family: string, weight: number): Promise { + try { + const css = await fetch( + `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family)}:wght@${weight}&display=swap`, + { headers: { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36' } }, + ).then((res) => res.text()); + const match = css.match(/src:\s*url\((https?:\/\/[^)]+)\)/); + if (!match) return null; + const fontRes = await fetch(match[1]); + if (!fontRes.ok) return null; + return await fontRes.arrayBuffer(); + } catch { + return null; + } +} + +export default async function OpenGraphImage() { + const [interRegular, interBold, monoBold] = await Promise.all([ + loadFont('Inter', 400), + loadFont('Inter', 600), + loadFont('JetBrains+Mono', 700), + ]); + const fonts = [ + interRegular && { name: 'Inter', data: interRegular, weight: 400 as const, style: 'normal' as const }, + interBold && { name: 'Inter', data: interBold, weight: 700 as const, style: 'normal' as const }, + monoBold && { name: 'JetBrains Mono', data: monoBold, weight: 700 as const, style: 'normal' as const }, + ].filter((f): f is NonNullable => f !== null); + + return new ImageResponse( + ( +
+ {/* Eyebrow */} +
+ Cockpit · Angular Agent Framework +
+ + {/* Headline — Inter Bold (cockpit chrome is sans-serif Linear-style) */} +
+ The live reference app for the framework. +
+ + {/* Subhead */} +
+ Real LangGraph and AG-UI agents running through the same Angular surface you'll ship. + Switch between Run · Code · Docs · API for each capability. +
+ + {/* Mode pills + cockpit wordmark */} +
+
+ {['Run', 'Code', 'Docs', 'API'].map((mode, i) => ( + {mode} + ))} +
+
+ 🛩️ + cockpit.cacheplane.ai +
+
+
+ ), + { + ...size, + fonts, + }, + ); +} + +interface ModePillProps { + active: boolean; + children: React.ReactNode; +} + +/** Mimics the cockpit mode-switcher: rounded pill, accent on the active one. */ +function ModePill({ active, children }: ModePillProps) { + return ( +
+ {children} +
+ ); +} diff --git a/libs/ag-ui/README.md b/libs/ag-ui/README.md index a3f20431..7b888115 100644 --- a/libs/ag-ui/README.md +++ b/libs/ag-ui/README.md @@ -1,6 +1,16 @@ # @ngaf/ag-ui -Adapter that wraps an [AG-UI](https://github.com/ag-ui-protocol/ag-ui) `AbstractAgent` into the runtime-neutral `Agent` contract from `@ngaf/chat`. +Adapter that wraps an [AG-UI](https://github.com/ag-ui-protocol/ag-ui) `AbstractAgent` into the runtime-neutral `Agent` contract from `@ngaf/chat`. Works with any AG-UI-compatible backend — LangGraph, CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit runtime. + +Part of the [Angular Agent Framework](https://github.com/cacheplane/angular-agent-framework). MIT licensed. + +## Install + +```bash +npm install @ngaf/ag-ui @ngaf/chat @ag-ui/client +``` + +## Quick start ```ts import { provideAgUiAgent, AG_UI_AGENT } from '@ngaf/ag-ui'; @@ -46,3 +56,13 @@ The `bridgeCitationsState()` function populates `Message.citations` from AG-UI S ``` Each citation object in the array supports `id`, `index`, `title`, `url`, `snippet`, and custom `extra` fields. The messageId key matches the corresponding message in the chat history. + +## Documentation + +- [Quickstart](https://cacheplane.ai/docs/agent/getting-started/quickstart) +- [AG-UI adapter guide](https://cacheplane.ai/docs/chat/guides/writing-an-adapter) +- [AG-UI protocol](https://github.com/ag-ui-protocol/ag-ui) + +## License + +MIT — free for any use. See [LICENSE](../../LICENSE). diff --git a/libs/langgraph/README.md b/libs/langgraph/README.md index a2c859a3..1991397f 100644 --- a/libs/langgraph/README.md +++ b/libs/langgraph/README.md @@ -1,21 +1,65 @@ # @ngaf/langgraph -Adapter that wraps a LangGraph agent into the runtime-neutral `Agent` contract from `@ngaf/chat`. +Adapter that wraps a LangGraph agent into the runtime-neutral `Agent` contract from `@ngaf/chat`. The Angular equivalent of LangGraph's React `useStream()` hook — signal-driven access to messages, status, tool calls, interrupts, subagents, regenerate, and thread history. -## Citations +Part of the [Angular Agent Framework](https://github.com/cacheplane/angular-agent-framework). MIT licensed. -The `extractCitations()` function populates `Message.citations` from LangGraph message metadata. It reads from `additional_kwargs.citations` (preferred) or `additional_kwargs.sources` (fallback). +## Install -### Example: RAG chain with citations +```bash +npm install @ngaf/langgraph @ngaf/chat +``` + +**Peer dependencies:** `@angular/core ^20.0.0 || ^21.0.0`, `@langchain/core ^1.1.0`, `@langchain/langgraph-sdk ^1.7.0`, `rxjs ~7.8.0` + +## What it does + +- **`agent()`** — Angular Signal-based handle to a LangGraph streaming run. Returns `messages()`, `status()`, `isLoading()`, `error()`, `interrupt()`, `toolCalls()`, plus actions (`submit`, `stop`, `regenerate`, `reload`). +- **`provideAgent()`** — configure the LangGraph endpoint once in `app.config.ts`. Per-call overrides are accepted by `agent()` itself. +- **Thread persistence** — pass `threadId: signal(...)` + `onThreadId` to round-trip thread IDs through your own storage (localStorage, URL, etc.). +- **`MockAgentTransport`** — deterministic in-memory transport for tests. Never mock `agent()` itself; swap the transport instead. +- **`extractCitations()`** — populates `Message.citations` from LangGraph message metadata. Reads from `additional_kwargs.citations` (preferred) or `additional_kwargs.sources` (fallback). + +## Quick start + +```ts +// app.config.ts +import { provideAgent } from '@ngaf/langgraph'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideAgent({ + apiUrl: 'https://your-langgraph-platform.com', + assistantId: 'my-agent', + }), + ], +}; +``` ```ts -import { additional_kwargs } from '@langchain/core/messages'; +// chat.component.ts +import { Component } from '@angular/core'; +import { agent } from '@ngaf/langgraph'; +import { ChatComponent } from '@ngaf/chat'; +@Component({ + imports: [ChatComponent], + template: ``, +}) +export class ChatComponentHost { + chat = agent({ assistantId: 'my-agent' }); +} +``` + +> `agent()` must be called within an Angular injection context (component field initializer or constructor). Calling it in `ngOnInit` or any async context throws `NG0203: inject() must be called from an injection context`. + +## Citations example + +```ts // In your LangGraph node: const response = await llm.invoke([...]); -// Attach citations metadata: -const messageWithCitations = new AIMessage({ +return new AIMessage({ content: response.content, additional_kwargs: { citations: [ @@ -24,11 +68,23 @@ const messageWithCitations = new AIMessage({ index: 1, title: 'Example Article', url: 'https://example.com/article', - snippet: 'Relevant excerpt...' - } - ] - } + snippet: 'Relevant excerpt...', + }, + ], + }, }); // Message.citations auto-populates in @ngaf/chat via extractCitations() ``` + +## Documentation + +- [Quickstart](https://cacheplane.ai/docs/agent/getting-started/quickstart) +- [`agent()` API reference](https://cacheplane.ai/docs/agent/api/agent) +- [Human-in-the-loop / interrupts](https://cacheplane.ai/docs/agent/guides/interrupts) +- [Thread persistence](https://cacheplane.ai/docs/agent/guides/persistence) +- [Testing with `MockAgentTransport`](https://cacheplane.ai/docs/agent/guides/testing) + +## License + +MIT — free for any use. See [LICENSE](../../LICENSE).