From 915415a260babefa2728df79807667aad2605e05 Mon Sep 17 00:00:00 2001 From: Tyler Slaton Date: Mon, 26 Jan 2026 12:30:45 -0500 Subject: [PATCH] refactor: overhaul design, UI and architecture for simplicity - Brand new mono-repo structure with Turbo - Utilize all of the latest recommendations for LangGraph for agent - Use CopilotKit's v2 UI's and hooks - Add ThreeJS example MCP App server to the mono-repo - Add .env.example - New README structure - Restructure components for progressive disclosure of complexity - Add new "canvas mode" to default UI - Include new suggestions for guiding and teaching about CopilotKit --- .env.example | 1 + .github/workflows/{smoke.yml => ci.yml} | 62 +++- .gitignore | 22 +- CLAUDE.md | 286 ++++++++++++++++++ README.md | 12 +- agent/main.py | 30 -- {agent => apps/agent}/.gitignore | 0 {agent => apps/agent}/langgraph.json | 2 +- apps/agent/main.py | 37 +++ apps/agent/package.json | 12 + {agent => apps/agent}/pyproject.toml | 5 +- apps/agent/src/db.csv | 16 + apps/agent/src/query.py | 13 + apps/agent/src/todos.py | 48 +++ {agent => apps/agent}/uv.lock | 275 +++++++++++++++-- .../app/eslint.config.mjs | 0 next.config.ts => apps/app/next.config.ts | 0 apps/app/package.json | 35 +++ .../app/postcss.config.mjs | 0 {public => apps/app/public}/file.svg | 0 {public => apps/app/public}/globe.svg | 0 {public => apps/app/public}/next.svg | 0 {public => apps/app/public}/vercel.svg | 0 {public => apps/app/public}/window.svg | 0 .../app/api/copilotkit/ag-ui-middleware.ts | 13 + apps/app/src/app/api/copilotkit/route.ts | 33 ++ {src => apps/app/src}/app/favicon.ico | Bin {src => apps/app/src}/app/globals.css | 0 apps/app/src/app/layout.tsx | 22 ++ apps/app/src/app/page.tsx | 24 ++ apps/app/src/components/canvas/index.tsx | 21 ++ apps/app/src/components/canvas/todo-card.tsx | 153 ++++++++++ .../app/src/components/canvas/todo-column.tsx | 78 +++++ apps/app/src/components/canvas/todo-list.tsx | 106 +++++++ .../src/components/example-layout/index.tsx | 64 ++++ .../components/example-layout/mode-toggle.tsx | 37 +++ .../generative-ui/charts/bar-chart.tsx | 53 ++++ .../components/generative-ui/charts/config.ts | 18 ++ .../generative-ui/charts/pie-chart.tsx | 64 ++++ .../generative-ui/meeting-time-picker.tsx | 120 ++++++++ apps/app/src/components/headless-chat.tsx | 30 ++ apps/app/src/hooks/index.ts | 2 + .../app/src/hooks/use-example-suggestions.tsx | 14 + .../src/hooks/use-generative-ui-examples.tsx | 112 +++++++ tsconfig.json => apps/app/tsconfig.json | 0 apps/mcp/.gitignore | 3 + apps/mcp/README.md | 110 +++++++ apps/mcp/grid-cell.png | Bin 0 -> 4857 bytes apps/mcp/mcp-app.html | 14 + apps/mcp/package.json | 53 ++++ apps/mcp/screenshot.png | Bin 0 -> 5647 bytes apps/mcp/server-utils.ts | 72 +++++ apps/mcp/server.ts | 246 +++++++++++++++ apps/mcp/src/global.css | 43 +++ apps/mcp/src/mcp-app-wrapper.tsx | 135 +++++++++ apps/mcp/src/threejs-app.tsx | 232 ++++++++++++++ apps/mcp/test-input.json | 3 + apps/mcp/tsconfig.json | 20 ++ apps/mcp/vite.config.ts | 25 ++ package.json | 47 +-- pnpm-workspace.yaml | 4 + scripts/setup-agent.bat | 6 - scripts/setup-agent.sh | 7 - src/app/api/copilotkit/route.ts | 35 --- src/app/layout.tsx | 26 -- src/app/page.tsx | 154 ---------- src/components/moon.tsx | 88 ------ src/components/proverbs.tsx | 18 -- src/components/weather.tsx | 53 ---- src/lib/types.ts | 4 - turbo.json | 18 ++ 71 files changed, 2733 insertions(+), 503 deletions(-) create mode 100644 .env.example rename .github/workflows/{smoke.yml => ci.yml} (76%) create mode 100644 CLAUDE.md delete mode 100644 agent/main.py rename {agent => apps/agent}/.gitignore (100%) rename {agent => apps/agent}/langgraph.json (88%) create mode 100644 apps/agent/main.py create mode 100644 apps/agent/package.json rename {agent => apps/agent}/pyproject.toml (81%) create mode 100644 apps/agent/src/db.csv create mode 100644 apps/agent/src/query.py create mode 100644 apps/agent/src/todos.py rename {agent => apps/agent}/uv.lock (88%) rename eslint.config.mjs => apps/app/eslint.config.mjs (100%) rename next.config.ts => apps/app/next.config.ts (100%) create mode 100644 apps/app/package.json rename postcss.config.mjs => apps/app/postcss.config.mjs (100%) rename {public => apps/app/public}/file.svg (100%) rename {public => apps/app/public}/globe.svg (100%) rename {public => apps/app/public}/next.svg (100%) rename {public => apps/app/public}/vercel.svg (100%) rename {public => apps/app/public}/window.svg (100%) create mode 100644 apps/app/src/app/api/copilotkit/ag-ui-middleware.ts create mode 100644 apps/app/src/app/api/copilotkit/route.ts rename {src => apps/app/src}/app/favicon.ico (100%) rename {src => apps/app/src}/app/globals.css (100%) create mode 100644 apps/app/src/app/layout.tsx create mode 100644 apps/app/src/app/page.tsx create mode 100644 apps/app/src/components/canvas/index.tsx create mode 100644 apps/app/src/components/canvas/todo-card.tsx create mode 100644 apps/app/src/components/canvas/todo-column.tsx create mode 100644 apps/app/src/components/canvas/todo-list.tsx create mode 100644 apps/app/src/components/example-layout/index.tsx create mode 100644 apps/app/src/components/example-layout/mode-toggle.tsx create mode 100644 apps/app/src/components/generative-ui/charts/bar-chart.tsx create mode 100644 apps/app/src/components/generative-ui/charts/config.ts create mode 100644 apps/app/src/components/generative-ui/charts/pie-chart.tsx create mode 100644 apps/app/src/components/generative-ui/meeting-time-picker.tsx create mode 100644 apps/app/src/components/headless-chat.tsx create mode 100644 apps/app/src/hooks/index.ts create mode 100644 apps/app/src/hooks/use-example-suggestions.tsx create mode 100644 apps/app/src/hooks/use-generative-ui-examples.tsx rename tsconfig.json => apps/app/tsconfig.json (100%) create mode 100644 apps/mcp/.gitignore create mode 100644 apps/mcp/README.md create mode 100644 apps/mcp/grid-cell.png create mode 100644 apps/mcp/mcp-app.html create mode 100644 apps/mcp/package.json create mode 100644 apps/mcp/screenshot.png create mode 100644 apps/mcp/server-utils.ts create mode 100644 apps/mcp/server.ts create mode 100644 apps/mcp/src/global.css create mode 100644 apps/mcp/src/mcp-app-wrapper.tsx create mode 100644 apps/mcp/src/threejs-app.tsx create mode 100644 apps/mcp/test-input.json create mode 100644 apps/mcp/tsconfig.json create mode 100644 apps/mcp/vite.config.ts create mode 100644 pnpm-workspace.yaml delete mode 100644 scripts/setup-agent.bat delete mode 100755 scripts/setup-agent.sh delete mode 100644 src/app/api/copilotkit/route.ts delete mode 100644 src/app/layout.tsx delete mode 100644 src/app/page.tsx delete mode 100644 src/components/moon.tsx delete mode 100644 src/components/proverbs.tsx delete mode 100644 src/components/weather.tsx delete mode 100644 src/lib/types.ts create mode 100644 turbo.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..9847a1d --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +OPENAI_API_KEY= \ No newline at end of file diff --git a/.github/workflows/smoke.yml b/.github/workflows/ci.yml similarity index 76% rename from .github/workflows/smoke.yml rename to .github/workflows/ci.yml index 2ccf24b..54f0bd1 100644 --- a/.github/workflows/smoke.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Smoke +name: CI on: push: @@ -10,13 +10,13 @@ on: jobs: smoke: - name: ${{ matrix.os }} / Node ${{ matrix.node }} / Python ${{ matrix.python }} + name: Smoke / ${{ matrix.os }} / Node ${{ matrix.node }} / Python ${{ matrix.python }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - node: [20, 22] + os: [ubuntu-latest, windows-latest] + node: [22, 24] python: [3.12, 3.13] steps: @@ -33,6 +33,9 @@ jobs: with: python-version: ${{ matrix.python }} + - name: Install pnpm + uses: pnpm/action-setup@v4 + - name: Install uv uses: astral-sh/setup-uv@v4 with: @@ -41,22 +44,20 @@ jobs: - name: Configure uv to use matrix Python version run: echo "UV_PYTHON=python${{ matrix.python }}" >> $GITHUB_ENV - - name: Install Node.js dependencies (root) - run: npm install + - name: Install dependencies (monorepo) + run: pnpm install - - name: Install Node.js dependencies (agent) - run: | - cd agent - npm install + - name: Build all apps + run: pnpm build - - name: Build frontend - run: npm run build + - name: Create empty .env file + run: touch .env - name: Test frontend startup (Linux/macOS) if: runner.os != 'Windows' run: | # Start the Next.js frontend in background - npm start & + pnpm --filter app start & FRONTEND_PID=$! # Wait for frontend to start (max 30 seconds) @@ -87,7 +88,7 @@ jobs: if: runner.os == 'Windows' run: | # Start the Next.js frontend in background - npm start & + pnpm --filter app start & # Wait for frontend to start (max 30 seconds) $timeout = 30 @@ -113,13 +114,42 @@ jobs: } shell: pwsh + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.12 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + enable-cache: true + + - name: Install dependencies + run: pnpm install + - name: Run linting - run: npm run lint + run: pnpm lint notify-slack: name: Notify Slack on Failure runs-on: ubuntu-latest - needs: smoke + needs: [smoke, lint] if: | failure() && github.event_name == 'schedule' diff --git a/.gitignore b/.gitignore index be9e14d..5645994 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies -/node_modules -/.pnp +node_modules +.pnp .pnp.* .yarn/* !.yarn/patches @@ -11,14 +11,14 @@ !.yarn/versions # testing -/coverage +coverage # next.js -/.next/ -/out/ +.next/ +out/ # production -/build +build # misc .DS_Store @@ -32,6 +32,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* +!.env.example # vercel .vercel @@ -48,3 +49,12 @@ bun.lockb # LangGraph API .langgraph_api + +# Git worktrees +.worktrees + +# Turbo +.turbo + +# Tools +.claude diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..6070bc5 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,286 @@ +# CopilotKit + LangGraph Todo Demo + +## Purpose + +This repository serves as both a **showcase** and **template** for building AI agents with CopilotKit and LangGraph. It demonstrates how CopilotKit can drive interactive UI beyond just chat, using a **collaborative todo list** as the primary example. + +**Target audience:** Developers evaluating CopilotKit or starting new projects with AI agents. + +## Core Concept + +The todo list demonstrates **agent-driven UI** where: +- The agent can manipulate application state (adding todos, updating status, organizing tasks) +- Users can interact with the same state (editing titles, checking off tasks, deleting todos) +- Both agent and user changes update the same shared state +- The UI reactively updates based on agent state changes + +This uses CopilotKit's **v2 agent state pattern** where state lives in the agent and syncs to the frontend. + +## Architecture + +This is a **Turborepo monorepo** with three apps: + +### 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/ +│ ├── todos.py # Todo tools and state schema +│ └── query.py # Example data query tool +└── mcp/ # MCP (Model Context Protocol) integration +``` + +## Key Pattern: Agent State with CopilotKit v2 + +The todo list uses **CopilotKit v2's agent state pattern** where state lives in the agent backend and syncs bidirectionally with the frontend. + +### How It Works + +1. **Agent defines state schema and tools** (Python) + ```python + # apps/agent/src/todos.py + class Todo(TypedDict): + id: str + title: str + description: str + emoji: str + status: Literal["pending", "completed"] + + class AgentState(TypedDict): + todos: list[Todo] + + @tool + def manage_todos(todos: list[Todo], runtime: ToolRuntime) -> Command: + """Manage the current todos.""" + return Command(update={"todos": todos, ...}) + ``` + +2. **Frontend reads from agent state** + ```typescript + // apps/app/src/components/canvas/index.tsx + const { agent } = useAgent(); + + return ( + agent.setState({ todos: updatedTodos })} + isAgentRunning={agent.isRunning} + /> + ); + ``` + +3. **User interactions update agent state** + ```typescript + // User clicks checkbox → frontend calls agent.setState() + const toggleStatus = (todo) => { + const updated = todos.map(t => + t.id === todo.id ? { ...t, status: t.status === "completed" ? "pending" : "completed" } : t + ); + agent.setState({ todos: updated }); + }; + ``` + +4. **Agent can manipulate state via tools** + - The agent calls `manage_todos` tool to update the todo list + - Both user and agent changes update the same `agent.state.todos` + - Frontend automatically re-renders when state changes + +### Why This Pattern? + +- **Single source of truth**: State lives in the agent, not duplicated in frontend +- **Bidirectional sync**: User changes → agent state, Agent changes → UI update +- **Simple**: No need for separate frontend state management +- **Observable**: Agent has full visibility into state changes + +## Implementation Details + +### Agent Backend + +**Agent Definition** (`apps/agent/main.py`): +```python +from langchain.agents import create_agent +from copilotkit import CopilotKitMiddleware +from src.todos import todo_tools, AgentState + +agent = create_agent( + model="gpt-5.2", + tools=[*todo_tools, ...], # manage_todos, get_todos + middleware=[CopilotKitMiddleware()], + state_schema=AgentState, # Defines state shape + system_prompt="You are a helpful assistant..." +) +``` + +**Todo Tools** (`apps/agent/src/todos.py`): +```python +@tool +def manage_todos(todos: list[Todo], runtime: ToolRuntime) -> Command: + """Manage the current todos.""" + # Ensure todos have unique IDs + for todo in todos: + if "id" not in todo or not todo["id"]: + todo["id"] = str(uuid.uuid4()) + + # Update agent state + return Command(update={ + "todos": todos, + "messages": [ToolMessage(...)] + }) + +@tool +def get_todos(runtime: ToolRuntime): + """Get the current todos.""" + return runtime.state.get("todos", []) +``` + +### Frontend + +**Canvas Component** (`apps/app/src/components/canvas/index.tsx`): +```typescript +export function Canvas() { + const { agent } = useAgent(); // CopilotKit v2 hook + + return ( +
+ agent.setState({ todos: updatedTodos })} + // React to agent execution + isAgentRunning={agent.isRunning} + /> +
+ ); +} +``` + +**Todo List** (`apps/app/src/components/canvas/todo-list.tsx`): +```typescript +export function TodoList({ todos, onUpdate, isAgentRunning }: TodoListProps) { + const toggleStatus = (todo: Todo) => { + const updated = todos.map((t) => + t.id === todo.id + ? { ...t, status: t.status === "completed" ? "pending" : "completed" } + : t + ); + onUpdate(updated); // Calls agent.setState() + }; + + const addTodo = () => { + const newTodo = { id: crypto.randomUUID(), ... }; + onUpdate([...todos, newTodo]); + }; + + return ( +
+ + +
+ ); +} +``` + +### How State Flows + +1. **User adds/edits todo** → Frontend calls `agent.setState({ todos: [...] })` +2. **Agent state updates** → CopilotKit syncs to backend +3. **Agent observes change** → Can respond via `manage_todos` tool +4. **Agent modifies todos** → Calls `manage_todos` tool +5. **State syncs to frontend** → `agent.state.todos` updates +6. **UI re-renders** → React sees new state and updates display + +**Key insight**: State lives in the agent, frontend just reads/writes to it via CopilotKit hooks. + +## Tech Stack + +- **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 + +## Development + +This is a Turborepo monorepo using pnpm workspaces. + +```bash +# Install dependencies (all apps) +pnpm install + +# Start all apps (app, agent, mcp) +pnpm 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 + +# Build all apps +pnpm build + +# Lint all apps +pnpm lint +``` + +### Environment Setup + +```bash +# Set OpenAI API key for the agent +echo 'OPENAI_API_KEY=your-key-here' > apps/agent/.env +``` + +## Design Principles + +1. **Simple over complex** - The todo list is intentionally simple and focused +2. **CopilotKit v2 patterns** - Uses modern agent state management +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 + +**State Management Pattern**: This app uses CopilotKit v2's agent state pattern where: +- State is defined in the agent backend (Python TypedDict) +- Frontend reads via `agent.state.todos` +- Frontend writes via `agent.setState({ todos: ... })` +- Agent can modify state via tools (`manage_todos`) +- Changes sync bidirectionally automatically + +**When extending this template**: +- Define state schema in the agent (`AgentState`) +- Create tools that manipulate state via `Command(update={...})` +- Use `useAgent()` hook in frontend to read/write state +- Let CopilotKit handle the sync - no manual state management needed + +This pattern works great for **agent-driven applications** where the AI needs to manipulate structured application state, not just chat. diff --git a/README.md b/README.md index c925160..18a757d 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,16 @@ yarn install bun install ``` -> **Note:** Installing the package dependencies will also install the agent's python dependencies via the `install:agent` script. +2. Set up your environment variables: +```bash +cp .env.example .env +``` + +Then edit the `.env` file and add your OpenAI API key: -2. Set up your OpenAI API key: ```bash -echo 'OPENAI_API_KEY=your-openai-api-key-here' > agent/.env +OPENAI_API_KEY=your-openai-api-key-here ``` 3. Start the development server: @@ -79,8 +83,6 @@ The main UI component is in `src/app/page.tsx`. You can: - [LangGraph Documentation](https://langchain-ai.github.io/langgraph/) - Learn more about LangGraph and its features - [CopilotKit Documentation](https://docs.copilotkit.ai) - Explore CopilotKit's capabilities -- [Next.js Documentation](https://nextjs.org/docs) - Learn about Next.js features and API -- [YFinance Documentation](https://pypi.org/project/yfinance/) - Financial data tools ## Contributing diff --git a/agent/main.py b/agent/main.py deleted file mode 100644 index f1312e8..0000000 --- a/agent/main.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -This is the main entry point for the agent. -It defines the workflow graph, state, tools, nodes and edges. -""" - -from typing import List - -from langchain.tools import tool -from langchain.agents import create_agent -from copilotkit import CopilotKitMiddleware, CopilotKitState - -@tool -def get_weather(location: str): - """ - Get the weather for a given location. - """ - return f"The weather for {location} is 70 degrees." - -class AgentState(CopilotKitState): - proverbs: List[str] - -agent = create_agent( - model="gpt-4.1-mini", - tools=[get_weather], - middleware=[CopilotKitMiddleware()], - state_schema=AgentState, - system_prompt="You are a helpful research assistant." -) - -graph = agent diff --git a/agent/.gitignore b/apps/agent/.gitignore similarity index 100% rename from agent/.gitignore rename to apps/agent/.gitignore diff --git a/agent/langgraph.json b/apps/agent/langgraph.json similarity index 88% rename from agent/langgraph.json rename to apps/agent/langgraph.json index dd21781..804b1ea 100644 --- a/agent/langgraph.json +++ b/apps/agent/langgraph.json @@ -6,5 +6,5 @@ "graphs": { "sample_agent": "./main.py:graph" }, - "env": ".env" + "env": "../../.env" } diff --git a/apps/agent/main.py b/apps/agent/main.py new file mode 100644 index 0000000..78c7f67 --- /dev/null +++ b/apps/agent/main.py @@ -0,0 +1,37 @@ +""" +This is the main entry point for the agent. +It defines the workflow graph, state, tools, nodes and edges. +""" + +import asyncio +from langchain.agents import create_agent +from copilotkit import CopilotKitMiddleware +from langchain_mcp_adapters.client import MultiServerMCPClient +from src.query import query_data +from src.todos import todo_tools, AgentState + +client = MultiServerMCPClient({ + "copilotkit": { + "transport": "http", + "url": "https://mcp.copilotkit.ai", + } +}) + +mcp_tools = asyncio.run(client.get_tools()) + +agent = create_agent( + model="gpt-5.2", + tools=[query_data, *mcp_tools, *todo_tools], + middleware=[CopilotKitMiddleware()], + state_schema=AgentState, + system_prompt=f""" + You are a helpful assistant that helps users understand CopilotKit and LangGraph used together. + + When asked about generative UI: + 1. Ground yourself in relevant information from the CopilotKit documentation. + 2. Use one of the relevant tools to demonstrate that piece of generative UI. + 3. Explain the concept to the user with a brief summary. + """ +) + +graph = agent diff --git a/apps/agent/package.json b/apps/agent/package.json new file mode 100644 index 0000000..53af393 --- /dev/null +++ b/apps/agent/package.json @@ -0,0 +1,12 @@ +{ + "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" + } +} diff --git a/agent/pyproject.toml b/apps/agent/pyproject.toml similarity index 81% rename from agent/pyproject.toml rename to apps/agent/pyproject.toml index fb0ea47..5bb21d3 100644 --- a/agent/pyproject.toml +++ b/apps/agent/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "python-dotenv>=1.0.0,<2.0.0", "langgraph-cli[inmem]>=0.4.11", "langchain-openai>=1.1.0", - "copilotkit>=0.1.74", - "langgraph-api>=0.6.0", + "copilotkit>=0.1.77", + "langgraph-api>=0.7.16", + "langchain-mcp-adapters>=0.2.1", ] diff --git a/apps/agent/src/db.csv b/apps/agent/src/db.csv new file mode 100644 index 0000000..ee25411 --- /dev/null +++ b/apps/agent/src/db.csv @@ -0,0 +1,16 @@ +date,category,subcategory,amount,type +2026-01-01,Revenue,Subscriptions,45000,income +2026-01-01,Revenue,One-time Sales,12000,income +2026-01-01,Expenses,Salaries,30000,expense +2026-01-01,Expenses,Infrastructure,5000,expense +2026-01-01,Expenses,Marketing,8000,expense +2026-02-01,Revenue,Subscriptions,48000,income +2026-02-01,Revenue,One-time Sales,15000,income +2026-02-01,Expenses,Salaries,30000,expense +2026-02-01,Expenses,Infrastructure,5500,expense +2026-02-01,Expenses,Marketing,10000,expense +2026-03-01,Revenue,Subscriptions,52000,income +2026-03-01,Revenue,One-time Sales,18000,income +2026-03-01,Expenses,Salaries,32000,expense +2026-03-01,Expenses,Infrastructure,6000,expense +2026-03-01,Expenses,Marketing,12000,expense diff --git a/apps/agent/src/query.py b/apps/agent/src/query.py new file mode 100644 index 0000000..c4a63c8 --- /dev/null +++ b/apps/agent/src/query.py @@ -0,0 +1,13 @@ +from langchain.tools import tool +from pathlib import Path +import csv + +@tool +def query_data(query: str): + """ + Query the database. Always call before showing a chart or graph. + """ + csv_path = Path(__file__).parent / "db.csv" + with open(csv_path) as f: + reader = csv.DictReader(f) + return list(reader) \ No newline at end of file diff --git a/apps/agent/src/todos.py b/apps/agent/src/todos.py new file mode 100644 index 0000000..2d10e82 --- /dev/null +++ b/apps/agent/src/todos.py @@ -0,0 +1,48 @@ +from langchain.tools import ToolRuntime, tool +from langchain.messages import ToolMessage +from langgraph.types import Command +from typing import TypedDict, Literal +import uuid + +class Todo(TypedDict): + id: str + title: str + description: str + emoji: str + status: Literal["pending", "completed"] + +class AgentState(TypedDict): + todos: list[Todo] + +@tool +def manage_todos(todos: list[Todo], runtime: ToolRuntime) -> Command: + """ + Manage the current todos. + """ + # Ensure all todos have IDs that are unique + for todo in todos: + if "id" not in todo or not todo["id"]: + todo["id"] = str(uuid.uuid4()) + + # Update the state + return Command(update={ + "todos": todos, + "messages": [ + ToolMessage( + content="Successfully updated todos", + tool_call_id=runtime.tool_call_id + ) + ] + }) + +@tool +def get_todos(runtime: ToolRuntime): + """ + Get the current todos. + """ + return runtime.state.get("todos", []) + +todo_tools = [ + manage_todos, + get_todos, +] \ No newline at end of file diff --git a/agent/uv.lock b/apps/agent/uv.lock similarity index 88% rename from agent/uv.lock rename to apps/agent/uv.lock index 0c5ad34..8991989 100644 --- a/agent/uv.lock +++ b/apps/agent/uv.lock @@ -4,7 +4,7 @@ requires-python = ">=3.12" [[package]] name = "ag-ui-langgraph" -version = "0.0.23" +version = "0.0.24" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ag-ui-protocol" }, @@ -13,9 +13,9 @@ dependencies = [ { name = "langgraph" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8b/76/beca247c88713c9ecca28d229419a1cb81644c0c7e1a4d9872baf4ad7900/ag_ui_langgraph-0.0.23.tar.gz", hash = "sha256:6f44958b996e7be4ca77533932857369e5ac038bfd60281b6a9fc28916d2af4e", size = 13032, upload-time = "2026-01-09T15:51:09.735Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/b2/b63b17f360c43db6b315f034d663faa43d0080f40a08ccc07c0c9654c291/ag_ui_langgraph-0.0.24.tar.gz", hash = "sha256:48416b22dc83aac677ad4c82a0946689e10fcb7283c33c8765c04fdf2b53f090", size = 13200, upload-time = "2026-01-28T13:50:32.793Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/0f/5f2d5bc4582ae3e9d7aab3a2db77827ab7bd2cc77381892deb58fd4fd00f/ag_ui_langgraph-0.0.23-py3-none-any.whl", hash = "sha256:d1911eb6cf28749ebc17c176de7d5bb134cc14b73cc5764005fc38ba6b76c2fb", size = 14760, upload-time = "2026-01-09T15:51:08.608Z" }, + { url = "https://files.pythonhosted.org/packages/13/30/f6f94479b80de837ad3f2f4189136365167477759f7d35a97ef254461eed/ag_ui_langgraph-0.0.24-py3-none-any.whl", hash = "sha256:d424e20d01254b58d45f99104ba368c6f304a67df471235444d0031d8579fa2c", size = 14945, upload-time = "2026-01-28T13:50:30.971Z" }, ] [package.optional-dependencies] @@ -57,6 +57,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, ] +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + [[package]] name = "blockbuster" version = "1.5.26" @@ -224,7 +233,7 @@ wheels = [ [[package]] name = "copilotkit" -version = "0.1.76" +version = "0.1.77" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ag-ui-langgraph", extra = ["fastapi"] }, @@ -234,9 +243,9 @@ dependencies = [ { name = "partialjson" }, { name = "toml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/cd/afd4cafa17d9196fb805a5a84ea2eebcef88492cd7d4df0250434eeacdc2/copilotkit-0.1.76.tar.gz", hash = "sha256:03190bf289aceac073f2524681a3c1bafbabcdd97276c697ded0aa661dbcec84", size = 36845, upload-time = "2026-01-09T16:07:55.278Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/4c/0a60cc230318f83fe2d8e217572955e5269eb0ca9b8e8357759ca40878d9/copilotkit-0.1.77.tar.gz", hash = "sha256:a3ea824e6957d844b1465f899599d714bd2db0740e6d7134dc5d55b06dbb914e", size = 36846, upload-time = "2026-01-28T13:54:19.186Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/28/57819cfe9a8a437e348ed04a3c958d4ec12fa455e52fdf35e8862323fd56/copilotkit-0.1.76-py3-none-any.whl", hash = "sha256:42d2e332afad47ce73760ce67ae6fae97df0def6d5446417150b075e1708d0ef", size = 46204, upload-time = "2026-01-09T16:07:53.855Z" }, + { url = "https://files.pythonhosted.org/packages/72/20/7982431f72a84c71daa885931a38485a842b2c32c19421f5e74a3d2156f2/copilotkit-0.1.77-py3-none-any.whl", hash = "sha256:df9c7f1dcd6c2cef17dcd52e89cdc2700d0f8587dbbf8e2572b44ef5f364e9e9", size = 46205, upload-time = "2026-01-28T13:54:17.551Z" }, ] [[package]] @@ -470,6 +479,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] +[[package]] +name = "httpx-sse" +version = "0.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, +] + [[package]] name = "idna" version = "3.11" @@ -580,6 +598,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595, upload-time = "2024-06-10T19:24:40.698Z" }, ] +[[package]] +name = "jsonschema" +version = "4.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, +] + [[package]] name = "jsonschema-rs" version = "0.29.1" @@ -602,6 +635,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/13/e8/f0ad941286cd350b879dd2b3c848deecd27f0b3fbc0ff44f2809ad59718d/jsonschema_rs-0.29.1-cp313-cp313-win_amd64.whl", hash = "sha256:1c4e5a61ac760a2fc3856a129cc84aa6f8fba7b9bc07b19fe4101050a8ecc33c", size = 1871619, upload-time = "2025-02-08T21:24:42.286Z" }, ] +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, +] + [[package]] name = "langchain" version = "1.2.0" @@ -635,6 +680,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/6f/34a9fba14d191a67f7e2ee3dbce3e9b86d2fa7310e2c7f2c713583481bd2/langchain_core-1.2.7-py3-none-any.whl", hash = "sha256:452f4fef7a3d883357b22600788d37e3d8854ef29da345b7ac7099f33c31828b", size = 490232, upload-time = "2026-01-09T17:44:24.236Z" }, ] +[[package]] +name = "langchain-mcp-adapters" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "langchain-core" }, + { name = "mcp" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/52/cebf0ef5b1acef6cbc63d671171d43af70f12d19f55577909c7afa79fb6e/langchain_mcp_adapters-0.2.1.tar.gz", hash = "sha256:58e64c44e8df29ca7eb3b656cf8c9931ef64386534d7ca261982e3bdc63f3176", size = 36394, upload-time = "2025-12-09T16:28:38.98Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/81/b2479eb26861ab36be851026d004b2d391d789b7856e44c272b12828ece0/langchain_mcp_adapters-0.2.1-py3-none-any.whl", hash = "sha256:9f96ad4c64230f6757297fec06fde19d772c99dbdfbca987f7b7cfd51ff77240", size = 22708, upload-time = "2025-12-09T16:28:37.877Z" }, +] + [[package]] name = "langchain-openai" version = "1.1.7" @@ -668,7 +727,7 @@ wheels = [ [[package]] name = "langgraph-api" -version = "0.6.35" +version = "0.7.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cloudpickle" }, @@ -683,7 +742,7 @@ dependencies = [ { name = "langgraph-checkpoint" }, { name = "langgraph-runtime-inmem" }, { name = "langgraph-sdk" }, - { name = "langsmith" }, + { name = "langsmith", extra = ["otel"] }, { name = "opentelemetry-api" }, { name = "opentelemetry-exporter-otlp-proto-http" }, { name = "opentelemetry-sdk" }, @@ -699,9 +758,9 @@ dependencies = [ { name = "uvicorn" }, { name = "watchfiles" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/92/ad/4591de9fce44229e82a4df0648278991620b0f7f3827c3a70a7b00eeb3fa/langgraph_api-0.6.35.tar.gz", hash = "sha256:b9da247163380fa63fc3db54b7c6b4b04d06323f37d5a1b3dbaaec5be69aea8d", size = 438441, upload-time = "2026-01-13T04:43:41.551Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/59/31309ac149b219213d32461c5b26ce56624f0aafa83b667c43057c709937/langgraph_api-0.7.16.tar.gz", hash = "sha256:f5f581637a855c99e7e286d777c000e4c5046284eedcbeb03c2c42cc54bde64a", size = 549679, upload-time = "2026-02-03T16:43:53.91Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/e4/fdc32d6eaf566332ee72db12a322c64388ba1e98b07c9b70bdcec192df21/langgraph_api-0.6.35-py3-none-any.whl", hash = "sha256:32233230f69db7f2753c3c8904b3dddec0357dc8c93c6a4d1ee957c4f3d0c832", size = 343479, upload-time = "2026-01-13T04:43:40.018Z" }, + { url = "https://files.pythonhosted.org/packages/90/3d/51adb80fa671fe73d99b935bde8df35d44a36ddc64c13571b0f6ceefc013/langgraph_api-0.7.16-py3-none-any.whl", hash = "sha256:b97338bbac66589f36387f1d33414bcd715838ca8f71ac3b6874cc0360690b5f", size = 478917, upload-time = "2026-02-03T16:43:52.634Z" }, ] [[package]] @@ -719,15 +778,15 @@ wheels = [ [[package]] name = "langgraph-cli" -version = "0.4.11" +version = "0.4.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "langgraph-sdk" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/2a/b5d51df0db49bf5dc8860f6f66605ff2f44da664d645b6287ceb24223df4/langgraph_cli-0.4.11.tar.gz", hash = "sha256:c38c531510ace1c2d90f8a15f4bb5b874ca9d07c0564cbda7590730da2b0dff3", size = 837429, upload-time = "2025-12-17T14:13:12.889Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/04/0764d07b1c5601cdb73f6949fab80ce867983684d024f3de48cce1d8aba2/langgraph_cli-0.4.12.tar.gz", hash = "sha256:7aa6ca37d6f60c75f2ea310f4cae233f59fe0026e5ef43dab34c04d1cb61c7a6", size = 838313, upload-time = "2026-01-23T13:34:16.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1c/ac/fd0af9c89638e4d99db8d00b6c6fe1a60f27d9d61b0bbe1d4d5300a74b18/langgraph_cli-0.4.11-py3-none-any.whl", hash = "sha256:807cd6e8c5d4fd7160bd44be67f3b5621cb34ec309658072f75852b639b41ca0", size = 41163, upload-time = "2025-12-17T14:13:12.026Z" }, + { url = "https://files.pythonhosted.org/packages/29/06/0adb15491518db7b0617bbc3f0894fbc1ffa74b1fc0c051217b1c1a538f5/langgraph_cli-0.4.12-py3-none-any.whl", hash = "sha256:ce85901b307dfa14408c6e4e122a2ccafe3abc53f6bcc99f5080715c1a0a8347", size = 41159, upload-time = "2026-01-23T13:34:14.65Z" }, ] [package.optional-dependencies] @@ -752,7 +811,7 @@ wheels = [ [[package]] name = "langgraph-runtime-inmem" -version = "0.22.0" +version = "0.23.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "blockbuster" }, @@ -762,9 +821,9 @@ dependencies = [ { name = "starlette" }, { name = "structlog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/4e/1658cfe871c2cd02013e97663cb64e734b531b3102cebbe50523f9f839ae/langgraph_runtime_inmem-0.22.0.tar.gz", hash = "sha256:8c50ccdfe2654a8524c3729d24f83705360c03b7d6a1c362584e0546abaeb32b", size = 103368, upload-time = "2026-01-08T02:03:28.315Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/25/f8ce026c9f56678ba3a0ee07456b60643be9ea46860b1b0268fe064bb1a8/langgraph_runtime_inmem-0.23.1.tar.gz", hash = "sha256:f7854bb2742dd797ae8d19a51d3b72d6218b51cb961af4fe9b437bb50710890b", size = 104659, upload-time = "2026-01-22T18:47:47.004Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/f9/09de2d2e09e122a93b4145487f1a4cd5923242ed4d3e3edfcea6fd6673cd/langgraph_runtime_inmem-0.22.0-py3-none-any.whl", hash = "sha256:46994bfebadc824e3b20374ed8ae151fa6da40eed3e43dd44c2a66d0185cb8ef", size = 37473, upload-time = "2026-01-08T02:03:27.372Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1e/db6c2a5189793b49c482449cff38ff60df757e7081c209678df4e4693d57/langgraph_runtime_inmem-0.23.1-py3-none-any.whl", hash = "sha256:79763b741d86643b28d6ea4b9b11f3189a64d1e233127339b458efd2f6f647f7", size = 39017, upload-time = "2026-01-22T18:47:45.567Z" }, ] [[package]] @@ -782,7 +841,7 @@ wheels = [ [[package]] name = "langsmith" -version = "0.6.2" +version = "0.6.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -792,11 +851,44 @@ dependencies = [ { name = "requests" }, { name = "requests-toolbelt" }, { name = "uuid-utils" }, + { name = "xxhash" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/8e/3ea7a8e9ce8c530204964207af7f7778597f5a548dc1a489c0c0940561f3/langsmith-0.6.2.tar.gz", hash = "sha256:c2efd7ed61eed3b6fdbf158ea2e9862bc2636f2edc95e90d2faad9462773d097", size = 1739277, upload-time = "2026-01-08T23:17:40.504Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/15/35f49a0b2efd33002fdcb9a7b0bdb65d77e40b4739104ffe843a3479874a/langsmith-0.6.8.tar.gz", hash = "sha256:3a7eb7155f2839dc729a5aa5b0bfc4aa1cb617b09a2290cf77031041271a7cdf", size = 973475, upload-time = "2026-02-02T23:20:02.208Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/2d/2389e65522ebeab17489df72b4fabcfc661fced8af178aa6c2bc3b9afff5/langsmith-0.6.8-py3-none-any.whl", hash = "sha256:d17da18aeef15fdb4c3baec348bad64056591d785629cd5ba4846fd93cab166b", size = 319165, upload-time = "2026-02-02T23:20:00.456Z" }, +] + +[package.optional-dependencies] +otel = [ + { name = "opentelemetry-api" }, + { name = "opentelemetry-exporter-otlp-proto-http" }, + { name = "opentelemetry-sdk" }, +] + +[[package]] +name = "mcp" +version = "1.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/e0/9d173dd2fa7f85d9ec4989f6f5a1a057d281daa8dada0ff8db0de0cb68aa/langsmith-0.6.2-py3-none-any.whl", hash = "sha256:1ea1a591f52683a5aeebdaa2b58458d72ce9598105dd8b29e16f7373631a6434", size = 282918, upload-time = "2026-01-08T23:17:38.858Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, ] [[package]] @@ -1119,6 +1211,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, ] +[[package]] +name = "pydantic-settings" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, +] + [[package]] name = "pyjwt" version = "2.10.1" @@ -1128,6 +1234,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, ] +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + [[package]] name = "python-dotenv" version = "1.2.1" @@ -1137,6 +1248,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, ] +[[package]] +name = "python-multipart" +version = "0.0.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, +] + +[[package]] +name = "pywin32" +version = "311" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, + { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, + { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, + { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, + { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, + { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, + { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -1183,6 +1319,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] +[[package]] +name = "referencing" +version = "0.37.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, +] + [[package]] name = "regex" version = "2025.11.3" @@ -1288,6 +1438,87 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, ] +[[package]] +name = "rpds-py" +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, +] + [[package]] name = "sample-agent" version = "0.1.0" @@ -1296,6 +1527,7 @@ dependencies = [ { name = "copilotkit" }, { name = "fastapi" }, { name = "langchain" }, + { name = "langchain-mcp-adapters" }, { name = "langchain-openai" }, { name = "langgraph" }, { name = "langgraph-api" }, @@ -1308,12 +1540,13 @@ dependencies = [ [package.metadata] requires-dist = [ - { name = "copilotkit", specifier = ">=0.1.74" }, + { name = "copilotkit", specifier = ">=0.1.77" }, { name = "fastapi", specifier = ">=0.115.5,<1.0.0" }, { name = "langchain", specifier = "==1.2.0" }, + { name = "langchain-mcp-adapters", specifier = ">=0.2.1" }, { name = "langchain-openai", specifier = ">=1.1.0" }, { name = "langgraph", specifier = "==1.0.5" }, - { name = "langgraph-api", specifier = ">=0.6.0" }, + { name = "langgraph-api", specifier = ">=0.7.16" }, { name = "langgraph-cli", extras = ["inmem"], specifier = ">=0.4.11" }, { name = "langsmith", specifier = ">=0.4.49" }, { name = "openai", specifier = ">=1.68.2,<2.0.0" }, diff --git a/eslint.config.mjs b/apps/app/eslint.config.mjs similarity index 100% rename from eslint.config.mjs rename to apps/app/eslint.config.mjs diff --git a/next.config.ts b/apps/app/next.config.ts similarity index 100% rename from next.config.ts rename to apps/app/next.config.ts diff --git a/apps/app/package.json b/apps/app/package.json new file mode 100644 index 0000000..6ac221b --- /dev/null +++ b/apps/app/package.json @@ -0,0 +1,35 @@ +{ + "name": "@repo/app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build", + "start": "next start", + "lint": "eslint ." + }, + "dependencies": { + "@ag-ui/mcp-apps-middleware": "^0.0.3", + "@copilotkit/react-core": "1.51.4-next.1", + "@copilotkit/react-ui": "1.51.4-next.1", + "@copilotkit/runtime": "1.51.4-next.1", + "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" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.1.6", + "tailwindcss": "^4", + "typescript": "^5", + "zod": "^3.23.8" + } +} diff --git a/postcss.config.mjs b/apps/app/postcss.config.mjs similarity index 100% rename from postcss.config.mjs rename to apps/app/postcss.config.mjs diff --git a/public/file.svg b/apps/app/public/file.svg similarity index 100% rename from public/file.svg rename to apps/app/public/file.svg diff --git a/public/globe.svg b/apps/app/public/globe.svg similarity index 100% rename from public/globe.svg rename to apps/app/public/globe.svg diff --git a/public/next.svg b/apps/app/public/next.svg similarity index 100% rename from public/next.svg rename to apps/app/public/next.svg diff --git a/public/vercel.svg b/apps/app/public/vercel.svg similarity index 100% rename from public/vercel.svg rename to apps/app/public/vercel.svg diff --git a/public/window.svg b/apps/app/public/window.svg similarity index 100% rename from public/window.svg rename to apps/app/public/window.svg diff --git a/apps/app/src/app/api/copilotkit/ag-ui-middleware.ts b/apps/app/src/app/api/copilotkit/ag-ui-middleware.ts new file mode 100644 index 0000000..1685ded --- /dev/null +++ b/apps/app/src/app/api/copilotkit/ag-ui-middleware.ts @@ -0,0 +1,13 @@ +import { MCPAppsMiddleware } from "@ag-ui/mcp-apps-middleware"; + +export const aguiMiddleware = [ + new MCPAppsMiddleware({ + mcpServers: [ + { + type: "http", + url: "http://localhost:3108/mcp", + serverId: "example_mcp_app", + }, + ], + }), +]; \ No newline at end of file diff --git a/apps/app/src/app/api/copilotkit/route.ts b/apps/app/src/app/api/copilotkit/route.ts new file mode 100644 index 0000000..502f950 --- /dev/null +++ b/apps/app/src/app/api/copilotkit/route.ts @@ -0,0 +1,33 @@ +import { + CopilotRuntime, + ExperimentalEmptyAdapter, + copilotRuntimeNextJSAppRouterEndpoint, +} from "@copilotkit/runtime"; +import { LangGraphAgent } from "@copilotkit/runtime/langgraph"; +import { NextRequest } from "next/server"; +import { aguiMiddleware } from "@/app/api/copilotkit/ag-ui-middleware"; + +// 1. Define the agent connection to LangGraph +const defaultAgent = new LangGraphAgent({ + deploymentUrl: process.env.LANGGRAPH_DEPLOYMENT_URL || "http://localhost:8123", + graphId: "sample_agent", + langsmithApiKey: process.env.LANGSMITH_API_KEY || "", +}); + +// 2. Bind in middleware to the agent. For A2UI and MCP Apps. +defaultAgent.use(...aguiMiddleware) + +// 3. Define the route and CopilotRuntime for the agent +export const POST = async (req: NextRequest) => { + const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({ + endpoint: "/api/copilotkit", + serviceAdapter: new ExperimentalEmptyAdapter(), + runtime: new CopilotRuntime({ + agents: { + default: defaultAgent, + }, + }), + }); + + return handleRequest(req); +}; diff --git a/src/app/favicon.ico b/apps/app/src/app/favicon.ico similarity index 100% rename from src/app/favicon.ico rename to apps/app/src/app/favicon.ico diff --git a/src/app/globals.css b/apps/app/src/app/globals.css similarity index 100% rename from src/app/globals.css rename to apps/app/src/app/globals.css diff --git a/apps/app/src/app/layout.tsx b/apps/app/src/app/layout.tsx new file mode 100644 index 0000000..6917a03 --- /dev/null +++ b/apps/app/src/app/layout.tsx @@ -0,0 +1,22 @@ +"use client"; + +import "./globals.css"; + +import { CopilotKit } from "@copilotkit/react-core"; +import "@copilotkit/react-ui/v2/styles.css"; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + {children} + + + + ); +} diff --git a/apps/app/src/app/page.tsx b/apps/app/src/app/page.tsx new file mode 100644 index 0000000..514277c --- /dev/null +++ b/apps/app/src/app/page.tsx @@ -0,0 +1,24 @@ +"use client"; + +import { ExampleLayout } from "@/components/example-layout"; +import { Canvas } from "@/components/canvas"; +import { useGenerativeUIExamples, useExampleSuggestions } from "@/hooks"; + +import { CopilotChat } from "@copilotkit/react-core/v2"; +// import { HeadlessChat } from "@/components/headless-chat"; + +export default function HomePage() { + // 🪁 Generative UI Examples + useGenerativeUIExamples(); + + // 🪁 Example Suggestions + useExampleSuggestions(); + + return ( + } + // chatContent={} + appContent={} + /> + ); +} \ No newline at end of file diff --git a/apps/app/src/components/canvas/index.tsx b/apps/app/src/components/canvas/index.tsx new file mode 100644 index 0000000..9ab34fe --- /dev/null +++ b/apps/app/src/components/canvas/index.tsx @@ -0,0 +1,21 @@ +"use client"; + +import { useAgent } from "@copilotkit/react-core/v2"; +import { TodoList } from "./todo-list"; + +export function Canvas() { + const { agent } = useAgent(); + + return ( +
+ agent.setState({ todos: updatedTodos })} + // change UI based on agent execution + isAgentRunning={agent.isRunning} + /> +
+ ); +} diff --git a/apps/app/src/components/canvas/todo-card.tsx b/apps/app/src/components/canvas/todo-card.tsx new file mode 100644 index 0000000..3088147 --- /dev/null +++ b/apps/app/src/components/canvas/todo-card.tsx @@ -0,0 +1,153 @@ +"use client"; + +import { useState } from "react"; + +interface Todo { + id: string; + title: string; + description: string; + emoji: string; + status: "pending" | "completed"; +} + +interface TodoCardProps { + todo: Todo; + onToggleStatus: (todo: Todo) => void; + onDelete: (todo: Todo) => void; + onUpdateTitle: (todoId: string, title: string) => void; + onUpdateDescription: (todoId: string, description: string) => void; +} + +export function TodoCard({ + todo, + onToggleStatus, + onDelete, + onUpdateTitle, + onUpdateDescription, +}: TodoCardProps) { + const [editingField, setEditingField] = useState<"title" | "description" | null>(null); + const [editValue, setEditValue] = useState(""); + + const isEditingTitle = editingField === "title"; + const isEditingDescription = editingField === "description"; + const isCompleted = todo.status === "completed"; + const truncatedDescription = todo.description.length > 100 + ? todo.description.slice(0, 100) + "..." + : todo.description; + + const startEdit = (field: "title" | "description") => { + setEditingField(field); + setEditValue(field === "title" ? todo.title : todo.description); + }; + + const saveEdit = (field: "title" | "description") => { + if (editValue.trim()) { + if (field === "title") { + onUpdateTitle(todo.id, editValue.trim()); + } else { + onUpdateDescription(todo.id, editValue.trim()); + } + } + setEditingField(null); + setEditValue(""); + }; + + const cancelEdit = () => { + setEditingField(null); + setEditValue(""); + }; + + return ( +
+
+ {/* Checkbox toggle */} + + +
+
+ {/* Emoji */} + {todo.emoji} + + {/* Title (editable) */} +
+ {isEditingTitle ? ( + setEditValue(e.target.value)} + onBlur={() => saveEdit("title")} + onKeyDown={(e) => { + if (e.key === "Enter") saveEdit("title"); + if (e.key === "Escape") cancelEdit(); + }} + className="w-full px-2 py-1 text-base font-semibold border-b-2 border-gray-400 focus:outline-none" + autoFocus + aria-label="Edit todo title" + /> + ) : ( +
startEdit("title")} + className={`text-base font-semibold cursor-text break-words ${ + isCompleted ? "line-through text-gray-400" : "text-gray-900" + }`} + > + {todo.title} +
+ )} +
+
+ + {/* Description (editable, truncated) */} +
+ {isEditingDescription ? ( +