-
-
Notifications
You must be signed in to change notification settings - Fork 135
Add an example of TanStack AI <> AG UI integration #287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
shivaylamba
wants to merge
7
commits into
TanStack:main
Choose a base branch
from
shivaylamba:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
7b23e6d
Initial plan
Copilot 6faa19f
Add ts-react-copilotkit example: TanStack AI + CopilotKit AG-UI integ…
Copilot 3ce5d75
Fix CopilotKit integration: use HttpAgent for AG-UI protocol, add /in…
Copilot ba10ffb
Improve type assertion comment for AG-UI message conversion
Copilot 5cb2cde
push
shivaylamba 03129ef
Merge pull request #1 from shivaylamba/copilot/add-example-for-copilo…
shivaylamba 67037cf
Merge branch 'main' into main
AlemTuzlak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| node_modules | ||
| .DS_Store | ||
| dist | ||
| dist-ssr | ||
| *.local | ||
| .env | ||
| .nitro | ||
| .tanstack | ||
| .output | ||
| .vinxi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| # TanStack AI — AG-UI Protocol Example | ||
|
|
||
| This example demonstrates how **TanStack AI** implements the open [AG-UI protocol](https://docs.ag-ui.com) standard for agent-user interaction. | ||
|
|
||
| ## What is AG-UI? | ||
|
|
||
| AG-UI (Agent-User Interaction) is an open protocol that standardizes how AI agents communicate with user interfaces. TanStack AI is AG-UI compliant, enabling interoperability with other AG-UI compatible clients. | ||
|
|
||
| ## Architecture | ||
|
|
||
| ``` | ||
| ┌─────────────────────────────────────────────────┐ | ||
| │ Client │ | ||
| │ │ | ||
| │ ┌──────────────────┐ │ | ||
| │ │ TanStack AI │ │ | ||
| │ │ useChat() hook │ │ | ||
| │ └────────┬─────────┘ │ | ||
| │ │ │ | ||
| │ │ AG-UI Protocol │ | ||
| │ │ (SSE / Events) │ | ||
| │ │ │ | ||
| └───────────┼──────────────────────────────────────┘ | ||
| │ | ||
| ▼ | ||
| ┌───────────────────────────────────────────────────┐ | ||
| │ Server │ | ||
| │ │ | ||
| │ ┌─────────────────────────────────────────────┐ │ | ||
| │ │ TanStack AI Server │ │ | ||
| │ │ │ │ | ||
| │ │ chat() → AG-UI Events → SSE Response │ │ | ||
| │ │ │ │ | ||
| │ │ Adapters: OpenAI, Anthropic, Gemini, etc. │ │ | ||
| │ └─────────────────────────────────────────────┘ │ | ||
| └───────────────────────────────────────────────────┘ | ||
| ``` | ||
|
|
||
| ## What This Example Shows | ||
|
|
||
| 1. **TanStack AI Server**: The `/api/chat` endpoint uses TanStack AI's `chat()` function with OpenAI adapter to process messages and stream AG-UI events via Server-Sent Events (SSE). | ||
|
|
||
| 2. **TanStack AI Client**: Uses `useChat` hook from `@tanstack/ai-react` with `fetchServerSentEvents` to consume the AG-UI stream. | ||
|
|
||
| 3. **AG-UI Protocol**: The communication protocol that enables standardized agent-server interaction, supporting interoperability with other AG-UI compliant systems. | ||
|
|
||
| ## AG-UI Events | ||
|
|
||
| The server streams events following the AG-UI protocol: | ||
|
|
||
| | Event | Description | | ||
| |-------|------------| | ||
| | `RUN_STARTED` | Signals the beginning of an AI run | | ||
| | `TEXT_MESSAGE_START` | Marks the start of a text message | | ||
| | `TEXT_MESSAGE_CONTENT` | Streams text content chunks | | ||
| | `TEXT_MESSAGE_END` | Marks the end of a text message | | ||
| | `TOOL_CALL_START` | Initiates a tool call | | ||
| | `TOOL_CALL_ARGS` | Streams tool call arguments | | ||
| | `TOOL_CALL_END` | Completes a tool call | | ||
| | `RUN_FINISHED` | Signals completion of the run | | ||
|
|
||
| ## Getting Started | ||
|
|
||
| ### Prerequisites | ||
|
|
||
| - Node.js 18+ | ||
| - pnpm | ||
| - An OpenAI API key | ||
|
|
||
| ### Setup | ||
|
|
||
| 1. Create a `.env` file in this directory: | ||
|
|
||
| ```bash | ||
| OPENAI_API_KEY=your-openai-api-key | ||
| ``` | ||
|
|
||
| 2. Install dependencies from the monorepo root: | ||
|
|
||
| ```bash | ||
| cd ../.. | ||
| pnpm install | ||
| ``` | ||
|
|
||
| 3. Run the example: | ||
|
|
||
| ```bash | ||
| cd examples/ts-react-copilotkit | ||
| pnpm dev | ||
| ``` | ||
|
|
||
| 4. Open [http://localhost:3001](http://localhost:3001) in your browser. | ||
|
|
||
| ### Switching Between Clients | ||
|
|
||
| Use the tabs at the top of the page to switch between: | ||
| - **🔥 TanStack AI Client** — Uses `@tanstack/ai-react`'s `useChat` hook | ||
| - **🪁 CopilotKit Client** — Uses CopilotKit's `<CopilotChat>` component | ||
|
|
||
| Both connect to the same TanStack AI server endpoint. | ||
|
|
||
| ## Key Files | ||
|
|
||
| | File | Description | | ||
| |------|-------------| | ||
| | `src/routes/api.chat.ts` | Server endpoint using TanStack AI's `chat()` function | | ||
| | `src/routes/index.tsx` | Client page with both TanStack AI and CopilotKit UIs | | ||
| | `src/routes/__root.tsx` | Root layout | | ||
|
|
||
| ## Learn More | ||
|
|
||
| - [TanStack AI Documentation](https://tanstack.com/ai) | ||
| - [CopilotKit Documentation](https://docs.copilotkit.ai) | ||
| - [AG-UI Protocol](https://docs.ag-ui.com) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| { | ||
| "name": "ts-react-copilotkit", | ||
| "private": true, | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "vite dev --port 3001", | ||
| "build": "vite build", | ||
| "serve": "vite preview", | ||
| "test": "exit 0" | ||
| }, | ||
| "dependencies": { | ||
| "@ag-ui/client": "^0.0.45", | ||
| "@tailwindcss/vite": "^4.1.18", | ||
| "@tanstack/ai": "workspace:*", | ||
| "@tanstack/ai-client": "workspace:*", | ||
| "@tanstack/ai-openai": "workspace:*", | ||
| "@tanstack/ai-react": "workspace:*", | ||
| "@tanstack/nitro-v2-vite-plugin": "^1.154.7", | ||
| "@tanstack/react-router": "^1.158.4", | ||
| "@tanstack/react-start": "^1.159.0", | ||
| "react": "^19.2.3", | ||
| "react-dom": "^19.2.3", | ||
| "tailwindcss": "^4.1.18", | ||
| "zod": "^4.2.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/react": "^19.2.7", | ||
| "@types/react-dom": "^19.2.3", | ||
| "@vitejs/plugin-react": "^5.1.2", | ||
| "typescript": "5.9.3", | ||
| "vite": "^7.2.7", | ||
| "vite-tsconfig-paths": "^5.1.4" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| /* eslint-disable */ | ||
|
|
||
| // @ts-nocheck | ||
|
|
||
| // noinspection JSUnusedGlobalSymbols | ||
|
|
||
| // This file was automatically generated by TanStack Router. | ||
| // You should NOT make any changes in this file as it will be overwritten. | ||
| // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. | ||
|
|
||
| import { Route as rootRouteImport } from './routes/__root' | ||
| import { Route as IndexRouteImport } from './routes/index' | ||
| import { Route as ApiChatRouteImport } from './routes/api.chat' | ||
| import { Route as ApiChatInfoRouteImport } from './routes/api.chat.info' | ||
|
|
||
| const IndexRoute = IndexRouteImport.update({ | ||
| id: '/', | ||
| path: '/', | ||
| getParentRoute: () => rootRouteImport, | ||
| } as any) | ||
| const ApiChatRoute = ApiChatRouteImport.update({ | ||
| id: '/api/chat', | ||
| path: '/api/chat', | ||
| getParentRoute: () => rootRouteImport, | ||
| } as any) | ||
| const ApiChatInfoRoute = ApiChatInfoRouteImport.update({ | ||
| id: '/info', | ||
| path: '/info', | ||
| getParentRoute: () => ApiChatRoute, | ||
| } as any) | ||
|
|
||
| export interface FileRoutesByFullPath { | ||
| '/': typeof IndexRoute | ||
| '/api/chat': typeof ApiChatRouteWithChildren | ||
| '/api/chat/info': typeof ApiChatInfoRoute | ||
| } | ||
| export interface FileRoutesByTo { | ||
| '/': typeof IndexRoute | ||
| '/api/chat': typeof ApiChatRouteWithChildren | ||
| '/api/chat/info': typeof ApiChatInfoRoute | ||
| } | ||
| export interface FileRoutesById { | ||
| __root__: typeof rootRouteImport | ||
| '/': typeof IndexRoute | ||
| '/api/chat': typeof ApiChatRouteWithChildren | ||
| '/api/chat/info': typeof ApiChatInfoRoute | ||
| } | ||
| export interface FileRouteTypes { | ||
| fileRoutesByFullPath: FileRoutesByFullPath | ||
| fullPaths: '/' | '/api/chat' | '/api/chat/info' | ||
| fileRoutesByTo: FileRoutesByTo | ||
| to: '/' | '/api/chat' | '/api/chat/info' | ||
| id: '__root__' | '/' | '/api/chat' | '/api/chat/info' | ||
| fileRoutesById: FileRoutesById | ||
| } | ||
| export interface RootRouteChildren { | ||
| IndexRoute: typeof IndexRoute | ||
| ApiChatRoute: typeof ApiChatRouteWithChildren | ||
| } | ||
|
|
||
| declare module '@tanstack/react-router' { | ||
| interface FileRoutesByPath { | ||
| '/': { | ||
| id: '/' | ||
| path: '/' | ||
| fullPath: '/' | ||
| preLoaderRoute: typeof IndexRouteImport | ||
| parentRoute: typeof rootRouteImport | ||
| } | ||
| '/api/chat': { | ||
| id: '/api/chat' | ||
| path: '/api/chat' | ||
| fullPath: '/api/chat' | ||
| preLoaderRoute: typeof ApiChatRouteImport | ||
| parentRoute: typeof rootRouteImport | ||
| } | ||
| '/api/chat/info': { | ||
| id: '/api/chat/info' | ||
| path: '/info' | ||
| fullPath: '/api/chat/info' | ||
| preLoaderRoute: typeof ApiChatInfoRouteImport | ||
| parentRoute: typeof ApiChatRoute | ||
| } | ||
| } | ||
| } | ||
|
|
||
| interface ApiChatRouteChildren { | ||
| ApiChatInfoRoute: typeof ApiChatInfoRoute | ||
| } | ||
|
|
||
| const ApiChatRouteChildren: ApiChatRouteChildren = { | ||
| ApiChatInfoRoute: ApiChatInfoRoute, | ||
| } | ||
|
|
||
| const ApiChatRouteWithChildren = | ||
| ApiChatRoute._addFileChildren(ApiChatRouteChildren) | ||
|
|
||
| const rootRouteChildren: RootRouteChildren = { | ||
| IndexRoute: IndexRoute, | ||
| ApiChatRoute: ApiChatRouteWithChildren, | ||
| } | ||
| export const routeTree = rootRouteImport | ||
| ._addFileChildren(rootRouteChildren) | ||
| ._addFileTypes<FileRouteTypes>() | ||
|
|
||
| import type { getRouter } from './router.tsx' | ||
| import type { createStart } from '@tanstack/react-start' | ||
| declare module '@tanstack/react-start' { | ||
| interface Register { | ||
| ssr: true | ||
| router: Awaited<ReturnType<typeof getRouter>> | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { createRouter } from '@tanstack/react-router' | ||
|
|
||
| import { routeTree } from './routeTree.gen' | ||
|
|
||
| export const getRouter = () => { | ||
| return createRouter({ | ||
| routeTree, | ||
| scrollRestoration: true, | ||
| defaultPreloadStaleTime: 0, | ||
| }) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router' | ||
| import appCss from '../styles.css?url' | ||
|
|
||
| export const Route = createRootRoute({ | ||
| head: () => ({ | ||
| meta: [ | ||
| { | ||
| charSet: 'utf-8', | ||
| }, | ||
| { | ||
| name: 'viewport', | ||
| content: 'width=device-width, initial-scale=1', | ||
| }, | ||
| { | ||
| title: 'TanStack AI + CopilotKit — AG-UI Integration', | ||
| }, | ||
| ], | ||
| links: [ | ||
| { | ||
| rel: 'stylesheet', | ||
| href: appCss, | ||
| }, | ||
| ], | ||
| }), | ||
|
|
||
| shellComponent: RootDocument, | ||
| }) | ||
|
|
||
| function RootDocument({ children }: { children: React.ReactNode }) { | ||
| return ( | ||
| <html lang="en"> | ||
| <head> | ||
| <HeadContent /> | ||
| </head> | ||
| <body> | ||
| {children} | ||
| <Scripts /> | ||
| </body> | ||
| </html> | ||
| ) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import { createFileRoute } from '@tanstack/react-router' | ||
|
|
||
| /** | ||
| * Runtime info endpoint for CopilotKit/AG-UI compatibility. | ||
| * | ||
| * CopilotKit's HttpAgent client may fetch runtime information | ||
| * about available agents and their capabilities. | ||
| * | ||
| * Supports both GET (for simple info queries) and POST (for detailed requests). | ||
| */ | ||
| export const Route = createFileRoute('/api/chat/info')({ | ||
| server: { | ||
| handlers: { | ||
| GET: async () => { | ||
| const runtimeInfo = { | ||
| agents: [ | ||
| { | ||
| id: 'tanstack-ai', | ||
| name: 'tanstack-ai', | ||
| description: 'TanStack AI agent connected via AG-UI protocol', | ||
| capabilities: ['chat', 'sse', 'ag-ui-protocol'], | ||
| }, | ||
| ], | ||
| provider: 'tanstack-ai', | ||
| version: '1.0.0', | ||
| } | ||
|
|
||
| return new Response(JSON.stringify(runtimeInfo), { | ||
| status: 200, | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| }) | ||
| }, | ||
|
|
||
| POST: async ({ request }) => { | ||
| // Some AG-UI clients may POST to /info for discovery | ||
| // Return the same runtime information | ||
| const runtimeInfo = { | ||
| agents: [ | ||
| { | ||
| id: 'tanstack-ai', | ||
| name: 'tanstack-ai', | ||
| description: 'TanStack AI agent connected via AG-UI protocol', | ||
| capabilities: ['chat', 'sse', 'ag-ui-protocol'], | ||
| }, | ||
| ], | ||
| provider: 'tanstack-ai', | ||
| version: '1.0.0', | ||
| } | ||
|
|
||
| return new Response(JSON.stringify(runtimeInfo), { | ||
| status: 200, | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| }) | ||
| }, | ||
| }, | ||
| }, | ||
| }) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
README describes tab-switching UI that doesn't exist in the code.
The README references switching between "🔥 TanStack AI Client" and "🪁 CopilotKit Client" tabs, but
src/routes/index.tsxonly rendersTanStackAIChat— there's no CopilotKit client component or tab UI implemented. Either update the README to match the current implementation or add the missing CopilotKit client tab.🤖 Prompt for AI Agents