Skip to content

Commit 68dce15

Browse files
bloveclaude
andauthored
feat(ag-ui): @cacheplane/ag-ui adapter wrapping @ag-ui/client (#139)
* docs: AG-UI adapter design New @cacheplane/ag-ui package wraps @ag-ui/client AbstractAgent into the runtime-neutral Agent contract. Scope B: messages + lifecycle + tool calls + state. toAgent(source) primitive + provideAgUiAgent({url}) DI convenience. Pure-function reducer; conformance test against shared suite. Cockpit demo proves end-to-end decoupling. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs: AG-UI adapter implementation plan 6 tasks: scaffold @cacheplane/ag-ui lib, pure-function reducer with table-driven spec, toAgent + conformance test, provideAgUiAgent DI convenience, cockpit streaming demo, final verify + PR. Pinned to @ag-ui/client and fast-json-patch (RFC 6902 for StateDelta). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ag-ui): scaffold @cacheplane/ag-ui library Mirror libs/langgraph/ structure. Stubbed toAgent and provideAgUiAgent will be implemented in subsequent commits. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ag-ui): pure-function reducer mapping AG-UI events to Agent signals Implements reduceEvent(event, store) covering RUN_STARTED/FINISHED/ERROR, TEXT_MESSAGE_START/CONTENT/END, TOOL_CALL_START/ARGS/END/RESULT, STATE_SNAPSHOT/DELTA, MESSAGES_SNAPSHOT, and CUSTOM. Discriminates state_update CustomEvents into AgentStateUpdateEvent. Table-driven tests cover every variant. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ag-ui): toAgent wraps AbstractAgent into Agent contract Subscribes to source via AgentSubscriber.onEvent and reduces every event into the produced Agent's signals. submit() optimistically appends user message to both our signals and source.addMessage(), then calls source.runAgent(). stop() calls source.abortRun(). Conformance suite validates the AG-UI adapter passes the same contract assertions as the LangGraph adapter. Also fixes reducer.ts type casts (as unknown as) required by @ag-ui/client 0.0.52 which uses Zod-inferred BaseEvent types that do not overlap with narrower cast targets without the intermediate unknown step. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(ag-ui): provideAgUiAgent DI convenience for HttpAgent Constructs HttpAgent from config and wires it through toAgent into the AG_UI_AGENT injection token. Convenience entry point for the common case; custom transports go through toAgent(customAgent) directly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(cockpit): AG-UI streaming demo using @cacheplane/ag-ui Mirrors cockpit/langgraph/streaming/angular/ structure. Demonstrates the chat-runtime decoupling: same <chat> composition, AG-UI runtime instead of LangGraph. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 7ccd3ad commit 68dce15

38 files changed

