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
15 changes: 13 additions & 2 deletions apps/cockpit/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
168 changes: 168 additions & 0 deletions apps/cockpit/src/app/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -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<ArrayBuffer | null> {
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<typeof f> => f !== null);

return new ImageResponse(
(
<div
style={{
width: '100%',
height: '100%',
background: '#f4f6fb',
display: 'flex',
flexDirection: 'column',
padding: '64px 72px',
color: '#1a1a2e',
fontFamily: 'Inter, sans-serif',
}}
>
{/* Eyebrow */}
<div
style={{
fontFamily: 'JetBrains Mono, monospace',
fontSize: 16,
letterSpacing: '0.12em',
color: '#004090',
fontWeight: 700,
textTransform: 'uppercase',
marginBottom: 24,
}}
>
Cockpit · Angular Agent Framework
</div>

{/* Headline — Inter Bold (cockpit chrome is sans-serif Linear-style) */}
<div
style={{
fontSize: 68,
lineHeight: 1.08,
fontWeight: 700,
letterSpacing: '-0.02em',
color: '#1a1a2e',
marginBottom: 22,
maxWidth: 1000,
}}
>
The live reference app for the framework.
</div>

{/* Subhead */}
<div
style={{
fontSize: 24,
lineHeight: 1.5,
color: '#555770',
maxWidth: 940,
marginBottom: 'auto',
}}
>
Real LangGraph and AG-UI agents running through the same Angular surface you&apos;ll ship.
Switch between Run · Code · Docs · API for each capability.
</div>

{/* Mode pills + cockpit wordmark */}
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginTop: 32,
}}
>
<div style={{ display: 'flex', gap: 10 }}>
{['Run', 'Code', 'Docs', 'API'].map((mode, i) => (
<ModePill key={mode} active={i === 0}>{mode}</ModePill>
))}
</div>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: 10,
fontFamily: 'JetBrains Mono, monospace',
fontSize: 18,
fontWeight: 700,
color: '#1a1a2e',
}}
>
<span style={{ fontSize: 26 }}>🛩️</span>
<span>cockpit.cacheplane.ai</span>
</div>
</div>
</div>
),
{
...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 (
<div
style={{
display: 'flex',
alignItems: 'center',
padding: '8px 20px',
borderRadius: 999,
background: active ? '#004090' : '#ffffff',
border: `1px solid ${active ? '#004090' : '#e6e8ee'}`,
color: active ? '#ffffff' : '#555770',
fontFamily: 'JetBrains Mono, monospace',
fontSize: 15,
fontWeight: 700,
}}
>
{children}
</div>
);
}
22 changes: 21 additions & 1 deletion libs/ag-ui/README.md
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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).
78 changes: 67 additions & 11 deletions libs/langgraph/README.md
Original file line number Diff line number Diff line change
@@ -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: `<chat [agent]="chat" />`,
})
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: [
Expand All @@ -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).
Loading