fix: convert starter templates from pnpm/Turborepo to flat npm projects

Flatten langgraph-python, langgraph-js, and mcp-apps starters so
npm install && npm run dev works out of the box. Replace Turborepo
with concurrently, move apps/* to root, update Dockerfiles, READMEs,
and entrypoints. Also remove stray pnpm-lock.yaml from a2a-a2ui and
ms-agent-framework-dotnet starters.
This commit is contained in:
Alem Tuzlak
2026-04-16 13:23:30 +02:00
parent b7e009c56b
commit 453097ff18
147 changed files with 349 additions and 52086 deletions
@@ -141,3 +141,8 @@ vite.config.ts.timestamp-*
.vite/
next-env.d.ts
# lock files (keep package-lock.json for npm)
pnpm-lock.yaml
yarn.lock
bun.lockb
File diff suppressed because it is too large Load Diff
@@ -3,5 +3,4 @@ node_modules
.git
.env
.env.local
apps/web/node_modules
apps/agent/node_modules
agent/node_modules
@@ -17,9 +17,6 @@ node_modules
.next/
/out/
# turbo
.turbo
# production
/build
@@ -44,8 +41,10 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts
# lock files
package-lock.json
pnpm-lock.yaml
# lock files (keep package-lock.json for npm)
yarn.lock
pnpm-lock.yaml
bun.lockb
# LangGraph API
.langgraph_api
+12 -16
View File
@@ -1,19 +1,17 @@
# Stage 1: Build everything in monorepo
# Stage 1: Build the Next.js frontend
FROM node:20-slim AS builder
WORKDIR /app
RUN corepack enable
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./
COPY apps/web/package.json ./apps/web/
COPY apps/agent/package.json ./apps/agent/
RUN pnpm install --no-frozen-lockfile
COPY package.json ./
RUN npm install
COPY apps/ ./apps/
COPY turbo.json ./
COPY app/ ./app/
COPY public/ ./public/
COPY next.config.ts tsconfig.json postcss.config.mjs ./
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN pnpm --filter web build
RUN npx next build
# Stage 2: Production image
FROM node:20-slim AS runner
@@ -23,15 +21,13 @@ RUN npm install -g @langchain/langgraph-cli
WORKDIR /app
# Next.js build artifacts
COPY --from=builder /app/apps/web/.next ./apps/web/.next
COPY --from=builder /app/apps/web/node_modules ./apps/web/node_modules
COPY --from=builder /app/apps/web/package.json ./apps/web/
COPY --from=builder /app/apps/web/public ./apps/web/public
# Agent code + dependencies
COPY --from=builder /app/apps/agent ./apps/agent
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
COPY --from=builder /app/public ./public
# Agent code + dependencies
COPY agent/ ./agent/
# Copy entrypoint
COPY entrypoint.sh ./
+33 -41
View File
@@ -2,86 +2,78 @@
This is a starter template for building AI agents using [LangGraph](https://www.langchain.com/langgraph) and [CopilotKit](https://copilotkit.ai). It provides a modern Next.js application with an integrated LangGraph agent to be built on top of.
This project is organized as a monorepo using [Turborepo](https://turbo.build) and [pnpm workspaces](https://pnpm.io/workspaces).
## Project Structure
```
.
├── apps/
│ ├── web/ # Next.js frontend application
│ └── agent/ # LangGraph agent
├── pnpm-workspace.yaml
├── turbo.json
├── app/ # Next.js App Router pages and API routes
│ ├── page.tsx # Main page
│ └── api/copilotkit/ # CopilotKit API route
├── agent/ # LangGraph agent
│ ├── src/agent.ts # Agent definition
│ └── langgraph.json # LangGraph configuration
├── scripts/ # Agent run scripts
├── public/ # Static assets
├── next.config.ts
├── tsconfig.json
└── package.json
```
## Prerequisites
- Node.js 18+
- [pnpm](https://pnpm.io/installation) 9.15.0 or later
- OpenAI API Key (for the LangGraph agent)
## Getting Started
1. Install all dependencies (this installs everything for both apps):
1. Install dependencies:
```bash
pnpm install
npm install
```
2. Set up your OpenAI API key:
2. Set up your environment variables:
```bash
cd apps/agent
echo "OPENAI_API_KEY=your-openai-api-key-here" > .env
cp .env.example .env
```
Then edit the `.env` file and add your OpenAI API key:
```bash
OPENAI_API_KEY=your-openai-api-key-here
```
3. Start the development servers:
```bash
pnpm dev
npm run dev
```
This will start both the Next.js app (on port 3000) and the LangGraph agent (on port 8123) using Turborepo.
This will start both the Next.js app (on port 3000) and the LangGraph agent (on port 8123) concurrently.
## Available Scripts
All scripts use Turborepo to run tasks across the monorepo:
- `pnpm dev` - Starts both the web app and agent servers in development mode
- `pnpm dev:studio` - Starts the web app and agent with LangGraph Studio UI
- `pnpm build` - Builds all apps for production
- `pnpm lint` - Runs linting across all apps
### Running Scripts for Individual Apps
You can also run scripts for individual apps using pnpm's filter flag:
```bash
# Run dev for just the web app
pnpm --filter web dev
# Run dev for just the agent
pnpm --filter agent dev
# Or navigate to the app directory
cd apps/web
pnpm dev
```
- `npm run dev` - Starts both the web app and agent servers in development mode
- `npm run dev:debug` - Starts development servers with debug logging enabled
- `npm run dev:ui` - Starts only the Next.js UI server
- `npm run dev:agent` - Starts only the LangGraph agent server
- `npm run build` - Builds the Next.js application for production
- `npm run start` - Starts the production server
- `npm run lint` - Runs linting
## Customization
The main UI component is in `apps/web/src/app/page.tsx`. You can:
The main UI component is in `app/page.tsx`. You can:
- Modify the theme colors and styling
- Add new frontend actions
- Utilize shared-state
- Customize your user-interface for interacting with LangGraph
The LangGraph agent code is in `apps/agent/src/`.
The LangGraph agent code is in `agent/src/`.
## 📚 Documentation
## Documentation
- [CopilotKit Documentation](https://docs.copilotkit.ai) - Explore CopilotKit's capabilities
- [LangGraph Documentation](https://langchain-ai.github.io/langgraph/) - Learn more about LangGraph and its features
@@ -101,6 +93,6 @@ This project is licensed under the MIT License - see the LICENSE file for detail
If you see "I'm having trouble connecting to my tools", make sure:
1. The LangGraph agent is running on port 8000
1. The LangGraph agent is running on port 8123
2. Your OpenAI API key is set correctly
3. Both servers started successfully
@@ -5,5 +5,5 @@
"graphs": {
"starterAgent": "./src/agent.ts:graph"
},
"env": ".env"
"env": "../.env"
}

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

@@ -1,29 +0,0 @@
{
"name": "web",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@copilotkit/react-core": "1.52.1",
"@copilotkit/react-ui": "1.52.1",
"@copilotkit/runtime": "1.52.1",
"next": "16.0.8",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"shiki": "^3.22.0",
"zod": "^3.24.4"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"typescript": "^5"
}
}
@@ -10,8 +10,8 @@ services:
agent:
build:
context: ./apps/agent
dockerfile: ../../docker/Dockerfile.agent
context: ./agent
dockerfile: ../docker/Dockerfile.agent
environment:
- OPENAI_API_KEY=test-key-for-aimock
- OPENAI_BASE_URL=http://aimock:4010/v1
@@ -1,11 +1,9 @@
# Dockerfile for the LangGraph JS agent.
# Mirrors the user experience: pnpm install + langgraph-cli dev.
# Mirrors the user experience: npm install + langgraph-cli dev.
FROM node:22-alpine
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy agent package files
COPY package.json ./
COPY langgraph.json ./
@@ -13,7 +11,7 @@ COPY tsconfig.json ./
COPY src/ ./src/
# Install dependencies
RUN pnpm install --no-frozen-lockfile
RUN npm install
EXPOSE 8123
@@ -1,5 +1,5 @@
# Dockerfile for the LangGraph JS Next.js frontend.
# Builds from the monorepo root so pnpm workspaces resolve correctly.
# Builds from the project root.
FROM node:22-alpine AS base
FROM base AS deps
@@ -7,40 +7,30 @@ RUN apk add --no-cache libc6-compat
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy monorepo package files
COPY package.json pnpm-workspace.yaml ./
COPY apps/web/package.json ./apps/web/
COPY apps/agent/package.json ./apps/agent/
COPY turbo.json ./
# Copy package file
COPY package.json ./
# Install dependencies
RUN pnpm install --no-frozen-lockfile
RUN npm install
# Build stage
FROM base AS builder
WORKDIR /app
RUN corepack enable && corepack prepare pnpm@latest --activate
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/apps/web/node_modules ./apps/web/node_modules
COPY --from=deps /app/apps/agent/node_modules ./apps/agent/node_modules
COPY . .
# Add standalone output + ignoreBuildErrors + turbopack root for monorepo
# Add standalone output + ignoreBuildErrors for Docker build
RUN node -e "\
const fs=require('fs'); const f='apps/web/next.config.ts'; \
const fs=require('fs'); const f='next.config.ts'; \
let c=fs.readFileSync(f,'utf8'); \
if(!c.includes('standalone')){c=c.replace('};',' output: \"standalone\",\n};');} \
if(!c.includes('ignoreBuildErrors')){c=c.replace('};',' typescript: { ignoreBuildErrors: true },\n};');} \
if(!c.includes('turbopack')){c=c.replace('};',' turbopack: { root: \"../..\" },\n};');} \
fs.writeFileSync(f,c);"
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN pnpm --filter web build
RUN npx next build
# Production stage
FROM base AS runner
@@ -53,12 +43,12 @@ ENV HOSTNAME=0.0.0.0
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
# Copy standalone build output
COPY --from=builder /app/apps/web/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
CMD ["node", "apps/web/server.js"]
CMD ["node", "server.js"]
@@ -2,7 +2,7 @@
set -e
# Start LangGraph agent server on port 8123
cd /app/apps/agent
cd /app/agent
npx @langchain/langgraph-cli dev --port 8123 --no-browser &
AGENT_PID=$!
cd /app
@@ -10,10 +10,9 @@ cd /app
sleep 3
# Start Next.js frontend
cd /app/apps/web
cd /app
PORT=${PORT:-3000} npx next start --port ${PORT:-3000} &
NEXT_PID=$!
cd /app
wait -n $AGENT_PID $NEXT_PID
EXIT_CODE=$?
@@ -27,7 +27,7 @@
{
"match": {},
"response": {
"content": "You're currently running against aimock (a mock LLM server). This response is a catch-all for requests that don't match any test fixture. To use a real LLM: (1) Add your OPENAI_API_KEY to .env, (2) Remove or unset OPENAI_BASE_URL from your environment so requests go to OpenAI instead of aimock, (3) Restart with `pnpm dev`."
"content": "You're currently running against aimock (a mock LLM server). This response is a catch-all for requests that don't match any test fixture. To use a real LLM: (1) Add your OPENAI_API_KEY to .env, (2) Remove or unset OPENAI_BASE_URL from your environment so requests go to OpenAI instead of aimock, (3) Restart with `npm run dev`."
}
}
]
+27 -10
View File
@@ -2,17 +2,35 @@
"name": "langgraph-js-starter",
"version": "0.1.0",
"private": true,
"workspaces": [
"apps/*"
],
"scripts": {
"dev": "turbo run dev",
"build": "turbo run build",
"lint": "turbo run lint"
"dev": "concurrently \"npm run dev:ui\" \"npm run dev:agent\" --names ui,agent --prefix-colors blue,green --kill-others",
"dev:debug": "LOG_LEVEL=debug npm run dev",
"dev:ui": "next dev",
"dev:agent": "./scripts/run-agent.sh || scripts/run-agent.bat",
"build": "next build",
"start": "next start",
"lint": "next lint",
"install:agent": "cd agent && npm install",
"postinstall": "npm run install:agent"
},
"dependencies": {
"@copilotkit/react-core": "1.52.1",
"@copilotkit/react-ui": "1.52.1",
"@copilotkit/runtime": "1.52.1",
"next": "16.0.8",
"react": "^19.2.1",
"react-dom": "^19.2.1",
"shiki": "^3.22.0",
"zod": "^3.24.4"
},
"devDependencies": {
"@langchain/langgraph-cli": "^1.0.4",
"turbo": "^2.3.3"
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"concurrently": "^9.1.2",
"tailwindcss": "^4",
"typescript": "^5"
},
"overrides": {
"@langchain/core": "^1.0.1",
@@ -20,6 +38,5 @@
},
"engines": {
"node": ">=18.0.0"
},
"packageManager": "pnpm@9.15.0"
}
}
@@ -1,2 +0,0 @@
packages:
- "apps/*"

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 391 B

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 128 B

After

Width:  |  Height:  |  Size: 128 B

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 385 B

@@ -0,0 +1,3 @@
@echo off
cd /d "%~dp0..\agent"
npx @langchain/langgraph-cli dev --port 8123 --no-browser
@@ -0,0 +1,3 @@
#!/bin/bash
cd "$(dirname "$0")/../agent" || exit 1
npx @langchain/langgraph-cli dev --port 8123 --no-browser
@@ -19,7 +19,7 @@
}
],
"paths": {
"@/*": ["./src/*"]
"@/*": ["./*"]
}
},
"include": [
@@ -1,16 +0,0 @@
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"dev": {
"cache": false,
"persistent": true
},
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
},
"lint": {
"dependsOn": ["^lint"]
}
}
}
@@ -1,6 +1,5 @@
node_modules
.next
.turbo
.git
.gitignore
.env*
@@ -10,7 +9,6 @@ node_modules
.claude
.worktrees
.langgraph_api
apps/agent/.venv
apps/agent/__pycache__
apps/mcp
agent/.venv
agent/__pycache__
coverage
@@ -42,9 +42,9 @@ yarn-error.log*
*.tsbuildinfo
next-env.d.ts
# lock files
package-lock.json
# lock files (keep package-lock.json for npm)
yarn.lock
pnpm-lock.yaml
bun.lockb
# LangGraph API
@@ -53,9 +53,6 @@ bun.lockb
# Git worktrees
.worktrees
# Turbo
.turbo
# Tools
.claude
.scratch/
@@ -1,52 +0,0 @@
const path = require("path");
/**
* When COPILOTKIT_LOCAL is set, rewrites CopilotKit and AG-UI dependencies
* to link against local monorepo packages instead of pulling from npm.
*
* Usage:
* COPILOTKIT_LOCAL=1 pnpm install # link to local packages
* pnpm install # install from npm (default)
*
* Expects the AG-UI repo to be cloned alongside CopilotKit:
* /some/path/CopilotKit/
* /some/path/ag-ui/
*/
const COPILOTKIT_ROOT = path.resolve(__dirname, "..", "..", "..");
const AGUI_ROOT = path.resolve(COPILOTKIT_ROOT, "..", "ag-ui");
const LOCAL_PACKAGES = {
// CopilotKit
"@copilotkit/react-core": path.join(COPILOTKIT_ROOT, "packages/react-core"),
"@copilotkit/react-ui": path.join(COPILOTKIT_ROOT, "packages/react-ui"),
"@copilotkit/runtime": path.join(COPILOTKIT_ROOT, "packages/runtime"),
"@copilotkit/shared": path.join(COPILOTKIT_ROOT, "packages/shared"),
"@copilotkit/a2ui-renderer": path.join(
COPILOTKIT_ROOT,
"packages/a2ui-renderer",
),
// AG-UI
"@ag-ui/client": path.join(AGUI_ROOT, "sdks/typescript/packages/client"),
"@ag-ui/core": path.join(AGUI_ROOT, "sdks/typescript/packages/core"),
"@ag-ui/encoder": path.join(AGUI_ROOT, "sdks/typescript/packages/encoder"),
"@ag-ui/proto": path.join(AGUI_ROOT, "sdks/typescript/packages/proto"),
"@ag-ui/a2ui-middleware": path.join(AGUI_ROOT, "middlewares/a2ui-middleware"),
"@ag-ui/mcp-apps-middleware": path.join(
AGUI_ROOT,
"middlewares/mcp-apps-middleware",
),
};
function readPackage(pkg) {
if (process.env.COPILOTKIT_LOCAL) {
for (const [name, localPath] of Object.entries(LOCAL_PACKAGES)) {
if (pkg.dependencies?.[name]) {
pkg.dependencies[name] = `link:${localPath}`;
}
}
}
return pkg;
}
module.exports = { hooks: { readPackage } };
@@ -19,34 +19,36 @@ This uses CopilotKit's **v2 agent state pattern** where state lives in the agent
## Architecture
This is a **Turborepo monorepo** with three apps:
This is a **flat npm project** with a Next.js frontend at the root and a Python agent in `agent/`.
### Repository Structure
```
apps/
├── app/ # Next.js frontend
│ ├── src/
│ │ ├── app/
│ │ │ ├── page.tsx # Main page - wires up all components
│ │ │ └── api/copilotkit/ # CopilotKit API route
│ │ ├── components/
│ │ │ ├── canvas/ # Todo list UI
│ │ │ │ ├── index.tsx # Canvas container
│ │ │ │ ├── todo-list.tsx # Todo list with columns
│ │ │ │ ├── todo-column.tsx # Column (pending/completed)
│ │ │ │ └── todo-card.tsx # Individual todo card
│ │ │ ├── example-layout/ # Layout: chat + canvas side-by-side
│ │ │ └── generative-ui/ # Example generative UI components
│ │ └── hooks/
│ │ ├── use-generative-ui-examples.tsx # Example CopilotKit patterns
│ │ └── use-example-suggestions.tsx # Chat suggestions
├── agent/ # LangGraph Python agent
│ ├── main.py # Agent entry point
├── src/
│ ├── app/
│ │ ├── page.tsx # Main page - wires up all components
│ │ └── api/copilotkit/ # CopilotKit API route
│ ├── components/
│ │ ├── canvas/ # Todo list UI
│ │ │ ├── index.tsx # Canvas container
│ │ │ ├── todo-list.tsx # Todo list with columns
│ │ │ ├── todo-column.tsx # Column (pending/completed)
│ │ │ └── todo-card.tsx # Individual todo card
│ │ ├── example-layout/ # Layout: chat + canvas side-by-side
│ │ └── generative-ui/ # Example generative UI components
│ └── hooks/
│ ├── use-generative-ui-examples.tsx # Example CopilotKit patterns
│ └── use-example-suggestions.tsx # Chat suggestions
├── agent/ # LangGraph Python agent
│ ├── main.py # Agent entry point
│ └── src/
│ ├── todos.py # Todo tools and state schema
│ └── query.py # Example data query tool
└── mcp/ # MCP (Model Context Protocol) integration
│ ├── todos.py # Todo tools and state schema
│ └── query.py # Example data query tool
├── scripts/ # Agent setup and run scripts
│ ├── setup-agent.sh / .bat
│ └── run-agent.sh / .bat
├── package.json # Root project config (npm + concurrently)
└── next.config.ts
```
## Key Pattern: Agent State with CopilotKit v2
@@ -58,7 +60,7 @@ The todo list uses **CopilotKit v2's agent state pattern** where state lives in
1. **Agent defines state schema and tools** (Python)
```python
# apps/agent/src/todos.py
# agent/src/todos.py
class Todo(TypedDict):
id: str
title: str
@@ -78,7 +80,7 @@ The todo list uses **CopilotKit v2's agent state pattern** where state lives in
2. **Frontend reads from agent state**
```typescript
// apps/app/src/components/canvas/index.tsx
// src/components/canvas/index.tsx
const { agent } = useAgent();
return (
@@ -120,7 +122,7 @@ The todo list uses **CopilotKit v2's agent state pattern** where state lives in
### Agent Backend
**Agent Definition** (`apps/agent/main.py`):
**Agent Definition** (`agent/main.py`):
```python
from langchain.agents import create_agent
@@ -136,7 +138,7 @@ agent = create_agent(
)
```
**Todo Tools** (`apps/agent/src/todos.py`):
**Todo Tools** (`agent/src/todos.py`):
```python
@tool
@@ -161,7 +163,7 @@ def get_todos(runtime: ToolRuntime):
### Frontend
**Canvas Component** (`apps/app/src/components/canvas/index.tsx`):
**Canvas Component** (`src/components/canvas/index.tsx`):
```typescript
export function Canvas() {
@@ -182,7 +184,7 @@ export function Canvas() {
}
```
**Todo List** (`apps/app/src/components/canvas/todo-list.tsx`):
**Todo List** (`src/components/canvas/todo-list.tsx`):
```typescript
export function TodoList({ todos, onUpdate, isAgentRunning }: TodoListProps) {
@@ -225,37 +227,32 @@ export function TodoList({ todos, onUpdate, isAgentRunning }: TodoListProps) {
- **Frontend**: Next.js 16, React 19, TailwindCSS 4
- **Agent**: LangGraph (Python), OpenAI GPT-5.2
- **CopilotKit**: React hooks for agent integration (v2)
- **Monorepo**: Turborepo with pnpm workspaces
- **Other**: MCP (Model Context Protocol) integration, Recharts for generative UI examples
- **Build**: npm with concurrently for parallel dev processes
- **Other**: Recharts for generative UI examples
## Development
This is a Turborepo monorepo using pnpm workspaces.
```bash
# Install dependencies (all apps)
pnpm install
# Install dependencies (also sets up agent via postinstall)
npm install
# Start all apps (app, agent, mcp)
pnpm dev
# Start both frontend and agent
npm run dev
# Start individually
pnpm dev:app # Next.js frontend on port 3000
pnpm dev:agent # LangGraph agent on port 8123
pnpm dev:mcp # MCP server
npm run dev:ui # Next.js frontend on port 3000
npm run dev:agent # LangGraph agent on port 8123
# Build all apps
pnpm build
# Lint all apps
pnpm lint
# Build
npm run build
```
### Environment Setup
```bash
# Set OpenAI API key for the agent
echo 'OPENAI_API_KEY=your-key-here' > apps/agent/.env
# Set OpenAI API key
cp .env.example .env
# Edit .env and add your OPENAI_API_KEY
```
## Design Principles
@@ -265,18 +262,6 @@ echo 'OPENAI_API_KEY=your-key-here' > apps/agent/.env
3. **Template-first** - Code is meant to be forked and extended
4. **Showcasing agent-driven UI** - Demonstrates AI manipulating application state beyond chat
## Future Enhancements
Possible extensions to demonstrate more CopilotKit capabilities:
- Todo categories/tags/priorities
- Agent-driven task organization (auto-categorize, suggest priorities)
- Due dates and reminders
- Subtasks and dependencies
- Export/import todo lists
- Undo/redo with state history
- Real-time collaboration
---
## Key Takeaways for Developers
@@ -2,26 +2,25 @@
FROM node:20-slim AS frontend
WORKDIR /app
RUN corepack enable
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* ./
COPY apps/app/package.json ./apps/app/
RUN pnpm install --no-frozen-lockfile
COPY package.json ./
RUN npm install --ignore-scripts
COPY apps/app/ ./apps/app/
COPY src/ ./src/
COPY public/ ./public/
COPY next.config.ts tsconfig.json postcss.config.mjs ./
COPY showcase.json ./showcase.json
# Docker override: use AG-UI HttpAgent instead of LangGraphAgent
# (LangGraphAgent needs Docker-in-Docker which Railway doesn't provide)
# Docker override: replace catch-all with HttpAgent route (LangGraphAgent needs Docker-in-Docker)
# Next.js 16+ rejects both /api/copilotkit/route.ts AND /api/copilotkit/[[...slug]]/route.ts
RUN rm -f ./apps/app/src/app/api/copilotkit/\[\[...slug\]\]/route.ts
COPY docker-route-override.ts ./apps/app/src/app/api/copilotkit/route.ts
RUN pnpm --filter @repo/app add @ag-ui/client
RUN rm -f ./src/app/api/copilotkit/\[\[...slug\]\]/route.ts
COPY docker-route-override.ts ./src/app/api/copilotkit/route.ts
RUN npm install @ag-ui/client
ENV NODE_OPTIONS="--max-old-space-size=4096"
# Next.js 16+ uses Turbopack by default; use --webpack for serverExternalPackages compat
RUN pnpm --filter @repo/app exec next build --webpack
RUN npx next build --webpack
# Stage 2: Production image with Python + Node
FROM python:3.12.10-slim AS runner
@@ -41,7 +40,7 @@ WORKDIR /app
# Install Python deps — EXCLUDING langgraph-cli and langgraph-api
# (they need Docker-in-Docker which Railway doesn't provide)
# Instead serve via ag-ui-langgraph + copilotkit (same protocol, no Docker needed)
COPY apps/agent/ ./apps/agent/
COPY agent/ ./agent/
# Install ag-ui-langgraph FIRST at pinned version (before copilotkit can override it)
RUN uv pip install --system "ag-ui-langgraph[fastapi]==0.0.22" && \
uv pip install --system --no-deps "copilotkit==0.1.78" && \
@@ -61,9 +60,9 @@ RUN uv pip install --system "ag-ui-langgraph[fastapi]==0.0.22" && \
COPY serve.py ./
# Copy Next.js standalone build
COPY --from=frontend /app/apps/app/.next/standalone ./
COPY --from=frontend /app/apps/app/.next/static ./apps/app/.next/static
COPY --from=frontend /app/apps/app/public ./apps/app/public
COPY --from=frontend /app/.next/standalone ./
COPY --from=frontend /app/.next/static ./.next/static
COPY --from=frontend /app/public ./public
COPY entrypoint.sh ./
RUN chmod +x entrypoint.sh
@@ -8,33 +8,19 @@ https://github.com/user-attachments/assets/47761912-d46a-4fb3-b9bd-cb41ddd02e34
- Node.js 18+
- Python 3.8+
- Any of the following package managers:
- [pnpm](https://pnpm.io/installation) (recommended)
- npm
- [yarn](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable)
- [bun](https://bun.sh/)
- [uv](https://docs.astral.sh/uv/) (Python package manager)
- OpenAI API Key (for the LangGraph agent)
> **Note:** This repository ignores lock files (package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb) to avoid conflicts between different package managers. Each developer should generate their own lock file using their preferred package manager. After that, make sure to delete it from the .gitignore.
## Getting Started
1. Install dependencies using your preferred package manager:
1. Install dependencies:
```bash
# Using pnpm (recommended)
pnpm install
# Using npm
npm install
# Using yarn
yarn install
# Using bun
bun install
```
This will also install the Python agent dependencies via `uv sync`.
2. Set up your environment variables:
```bash
@@ -50,34 +36,47 @@ OPENAI_API_KEY=your-openai-api-key-here
3. Start the development server:
```bash
# Using pnpm
pnpm dev
# Using npm
npm run dev
# Using yarn
yarn dev
# Using bun
bun run dev
```
This will start both the UI and agent servers concurrently.
## Available Scripts
The following scripts can also be run using your preferred package manager:
- `dev` - Starts both UI and agent servers in development mode
- `dev:debug` - Starts development servers with debug logging enabled
- `dev:ui` - Starts only the Next.js UI server
- `dev:agent` - Starts only the LangGraph agent server
- `build` - Builds the Next.js application for production
- `start` - Starts the production server
- `lint` - Runs ESLint for code linting
- `install:agent` - Installs Python dependencies for the agent
## Project Structure
```
├── src/ # Next.js frontend source
│ ├── app/
│ │ ├── page.tsx # Main page
│ │ └── api/copilotkit/ # CopilotKit API route
│ ├── components/
│ │ ├── example-canvas/ # Todo list UI
│ │ ├── example-layout/ # Layout: chat + canvas side-by-side
│ │ └── generative-ui/ # Example generative UI components
│ └── hooks/
├── agent/ # LangGraph Python agent
│ ├── main.py # Agent entry point
│ └── src/
│ ├── todos.py # Todo tools and state schema
│ └── query.py # Example data query tool
├── scripts/ # Agent setup and run scripts
│ ├── setup-agent.sh / .bat
│ └── run-agent.sh / .bat
├── public/ # Static assets
├── next.config.ts
├── tsconfig.json
└── package.json
```
## A2UI — Agent-to-User Interface
This starter includes [A2UI](https://a2ui.org/specification/) support, allowing the agent to generate rich, interactive UI surfaces declaratively. Instead of returning plain text, the agent sends a JSON description of the UI it wants to render, and the frontend turns it into real components.
@@ -101,15 +100,15 @@ Both patterns use the same catalog on the frontend — the difference is where t
### Key files
| Purpose | Path |
| ------------------------------------ | ----------------------------------------------------------- |
| Catalog definitions (Zod schemas) | `apps/app/src/app/declarative-generative-ui/definitions.ts` |
| Catalog renderers (React components) | `apps/app/src/app/declarative-generative-ui/renderers.tsx` |
| Catalog registration | `apps/app/src/app/layout.tsx` |
| Fixed-schema agent tool | `apps/agent/src/a2ui_fixed_schema.py` |
| Dynamic-schema agent tool | `apps/agent/src/a2ui_dynamic_schema.py` |
| Flight schema JSON | `apps/agent/src/a2ui/schemas/flight_schema.json` |
| Showcase config | `showcase.json` |
| Purpose | Path |
| ------------------------------------ | -------------------------------------------------- |
| Catalog definitions (Zod schemas) | `src/app/declarative-generative-ui/definitions.ts` |
| Catalog renderers (React components) | `src/app/declarative-generative-ui/renderers.tsx` |
| Catalog registration | `src/app/layout.tsx` |
| Fixed-schema agent tool | `agent/src/a2ui_fixed_schema.py` |
| Dynamic-schema agent tool | `agent/src/a2ui_dynamic_schema.py` |
| Flight schema JSON | `agent/src/a2ui/schemas/flight_schema.json` |
| Showcase config | `showcase.json` |
### Adding a custom component
@@ -136,7 +135,7 @@ Both patterns use the same catalog on the frontend — the difference is where t
### Adding a new fixed-schema tool
1. Create a JSON schema file in `apps/agent/src/a2ui/schemas/` describing the component tree.
1. Create a JSON schema file in `agent/src/a2ui/schemas/` describing the component tree.
2. Create a Python tool that loads the schema with `a2ui.load_schema()` and returns `a2ui.render(operations=[...])` with your data. See `a2ui_fixed_schema.py` for the pattern.
### Showcase mode
@@ -167,7 +166,7 @@ This project is licensed under the MIT License - see the LICENSE file for detail
If you see "I'm having trouble connecting to my tools", make sure:
1. The LangGraph agent is running on port 8000
1. The LangGraph agent is running on port 8123
2. Your OpenAI API key is set correctly
3. Both servers started successfully
@@ -176,5 +175,5 @@ If you see "I'm having trouble connecting to my tools", make sure:
If you encounter Python import errors:
```bash
npm install:agent
npm run install:agent
```
@@ -6,5 +6,5 @@
"graphs": {
"sample_agent": "./main.py:graph"
},
"env": "../../.env"
"env": "../.env"
}
@@ -1,12 +0,0 @@
{
"name": "@repo/agent",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "npx @langchain/langgraph-cli dev --port 8123 --no-browser",
"postinstall": "uv sync"
},
"devDependencies": {
"@langchain/langgraph-cli": "^1.1.12"
}
}
@@ -1,40 +0,0 @@
{
"name": "@repo/app",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@copilotkit/a2ui-renderer": "https://pkg.pr.new/CopilotKit/CopilotKit/@copilotkit/a2ui-renderer@cfeb1c68c43e1c0fc00bcef91ea48b065668218b",
"@copilotkit/react-core": "https://pkg.pr.new/CopilotKit/CopilotKit/@copilotkit/react-core@cfeb1c68c43e1c0fc00bcef91ea48b065668218b",
"@copilotkit/react-ui": "https://pkg.pr.new/CopilotKit/CopilotKit/@copilotkit/react-ui@cfeb1c68c43e1c0fc00bcef91ea48b065668218b",
"@copilotkit/runtime": "https://pkg.pr.new/CopilotKit/CopilotKit/@copilotkit/runtime@cfeb1c68c43e1c0fc00bcef91ea48b065668218b",
"@copilotkit/shared": "https://pkg.pr.new/CopilotKit/CopilotKit/@copilotkit/shared@cfeb1c68c43e1c0fc00bcef91ea48b065668218b",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-separator": "^1.1.8",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"hono": "^4.12.10",
"lucide-react": "^0.577.0",
"next": "16.1.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-rnd": "^10.5.2",
"react18-json-view": "^0.2.9",
"recharts": "^3.7.0",
"tailwind-merge": "^3.5.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"tailwindcss": "^4",
"typescript": "^5"
}
}
@@ -10,8 +10,8 @@ services:
agent:
build:
context: ./apps/agent
dockerfile: ../../docker/Dockerfile.agent
context: ./agent
dockerfile: ../docker/Dockerfile.agent
environment:
- OPENAI_API_KEY=test-key-for-aimock
- OPENAI_BASE_URL=http://aimock:4010/v1
@@ -1,5 +1,5 @@
# Dockerfile for the Python LangGraph agent.
# Mirrors the user experience: uv sync + langgraph dev (same as pnpm dev:agent).
# Mirrors the user experience: uv sync + langgraph dev (same as npm run dev:agent).
FROM python:3.12-slim
RUN apt-get update && \
@@ -7,16 +7,11 @@ RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Copy package files
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .pnpmfile.cjs ./
COPY apps/app/package.json ./apps/app/
COPY turbo.json ./
COPY package.json ./
# Install dependencies
RUN pnpm install --frozen-lockfile
RUN npm install --ignore-scripts
# Build the application
FROM base AS builder
@@ -24,16 +19,12 @@ WORKDIR /app
# Copy dependencies from deps stage
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/apps/app/node_modules ./apps/app/node_modules
# Copy source code
COPY . .
# Enable pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Build the Next.js app
RUN pnpm --filter @repo/app build
RUN npx next build
# Production image
FROM base AS runner
@@ -45,9 +36,9 @@ RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copy built application
COPY --from=builder /app/apps/app/public ./apps/app/public
COPY --from=builder --chown=nextjs:nodejs /app/apps/app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/app/.next/static ./apps/app/.next/static
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
@@ -56,4 +47,4 @@ EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "apps/app/server.js"]
CMD ["node", "server.js"]
@@ -18,7 +18,7 @@ sleep 3
# Start Next.js standalone
echo "[entrypoint] Starting Next.js on port ${PORT:-3000}..."
HOSTNAME=0.0.0.0 PORT=${PORT:-3000} node apps/app/server.js 2>&1 &
HOSTNAME=0.0.0.0 PORT=${PORT:-3000} node server.js 2>&1 &
NEXT_PID=$!
echo "[entrypoint] Agent=$AGENT_PID Next=$NEXT_PID"
@@ -57,7 +57,7 @@
{
"match": {},
"response": {
"content": "You're currently running against aimock (a mock LLM server). This response is a catch-all for requests that don't match any test fixture. To use a real LLM: (1) Add your OPENAI_API_KEY to .env, (2) Remove or unset OPENAI_BASE_URL from your environment so requests go to OpenAI instead of aimock, (3) Restart with `pnpm dev`."
"content": "You're currently running against aimock (a mock LLM server). This response is a catch-all for requests that don't match any test fixture. To use a real LLM: (1) Add your OPENAI_API_KEY to .env, (2) Remove or unset OPENAI_BASE_URL from your environment so requests go to OpenAI instead of aimock, (3) Restart with `npm run dev`."
}
}
]
@@ -1,24 +1,46 @@
{
"name": "copilotkit-langgraph-template",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "turbo run dev",
"dev:app": "turbo run dev --filter='@repo/app'",
"dev:agent": "turbo run dev --filter='@repo/agent'",
"dev:mcp": "turbo run dev --filter='@repo/mcp'",
"build": "turbo run build",
"lint": "turbo run lint",
"clean": "turbo run clean",
"local-install": "bash -c 'cd ../../.. && ./node_modules/.bin/nx run-many -t build -p @copilotkit/react-core @copilotkit/react-ui @copilotkit/runtime @copilotkitnext/shared' && bash -c 'cd ../../../.. && cd ag-ui && npx -y pnpm@10 install && npx -y pnpm@10 run build' && COPILOTKIT_LOCAL=1 pnpm install && bash -c 'SRC=../../../../ag-ui/middlewares/a2ui-middleware/dist; for f in $(find ../../.. -path \"*/@ag-ui/a2ui-middleware/dist/index.js\" 2>/dev/null); do DIR=$(dirname \"$f\"); cp $SRC/index.js $SRC/index.mjs $SRC/index.d.ts $SRC/index.d.mts \"$DIR/\" 2>/dev/null; done; echo \"a2ui-middleware: all copies patched with local build\"'"
"dev": "concurrently \"npm run dev:ui\" \"npm run dev:agent\" --names ui,agent --prefix-colors blue,green --kill-others",
"dev:debug": "LOG_LEVEL=debug npm run dev",
"dev:ui": "next dev --turbopack",
"dev:agent": "./scripts/run-agent.sh || scripts/run-agent.bat",
"build": "next build",
"start": "next start",
"install:agent": "./scripts/setup-agent.sh || scripts\\setup-agent.bat",
"postinstall": "npm run install:agent"
},
"dependencies": {
"@copilotkit/a2ui-renderer": "https://pkg.pr.new/CopilotKit/CopilotKit/@copilotkit/a2ui-renderer@cfeb1c68c43e1c0fc00bcef91ea48b065668218b",
"@copilotkit/react-core": "https://pkg.pr.new/CopilotKit/CopilotKit/@copilotkit/react-core@cfeb1c68c43e1c0fc00bcef91ea48b065668218b",
"@copilotkit/react-ui": "https://pkg.pr.new/CopilotKit/CopilotKit/@copilotkit/react-ui@cfeb1c68c43e1c0fc00bcef91ea48b065668218b",
"@copilotkit/runtime": "https://pkg.pr.new/CopilotKit/CopilotKit/@copilotkit/runtime@cfeb1c68c43e1c0fc00bcef91ea48b065668218b",
"@copilotkit/shared": "https://pkg.pr.new/CopilotKit/CopilotKit/@copilotkit/shared@cfeb1c68c43e1c0fc00bcef91ea48b065668218b",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-separator": "^1.1.8",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"hono": "^4.12.10",
"lucide-react": "^0.577.0",
"next": "16.1.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-rnd": "^10.5.2",
"react18-json-view": "^0.2.9",
"recharts": "^3.7.0",
"tailwind-merge": "^3.5.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"concurrently": "^9.1.2",
"turbo": "^2.5.4"
},
"packageManager": "pnpm@9.0.0",
"pnpm": {
"overrides": {
"esbuild": "^0.27.3"
}
"tailwindcss": "^4",
"typescript": "^5"
}
}
File diff suppressed because it is too large Load Diff
@@ -1,3 +0,0 @@
packages:
- "apps/app"
- "apps/agent"

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 391 B

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 128 B

After

Width:  |  Height:  |  Size: 128 B

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 385 B

@@ -0,0 +1,3 @@
@echo off
cd /d "%~dp0..\agent"
npx @langchain/langgraph-cli dev --port 8123 --no-browser
@@ -0,0 +1,3 @@
#!/bin/bash
cd "$(dirname "$0")/../agent" || exit 1
npx @langchain/langgraph-cli dev --port 8123 --no-browser
@@ -0,0 +1,3 @@
@echo off
cd /d "%~dp0..\agent"
uv sync
@@ -0,0 +1,3 @@
#!/bin/bash
cd "$(dirname "$0")/../agent" || exit 1
uv sync
@@ -7,7 +7,7 @@ import os
import sys
# Add the agent directory to the path so imports work
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "apps", "agent"))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "agent"))
import uvicorn
from fastapi import FastAPI

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Some files were not shown because too many files have changed in this diff Show More