Skip to content

Commit 7eb0c52

Browse files
committed
feat: add better data, suggestions and use smaller model for speed
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai>
1 parent c9d91f3 commit 7eb0c52

File tree

16 files changed

+157
-109
lines changed

16 files changed

+157
-109
lines changed

apps/agent/main.py

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,24 @@
33
It defines the workflow graph, state, tools, nodes and edges.
44
"""
55

6-
import asyncio
76
from langchain.agents import create_agent
87
from copilotkit import CopilotKitMiddleware
9-
from langchain_mcp_adapters.client import MultiServerMCPClient
8+
from langchain_openai import ChatOpenAI
109
from src.query import query_data
1110
from src.todos import todo_tools, AgentState
12-
13-
client = MultiServerMCPClient({
14-
"copilotkit": {
15-
"transport": "http",
16-
"url": "https://mcp.copilotkit.ai",
17-
}
18-
})
19-
20-
mcp_tools = asyncio.run(client.get_tools())
11+
from src.middleware import DisableParallelToolCallsMiddleware
2112

2213
agent = create_agent(
23-
model="gpt-5.2",
24-
tools=[query_data, *mcp_tools, *todo_tools],
25-
middleware=[CopilotKitMiddleware()],
14+
model=ChatOpenAI(model="gpt-4.1-mini"),
15+
tools=[query_data, *todo_tools],
16+
middleware=[DisableParallelToolCallsMiddleware(), CopilotKitMiddleware()],
2617
state_schema=AgentState,
2718
system_prompt=f"""
2819
You are a helpful assistant that helps users understand CopilotKit and LangGraph used together.
2920
30-
When asked about generative UI:
31-
1. Ground yourself in relevant information from the CopilotKit documentation.
32-
2. Use one of the relevant tools to demonstrate that piece of generative UI.
33-
3. Explain the concept to the user with a brief summary.
21+
Be brief in your explanations of CopilotKit and LangGraph, 1 to 2 sentences.
22+
23+
When demonstrating charts, always call the query_data tool to fetch all data from the database first.
3424
"""
3525
)
3626

apps/agent/src/db.csv

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,41 @@
1-
date,category,subcategory,amount,type
2-
2026-01-01,Revenue,Subscriptions,45000,income
3-
2026-01-01,Revenue,One-time Sales,12000,income
4-
2026-01-01,Expenses,Salaries,30000,expense
5-
2026-01-01,Expenses,Infrastructure,5000,expense
6-
2026-01-01,Expenses,Marketing,8000,expense
7-
2026-02-01,Revenue,Subscriptions,48000,income
8-
2026-02-01,Revenue,One-time Sales,15000,income
9-
2026-02-01,Expenses,Salaries,30000,expense
10-
2026-02-01,Expenses,Infrastructure,5500,expense
11-
2026-02-01,Expenses,Marketing,10000,expense
12-
2026-03-01,Revenue,Subscriptions,52000,income
13-
2026-03-01,Revenue,One-time Sales,18000,income
14-
2026-03-01,Expenses,Salaries,32000,expense
15-
2026-03-01,Expenses,Infrastructure,6000,expense
16-
2026-03-01,Expenses,Marketing,12000,expense
1+
date,category,subcategory,amount,type,notes
2+
2026-01-05,Revenue,Enterprise Subscriptions,45000,income,3 new enterprise customers (Acme Corp, TechFlow, DataViz Inc)
3+
2026-01-05,Revenue,Pro Tier Upgrades,12000,income,24 users upgraded from free to pro
4+
2026-01-08,Revenue,API Usage Overages,3500,income,High API usage from top 5 customers
5+
2026-01-10,Expenses,Engineering Salaries,42000,expense,7 engineers + 2 contractors
6+
2026-01-10,Expenses,Product Team,18000,expense,PM and 2 designers
7+
2026-01-12,Expenses,AWS Infrastructure,8200,expense,Increased compute for new AI features
8+
2026-01-15,Expenses,Marketing - Paid Ads,12000,expense,Google Ads and LinkedIn campaigns
9+
2026-01-18,Revenue,Consulting Services,8500,income,Custom integration for Acme Corp
10+
2026-01-20,Expenses,Customer Success,15000,expense,3 CSMs + support tools (Intercom)
11+
2026-01-22,Expenses,AI Model Costs,4200,expense,OpenAI API usage for product features
12+
2026-01-25,Revenue,Marketplace Sales,6800,income,Template and plugin sales
13+
2026-01-28,Expenses,Office & Equipment,3500,expense,New laptops and coworking spaces
14+
2026-02-03,Revenue,Enterprise Subscriptions,51000,income,2 new customers + expansion from TechFlow
15+
2026-02-03,Revenue,Pro Tier Upgrades,15500,income,31 upgrades + reduced churn
16+
2026-02-05,Revenue,API Usage Overages,4800,income,DataViz Inc heavy API usage spike
17+
2026-02-07,Expenses,Engineering Salaries,42000,expense,Same headcount as January
18+
2026-02-07,Expenses,Product Team,18000,expense,No changes to product team
19+
2026-02-10,Expenses,AWS Infrastructure,9500,expense,Traffic spike from viral social post
20+
2026-02-12,Expenses,Marketing - Paid Ads,15000,expense,Increased ad spend for Q1 push
21+
2026-02-14,Revenue,Consulting Services,12000,income,2 custom projects (TechFlow + new client)
22+
2026-02-18,Expenses,Customer Success,16500,expense,Hired 1 additional CSM
23+
2026-02-20,Expenses,AI Model Costs,5800,expense,Increased usage from new AI features launch
24+
2026-02-22,Revenue,Marketplace Sales,8200,income,Top template hit featured list
25+
2026-02-25,Expenses,Conference & Travel,4500,expense,Team attended SaaS Conference 2026
26+
2026-02-27,Revenue,Partnership Revenue,5500,income,Referral fees from integration partners
27+
2026-03-02,Revenue,Enterprise Subscriptions,58000,income,Major win: Fortune 500 customer signed
28+
2026-03-02,Revenue,Pro Tier Upgrades,19000,income,42 upgrades - best month yet
29+
2026-03-05,Revenue,API Usage Overages,6200,income,Consistent high usage across top tier
30+
2026-03-08,Expenses,Engineering Salaries,48000,expense,Hired 1 senior engineer for AI team
31+
2026-03-08,Expenses,Product Team,21000,expense,Promoted designer to senior level
32+
2026-03-10,Expenses,AWS Infrastructure,11000,expense,Scaled infrastructure for enterprise client
33+
2026-03-12,Expenses,Marketing - Paid Ads,18000,expense,Doubled down on successful campaigns
34+
2026-03-14,Revenue,Consulting Services,15500,income,Fortune 500 onboarding + 2 other projects
35+
2026-03-16,Expenses,Customer Success,19500,expense,Hired dedicated enterprise CSM
36+
2026-03-18,Expenses,AI Model Costs,7200,expense,Fortune 500 client heavy AI usage
37+
2026-03-20,Revenue,Marketplace Sales,9800,income,3 new templates in top 10
38+
2026-03-22,Expenses,Sales & BD,12000,expense,Hired first sales rep for enterprise
39+
2026-03-24,Revenue,Partnership Revenue,8200,income,New integration partnerships launched
40+
2026-03-26,Expenses,Security & Compliance,6500,expense,SOC 2 audit and security tools
41+
2026-03-28,Revenue,Training & Workshops,4200,income,Conducted 2 customer training sessions

apps/agent/src/middleware.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from langchain.agents.middleware import AgentMiddleware
2+
3+
# Note: This disables parallel tool calling. For smaller models, this helps improve reliability.
4+
class DisableParallelToolCallsMiddleware(AgentMiddleware):
5+
def wrap_model_call(self, request, handler):
6+
request.model_settings["parallel_tool_calls"] = False
7+
return handler(request)
8+
9+
async def awrap_model_call(self, request, handler):
10+
request.model_settings["parallel_tool_calls"] = False
11+
return await handler(request)

apps/app/src/app/globals.css

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@
55
--foreground: #171717;
66
}
77

8-
@media (prefers-color-scheme: dark) {
9-
:root {
10-
--background: #0a0a0a;
11-
--foreground: #ededed;
12-
}
8+
:root.dark {
9+
--background: #0a0a0a;
10+
--foreground: #ededed;
1311
}
1412

1513
body {

apps/app/src/app/layout.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,34 @@ import "./globals.css";
44

55
import { CopilotKit } from "@copilotkit/react-core";
66
import "@copilotkit/react-ui/v2/styles.css";
7+
import { useEffect } from "react";
78

89
export default function RootLayout({
910
children,
1011
}: Readonly<{
1112
children: React.ReactNode;
1213
}>) {
14+
useEffect(() => {
15+
// Detect system preference and apply dark mode
16+
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
17+
if (isDark) {
18+
document.documentElement.classList.add('dark');
19+
}
20+
21+
// Listen for system preference changes
22+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
23+
const handleChange = (e: MediaQueryListEvent) => {
24+
if (e.matches) {
25+
document.documentElement.classList.add('dark');
26+
} else {
27+
document.documentElement.classList.remove('dark');
28+
}
29+
};
30+
31+
mediaQuery.addEventListener('change', handleChange);
32+
return () => mediaQuery.removeEventListener('change', handleChange);
33+
}, []);
34+
1335
return (
1436
<html lang="en">
1537
<body className={`antialiased`}>

apps/app/src/components/canvas/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export function Canvas() {
77
const { agent } = useAgent();
88

99
return (
10-
<div className="h-full p-8 bg-gray-50">
10+
<div className="h-full p-8 bg-gray-50 dark:bg-black">
1111
<TodoList
1212
// read state from agent
1313
todos={agent.state?.todos || []}

apps/app/src/components/canvas/todo-card.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,18 @@ export function TodoCard({
5959

6060
return (
6161
<div
62-
className="group bg-white rounded-lg border border-gray-200 p-4 hover:shadow-md transition-all duration-200 shadow-sm"
62+
className="group bg-white dark:bg-zinc-800 rounded-lg border border-gray-200 dark:border-zinc-700 p-4 hover:shadow-md transition-all duration-200 shadow-sm"
6363
>
6464
<div className="flex items-start gap-3">
6565
{/* Checkbox toggle */}
6666
<button
6767
onClick={() => onToggleStatus(todo)}
68-
className="flex-shrink-0 w-5 h-5 rounded-full border-2 border-gray-300 hover:border-gray-600 transition-colors flex items-center justify-center mt-0.5 cursor-pointer"
68+
className="flex-shrink-0 w-5 h-5 rounded-full border-2 border-gray-300 dark:border-zinc-600 hover:border-gray-600 dark:hover:border-zinc-400 transition-colors flex items-center justify-center mt-0.5 cursor-pointer"
6969
aria-label={isCompleted ? "Mark as incomplete" : "Mark as complete"}
7070
title={isCompleted ? "Mark as incomplete" : "Mark as complete"}
7171
>
7272
{isCompleted && (
73-
<svg className="w-3 h-3 text-gray-700" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2.5">
73+
<svg className="w-3 h-3 text-gray-700 dark:text-zinc-300" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2.5">
7474
<polyline points="2,6 5,9 10,3" />
7575
</svg>
7676
)}
@@ -93,15 +93,15 @@ export function TodoCard({
9393
if (e.key === "Enter") saveEdit("title");
9494
if (e.key === "Escape") cancelEdit();
9595
}}
96-
className="w-full px-2 py-1 text-base font-semibold border-b-2 border-gray-400 focus:outline-none"
96+
className="w-full px-2 py-1 text-base font-semibold border-b-2 border-gray-400 dark:border-zinc-500 focus:outline-none bg-transparent dark:text-white"
9797
autoFocus
9898
aria-label="Edit todo title"
9999
/>
100100
) : (
101101
<div
102102
onClick={() => startEdit("title")}
103103
className={`text-base font-semibold cursor-text break-words ${
104-
isCompleted ? "line-through text-gray-400" : "text-gray-900"
104+
isCompleted ? "line-through text-gray-400 dark:text-zinc-500" : "text-gray-900 dark:text-zinc-100"
105105
}`}
106106
>
107107
{todo.title}
@@ -120,7 +120,7 @@ export function TodoCard({
120120
onKeyDown={(e) => {
121121
if (e.key === "Escape") cancelEdit();
122122
}}
123-
className="w-full px-2 py-1 text-sm text-gray-600 border border-gray-400 rounded focus:outline-none resize-none"
123+
className="w-full px-2 py-1 text-sm text-gray-600 dark:text-zinc-300 border border-gray-400 dark:border-zinc-500 rounded focus:outline-none resize-none bg-transparent"
124124
rows={3}
125125
autoFocus
126126
aria-label="Edit todo description"
@@ -129,7 +129,7 @@ export function TodoCard({
129129
<p
130130
onClick={() => startEdit("description")}
131131
className={`text-sm cursor-text ${
132-
isCompleted ? "line-through text-gray-300" : "text-gray-600"
132+
isCompleted ? "line-through text-gray-300 dark:text-zinc-600" : "text-gray-600 dark:text-zinc-300"
133133
}`}
134134
>
135135
{truncatedDescription}
@@ -141,7 +141,7 @@ export function TodoCard({
141141
{/* Delete button (visible on hover) */}
142142
<button
143143
onClick={() => onDelete(todo)}
144-
className="flex-shrink-0 opacity-0 group-hover:opacity-100 text-gray-400 hover:text-red-600 transition-all text-lg cursor-pointer"
144+
className="flex-shrink-0 opacity-0 group-hover:opacity-100 text-gray-400 dark:text-zinc-500 hover:text-red-600 dark:hover:text-red-500 transition-all text-lg cursor-pointer"
145145
aria-label="Delete todo"
146146
title="Delete todo"
147147
>

apps/app/src/components/canvas/todo-column.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ export function TodoColumn({
3939
<section aria-label={`${title} column`} className="flex-1 min-w-0">
4040
<div className="mb-6 flex items-center justify-between">
4141
<div>
42-
<h2 className="text-xl font-bold text-gray-900">{title}</h2>
43-
<p className="text-sm text-gray-500 mt-1">
42+
<h2 className="text-xl font-bold text-gray-900 dark:text-zinc-100">{title}</h2>
43+
<p className="text-sm text-gray-500 dark:text-zinc-400 mt-1">
4444
{todos.length} {todos.length === 1 ? 'task' : 'tasks'}
4545
</p>
4646
</div>
4747
{showAddButton && onAddTodo && (
4848
<button
4949
onClick={onAddTodo}
50-
className="px-2.5 py-0.5 text-black text-xl border bg-white font-medium rounded-lg hover:border-gray-500 hover:border hover:shadow-md transition-all cursor-pointer"
50+
className="px-2.5 py-0.5 text-black dark:text-white text-xl border dark:border-zinc-600 bg-white dark:bg-zinc-800 font-medium rounded-lg hover:border-gray-500 dark:hover:border-zinc-400 hover:border hover:shadow-md transition-all cursor-pointer"
5151
aria-label="Add new todo"
5252
disabled={isAgentRunning}
5353
>
@@ -57,7 +57,7 @@ export function TodoColumn({
5757
</div>
5858
<div className="space-y-6">
5959
{todos.length === 0 ? (
60-
<div role="status" className="text-center py-12 text-gray-400 text-sm">
60+
<div role="status" className="text-center py-12 text-gray-400 dark:text-zinc-500 text-sm">
6161
{emptyMessage}
6262
</div>
6363
) : (

apps/app/src/components/canvas/todo-list.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export function TodoList({ todos, onUpdate, isAgentRunning }: TodoListProps) {
6666
<div className="flex items-center justify-center h-full">
6767
<button
6868
onClick={addTodo}
69-
className="px-6 py-3 bg-gray-800 text-white font-medium rounded-lg hover:bg-gray-900 shadow-md hover:shadow-lg transition-all cursor-pointer"
69+
className="px-6 py-3 bg-gray-800 dark:bg-zinc-700 text-white font-medium rounded-lg hover:bg-gray-900 dark:hover:bg-gray-600 shadow-md hover:shadow-lg transition-all cursor-pointer"
7070
aria-label="Add your first todo task"
7171
disabled={isAgentRunning}
7272
>

apps/app/src/components/example-layout/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export function ExampleLayout({
5151
<div
5252
className={`h-full overflow-hidden ${
5353
mode === 'app'
54-
? 'w-2/3 max-lg:w-full border-l max-lg:border-l-0' // Full width on mobile
54+
? 'w-2/3 max-lg:w-full border-l dark:border-zinc-700 max-lg:border-l-0' // Full width on mobile
5555
: 'w-0 border-l-0'
5656
}`}
5757
>

0 commit comments

Comments
 (0)