Skip to content

Commit f32e1dc

Browse files
committed
feat: upgrade versions, add better UI, improve UX, use copilotkit sdk
Signed-off-by: Tyler Slaton <[email protected]>
1 parent 7d1316c commit f32e1dc

File tree

9 files changed

+420
-208
lines changed

9 files changed

+420
-208
lines changed

agent/main.py

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,20 @@
33
It defines the workflow graph, state, tools, nodes and edges.
44
"""
55

6-
from typing import Any, List
6+
from typing import List
77

8+
from copilotkit import CopilotKitState
89
from langchain.tools import tool
910
from langchain_core.messages import BaseMessage, SystemMessage
1011
from langchain_core.runnables import RunnableConfig
1112
from langchain_openai import ChatOpenAI
12-
from langgraph.graph import END, MessagesState, StateGraph
13+
from langgraph.graph import END, StateGraph
1314
from langgraph.prebuilt import ToolNode
1415
from langgraph.types import Command
1516

1617

17-
class AgentState(MessagesState):
18-
"""
19-
Here we define the state of the agent
20-
21-
In this instance, we're inheriting from CopilotKitState, which will bring in
22-
the CopilotKitState fields. We're also adding a custom field, `language`,
23-
which will be used to set the language of the agent.
24-
"""
25-
18+
class AgentState(CopilotKitState):
2619
proverbs: List[str]
27-
tools: List[Any]
2820
# your_custom_agent_state: str = ""
2921

3022

@@ -36,46 +28,21 @@ def get_weather(location: str):
3628
return f"The weather for {location} is 70 degrees."
3729

3830

39-
# @tool
40-
# def your_tool_here(your_arg: str):
41-
# """Your tool description here."""
42-
# print(f"Your tool logic here")
43-
# return "Your tool response here."
44-
45-
backend_tools = [
46-
get_weather
47-
# your_tool_here
48-
]
49-
5031
# Extract tool names from backend_tools for comparison
32+
backend_tools = [get_weather]
5133
backend_tool_names = [tool.name for tool in backend_tools]
5234

5335

5436
async def chat_node(state: AgentState, config: RunnableConfig) -> Command[str]:
55-
"""
56-
Standard chat node based on the ReAct design pattern. It handles:
57-
- The model to use (and binds in CopilotKit actions and the tools defined above)
58-
- The system prompt
59-
- Getting a response from the model
60-
- Handling tool calls
61-
62-
For more about the ReAct design pattern, see:
63-
https://www.perplexity.ai/search/react-agents-NcXLQhreS0WDzpVaS4m9Cg
64-
"""
65-
6637
# 1. Define the model
67-
model = ChatOpenAI(model="gpt-5-mini")
38+
model = ChatOpenAI(model="gpt-4.1-mini")
6839

6940
# 2. Bind the tools to the model
7041
model_with_tools = model.bind_tools(
7142
[
72-
*state.get("tools", []), # bind tools defined by ag-ui
43+
*state.get("copilotkit", {}).get("actions", []),
7344
*backend_tools,
74-
# your_tool_here
7545
],
76-
# 2.1 Disable parallel tool calls to avoid race conditions,
77-
# enable this for faster performance if you want to manage
78-
# the complexity of running tool calls in parallel.
7946
parallel_tool_calls=False,
8047
)
8148

agent/pyproject.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ version = "0.1.0"
44
description = "A LangGraph agent"
55
requires-python = ">=3.12"
66
dependencies = [
7-
"langchain==1.1.0",
8-
"langgraph==1.0.4",
7+
"langchain==1.2.0",
8+
"langgraph==1.0.5",
99
"langsmith>=0.4.49",
1010
"openai>=1.68.2,<2.0.0",
1111
"fastapi>=0.115.5,<1.0.0",
1212
"uvicorn>=0.29.0,<1.0.0",
1313
"python-dotenv>=1.0.0,<2.0.0",
14-
"langgraph-cli[inmem]>=0.4.7",
14+
"langgraph-cli[inmem]>=0.4.11",
1515
"langchain-openai>=1.1.0",
16+
"copilotkit>=0.1.74",
17+
"langgraph-api>=0.6.0",
1618
]

agent/uv.lock

Lines changed: 121 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@
1414
"postinstall": "npm run install:agent"
1515
},
1616
"dependencies": {
17-
"@copilotkit/react-core": "1.50.0",
18-
"@copilotkit/react-ui": "1.50.0",
19-
"@copilotkit/runtime": "1.50.0",
20-
"next": "16.0.8",
21-
"react": "^19.2.1",
22-
"react-dom": "^19.2.1",
23-
"zod": "^3.24.4"
17+
"@copilotkit/react-core": "1.50.1",
18+
"@copilotkit/react-ui": "1.50.1",
19+
"@copilotkit/runtime": "1.50.1",
20+
"next": "16.1.1",
21+
"react": "^19.2.3",
22+
"react-dom": "^19.2.3"
2423
},
2524
"devDependencies": {
2625
"@eslint/eslintrc": "^3",
@@ -31,7 +30,7 @@
3130
"@types/react-dom": "^19",
3231
"concurrently": "^9.1.2",
3332
"eslint": "^9",
34-
"eslint-config-next": "16.0.7",
33+
"eslint-config-next": "16.1.1",
3534
"tailwindcss": "^4",
3635
"typescript": "^5"
3736
}

src/app/page.tsx

Lines changed: 96 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,171 +1,146 @@
11
"use client";
22

3-
import { useCoAgent, useCopilotAction } from "@copilotkit/react-core";
3+
import { ProverbsCard } from "@/components/proverbs";
4+
import { WeatherCard } from "@/components/weather";
5+
import { MoonCard } from "@/components/moon";
6+
import { AgentState } from "@/lib/types";
7+
import {
8+
useCoAgent,
9+
useFrontendTool,
10+
useHumanInTheLoop,
11+
useRenderToolCall,
12+
} from "@copilotkit/react-core";
413
import { CopilotKitCSSProperties, CopilotSidebar } from "@copilotkit/react-ui";
514
import { useState } from "react";
615

716
export default function CopilotKitPage() {
817
const [themeColor, setThemeColor] = useState("#6366f1");
918

10-
// 🪁 Frontend Actions: https://docs.copilotkit.ai/guides/frontend-actions
11-
useCopilotAction({
19+
// 🪁 Frontend Actions: https://docs.copilotkit.ai/pydantic-ai/frontend-actions
20+
useFrontendTool({
1221
name: "setThemeColor",
13-
parameters: [{
14-
name: "themeColor",
15-
description: "The theme color to set. Make sure to pick nice colors.",
16-
required: true,
17-
}],
22+
parameters: [
23+
{
24+
name: "themeColor",
25+
description: "The theme color to set. Make sure to pick nice colors.",
26+
required: true,
27+
},
28+
],
1829
handler({ themeColor }) {
1930
setThemeColor(themeColor);
2031
},
2132
});
2233

2334
return (
24-
<main style={{ "--copilot-kit-primary-color": themeColor } as CopilotKitCSSProperties}>
25-
<YourMainContent themeColor={themeColor} />
35+
<main
36+
style={
37+
{ "--copilot-kit-primary-color": themeColor } as CopilotKitCSSProperties
38+
}
39+
>
2640
<CopilotSidebar
41+
disableSystemMessage={true}
2742
clickOutsideToClose={false}
28-
defaultOpen={true}
2943
labels={{
3044
title: "Popup Assistant",
31-
initial: "👋 Hi, there! You're chatting with an agent. This agent comes with a few tools to get you started.\n\nFor example you can try:\n- **Frontend Tools**: \"Set the theme to orange\"\n- **Shared State**: \"Write a proverb about AI\"\n- **Generative UI**: \"Get the weather in SF\"\n\nAs you interact with the agent, you'll see the UI update in real-time to reflect the agent's **state**, **tool calls**, and **progress**."
45+
initial: "👋 Hi, there! You're chatting with an agent.",
3246
}}
33-
/>
47+
suggestions={[
48+
{
49+
title: "Generative UI",
50+
message: "Get the weather in San Francisco.",
51+
},
52+
{
53+
title: "Frontend Tools",
54+
message: "Set the theme to green.",
55+
},
56+
{
57+
title: "Human In the Loop",
58+
message: "Please go to the moon.",
59+
},
60+
{
61+
title: "Write Agent State",
62+
message: "Add a proverb about AI.",
63+
},
64+
{
65+
title: "Update Agent State",
66+
message:
67+
"Please remove 1 random proverb from the list if there are any.",
68+
},
69+
{
70+
title: "Read Agent State",
71+
message: "What are the proverbs?",
72+
},
73+
]}
74+
>
75+
<YourMainContent themeColor={themeColor} />
76+
</CopilotSidebar>
3477
</main>
3578
);
3679
}
3780

38-
// State of the agent, make sure this aligns with your agent's state.
39-
type AgentState = {
40-
proverbs: string[];
41-
}
42-
4381
function YourMainContent({ themeColor }: { themeColor: string }) {
44-
// 🪁 Shared State: https://docs.copilotkit.ai/coagents/shared-state
82+
// 🪁 Shared State: https://docs.copilotkit.ai/pydantic-ai/shared-state
4583
const { state, setState } = useCoAgent<AgentState>({
4684
name: "sample_agent",
4785
initialState: {
4886
proverbs: [
4987
"CopilotKit may be new, but its the best thing since sliced bread.",
5088
],
5189
},
52-
})
90+
});
5391

5492
// 🪁 Frontend Actions: https://docs.copilotkit.ai/coagents/frontend-actions
55-
useCopilotAction({
56-
name: "addProverb",
57-
parameters: [{
58-
name: "proverb",
59-
description: "The proverb to add. Make it witty, short and concise.",
60-
required: true,
61-
}],
62-
handler: ({ proverb }) => {
93+
useFrontendTool({
94+
name: "updateProverbs",
95+
parameters: [
96+
{
97+
name: "proverbs",
98+
description: "What the current list of proverbs should be updated to",
99+
type: "string[]",
100+
required: true,
101+
},
102+
],
103+
handler: ({ proverbs }) => {
63104
setState({
64105
...state,
65-
proverbs: [...(state.proverbs || []), proverb],
106+
proverbs: proverbs,
66107
});
67108
},
68109
});
69110

70-
//🪁 Generative UI: https://docs.copilotkit.ai/coagents/generative-ui
71-
useCopilotAction({
72-
name: "get_weather",
73-
description: "Get the weather for a given location.",
74-
available: "disabled",
75-
parameters: [
76-
{ name: "location", type: "string", required: true },
77-
],
78-
render: ({ args }) => {
79-
return <WeatherCard location={args.location} themeColor={themeColor} />
111+
//🪁 Generative UI: https://docs.copilotkit.ai/pydantic-ai/generative-ui
112+
useRenderToolCall(
113+
{
114+
name: "get_weather",
115+
description: "Get the weather for a given location.",
116+
parameters: [{ name: "location", type: "string", required: true }],
117+
render: ({ args }) => {
118+
return <WeatherCard location={args.location} themeColor={themeColor} />;
119+
},
80120
},
81-
});
82-
83-
return (
84-
<div
85-
style={{ backgroundColor: themeColor }}
86-
className="h-screen w-screen flex justify-center items-center flex-col transition-colors duration-300"
87-
>
88-
<div className="bg-white/20 backdrop-blur-md p-8 rounded-2xl shadow-xl max-w-2xl w-full">
89-
<h1 className="text-4xl font-bold text-white mb-2 text-center">Proverbs</h1>
90-
<p className="text-gray-200 text-center italic mb-6">This is a demonstrative page, but it could be anything you want! 🪁</p>
91-
<hr className="border-white/20 my-6" />
92-
<div className="flex flex-col gap-3">
93-
{state.proverbs?.map((proverb, index) => (
94-
<div
95-
key={index}
96-
className="bg-white/15 p-4 rounded-xl text-white relative group hover:bg-white/20 transition-all"
97-
>
98-
<p className="pr-8">{proverb}</p>
99-
<button
100-
onClick={() => setState({
101-
...state,
102-
proverbs: (state.proverbs || []).filter((_, i) => i !== index),
103-
})}
104-
className="absolute right-3 top-3 opacity-0 group-hover:opacity-100 transition-opacity
105-
bg-red-500 hover:bg-red-600 text-white rounded-full h-6 w-6 flex items-center justify-center"
106-
>
107-
108-
</button>
109-
</div>
110-
))}
111-
</div>
112-
{state.proverbs?.length === 0 && <p className="text-center text-white/80 italic my-8">
113-
No proverbs yet. Ask the assistant to add some!
114-
</p>}
115-
</div>
116-
</div>
121+
[themeColor],
117122
);
118-
}
119123

120-
// Simple sun icon for the weather card
121-
function SunIcon() {
122-
return (
123-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-14 h-14 text-yellow-200">
124-
<circle cx="12" cy="12" r="5" />
125-
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" strokeWidth="2" stroke="currentColor" />
126-
</svg>
124+
// 🪁 Human In the Loop: https://docs.copilotkit.ai/pydantic-ai/human-in-the-loop
125+
useHumanInTheLoop(
126+
{
127+
name: "go_to_moon",
128+
description: "Go to the moon on request.",
129+
render: ({ respond, status }) => {
130+
return (
131+
<MoonCard themeColor={themeColor} status={status} respond={respond} />
132+
);
133+
},
134+
},
135+
[themeColor],
127136
);
128-
}
129137

130-
// Weather card component where the location and themeColor are based on what the agent
131-
// sets via tool calls.
132-
function WeatherCard({ location, themeColor }: { location?: string, themeColor: string }) {
133138
return (
134139
<div
135-
style={{ backgroundColor: themeColor }}
136-
className="rounded-xl shadow-xl mt-6 mb-4 max-w-md w-full"
137-
>
138-
<div className="bg-white/20 p-4 w-full">
139-
<div className="flex items-center justify-between">
140-
<div>
141-
<h3 className="text-xl font-bold text-white capitalize">{location}</h3>
142-
<p className="text-white">Current Weather</p>
143-
</div>
144-
<SunIcon />
145-
</div>
146-
147-
<div className="mt-4 flex items-end justify-between">
148-
<div className="text-3xl font-bold text-white">70°</div>
149-
<div className="text-sm text-white">Clear skies</div>
150-
</div>
151-
152-
<div className="mt-4 pt-4 border-t border-white">
153-
<div className="grid grid-cols-3 gap-2 text-center">
154-
<div>
155-
<p className="text-white text-xs">Humidity</p>
156-
<p className="text-white font-medium">45%</p>
157-
</div>
158-
<div>
159-
<p className="text-white text-xs">Wind</p>
160-
<p className="text-white font-medium">5 mph</p>
161-
</div>
162-
<div>
163-
<p className="text-white text-xs">Feels Like</p>
164-
<p className="text-white font-medium">72°</p>
165-
</div>
166-
</div>
167-
</div>
140+
style={{ backgroundColor: themeColor }}
141+
className="h-screen flex justify-center items-center flex-col transition-colors duration-300"
142+
>
143+
<ProverbsCard state={state} setState={setState} />
168144
</div>
169-
</div>
170145
);
171146
}

0 commit comments

Comments
 (0)