Lines changed: 2549 additions & 11 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"name": "@cacheplane/cockpit-ag-ui-streaming-angular",
3+
"version": "0.0.1",
4+
"peerDependencies": {
5+
"@cacheplane/ag-ui": "^0.0.1",
6+
"@cacheplane/chat": "^0.0.1"
7+
},
8+
"license": "PolyForm-Noncommercial-1.0.0",
9+
"sideEffects": false
10+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"name": "cockpit-ag-ui-streaming-angular",
3+
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "cockpit/ag-ui/streaming/angular/src",
5+
"projectType": "application",
6+
"targets": {
7+
"build": {
8+
"executor": "@angular/build:application",
9+
"outputs": ["{options.outputPath.base}"],
10+
"options": {
11+
"outputPath": {
12+
"base": "dist/cockpit/ag-ui/streaming/angular",
13+
"browser": ""
14+
},
15+
"browser": "cockpit/ag-ui/streaming/angular/src/main.ts",
16+
"tsConfig": "cockpit/ag-ui/streaming/angular/tsconfig.app.json",
17+
"styles": ["cockpit/ag-ui/streaming/angular/src/styles.css"]
18+
},
19+
"configurations": {
20+
"production": {
21+
"budgets": [
22+
{ "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" },
23+
{ "type": "anyComponentStyle", "maximumWarning": "4kb", "maximumError": "8kb" }
24+
],
25+
"outputHashing": "none"
26+
},
27+
"development": {
28+
"optimization": false,
29+
"extractLicenses": false,
30+
"sourceMap": true,
31+
"fileReplacements": [
32+
{
33+
"replace": "cockpit/ag-ui/streaming/angular/src/environments/environment.ts",
34+
"with": "cockpit/ag-ui/streaming/angular/src/environments/environment.development.ts"
35+
}
36+
]
37+
}
38+
},
39+
"defaultConfiguration": "production"
40+
},
41+
"serve": {
42+
"continuous": true,
43+
"executor": "@angular/build:dev-server",
44+
"configurations": {
45+
"production": { "buildTarget": "cockpit-ag-ui-streaming-angular:build:production" },
46+
"development": { "buildTarget": "cockpit-ag-ui-streaming-angular:build:development" }
47+
},
48+
"defaultConfiguration": "development",
49+
"options": {
50+
"proxyConfig": "cockpit/ag-ui/streaming/angular/proxy.conf.json"
51+
}
52+
},
53+
"smoke": {
54+
"executor": "nx:run-commands",
55+
"options": {
56+
"cwd": "cockpit/ag-ui/streaming/angular",
57+
"command": "npx tsx -e \"import { agUiStreamingAngularModule } from './src/index.ts'; const module = agUiStreamingAngularModule; if (module.id !== 'ag-ui-streaming-angular' || module.title !== 'AG-UI Streaming (Angular)') { throw new Error('Unexpected module shape for ' + module.id); }\""
58+
}
59+
}
60+
}
61+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# AG-UI Streaming (Angular)
2+
3+
This capability demonstrates real-time token streaming from an AG-UI compatible agent using the `@cacheplane/chat` Angular component library. The example shows how to wire the `AG_UI_AGENT` injection token (provided by `provideAgUiAgent`) into the `<chat>` host component and compose `<chat-messages>`, `<chat-input>`, and `<chat-typing-indicator>` to deliver a responsive, streaming chat experience.
4+
5+
Key components used: `<chat>`, `<chat-messages>`, `<chat-input>`, `<chat-typing-indicator>`. The `provideAgUiAgent` provider handles SSE event processing from the AG-UI streaming endpoint, and the chat components subscribe reactively without any manual subscription management.
6+
7+
The demo illustrates the chat-runtime decoupling: the same `<chat>` composition works with any agent runtime — LangGraph, AG-UI, or others — by conforming to the `AgentRef` interface.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"/agent": {
3+
"target": "http://localhost:3000",
4+
"secure": false,
5+
"changeOrigin": true,
6+
"ws": true
7+
}
8+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
2+
import { ApplicationConfig } from '@angular/core';
3+
import { provideAgUiAgent } from '@cacheplane/ag-ui';
4+
import { environment } from '../environments/environment';
5+
6+
export const appConfig: ApplicationConfig = {
7+
providers: [
8+
provideAgUiAgent({ url: environment.agUiUrl }),
9+
],
10+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
2+
import { Component, inject } from '@angular/core';
3+
import { ChatComponent } from '@cacheplane/chat';
4+
import { AG_UI_AGENT } from '@cacheplane/ag-ui';
5+
import { ExampleChatLayoutComponent } from '@cacheplane/example-layouts';
6+
7+
/**
8+
* Streaming demo — simplest possible @cacheplane/chat integration with AG-UI.
9+
*
10+
* Injects the AG_UI_AGENT token (provided by provideAgUiAgent) and passes it
11+
* to the prebuilt <chat> composition. The composition handles message rendering,
12+
* input, typing indicator, and error display internally.
13+
*
14+
* Demonstrates the chat-runtime decoupling: same <chat> composition as the
15+
* LangGraph cockpit, AG-UI runtime instead of LangGraph.
16+
*/
17+
@Component({
18+
selector: 'app-streaming',
19+
standalone: true,
20+
imports: [ChatComponent, ExampleChatLayoutComponent],
21+
template: `
22+
<example-chat-layout>
23+
<chat main [agent]="agent" class="flex-1 min-w-0" />
24+
</example-chat-layout>
25+
`,
26+
})
27+
export class StreamingComponent {
28+
protected readonly agent = inject(AG_UI_AGENT);
29+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Development environment configuration.
3+
*
4+
* Points to a local AG-UI compatible agent server started on port 3000.
5+
* The dev-server proxy (proxy.conf.json) forwards /agent to http://localhost:3000.
6+
*/
7+
export const environment = {
8+
production: false,
9+
agUiUrl: 'http://localhost:3000/agent',
10+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Production environment configuration.
3+
*
4+
* Uses relative /agent URL — configure a reverse proxy or Vercel rewrite
5+
* to forward requests to the AG-UI backend.
6+
*/
7+
export const environment = {
8+
production: true,
9+
agUiUrl: '/agent',
10+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>AG-UI Streaming — Angular</title>
6+
<base href="/" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1" />
8+
<script src="https://cdn.tailwindcss.com"></script>
9+
</head>
10+
<body class="bg-gray-950 text-gray-100 h-screen">
11+
<app-streaming></app-streaming>
12+
</body>
13+
</html>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
export interface CockpitCapabilityModule {
2+
id: string;
3+
manifestIdentity: {
4+
product: 'ag-ui';
5+
section: 'core-capabilities';
6+
topic: 'streaming';
7+
page: 'overview';
8+
language: 'angular';
9+
};
10+
title: string;
11+
docsPath: string;
12+
promptAssetPaths: string[];
13+
codeAssetPaths: string[];
14+
}
15+
16+
export const agUiStreamingAngularModule: CockpitCapabilityModule = {
17+
id: 'ag-ui-streaming-angular',
18+
manifestIdentity: {
19+
product: 'ag-ui',
20+
section: 'core-capabilities',
21+
topic: 'streaming',
22+
page: 'overview',
23+
language: 'angular',
24+
},
25+
title: 'AG-UI Streaming (Angular)',
26+
docsPath: '/docs/ag-ui/core-capabilities/streaming/overview/angular',
27+
promptAssetPaths: [
28+
'cockpit/ag-ui/streaming/angular/prompts/streaming.md',
29+
],
30+
codeAssetPaths: [
31+
'cockpit/ag-ui/streaming/angular/src/app/streaming.component.ts',
32+
],
33+
};

0 commit comments

Comments
 (0)