Skip to content

Commit 97996a2

Browse files
Merge branch 'ag-ui-protocol:main' into adk-middleware
2 parents 3313c27 + 4bb6293 commit 97996a2

File tree

51 files changed

+6465
-680
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+6465
-680
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test('renders initial message', async ({ page }) => {
4+
await page.goto('http://localhost:9999/langgraph-fastapi/feature/agentic_chat');
5+
6+
await expect(page.getByText('Hi, I\'m an agent. Want to chat?')).toBeVisible();
7+
});
8+
9+
test('responds to user message', async ({ page }) => {
10+
await page.goto('http://localhost:9999/langgraph-fastapi/feature/agentic_chat');
11+
12+
const textarea = page.getByPlaceholder('Type a message...');
13+
textarea.fill('How many sides are in a square? Please answer in one word. Do not use any punctuation, just the number in word form.');
14+
await page.keyboard.press('Enter');
15+
16+
page.locator('.copilotKitInputControls button.copilotKitInputControlButton').click();
17+
18+
await expect(page.locator('.copilotKitMessage')).toHaveCount(3);
19+
await expect(page.locator('.copilotKitMessage.copilotKitAssistantMessage')).toHaveCount(2);
20+
await expect(page.locator('.copilotKitMessage.copilotKitUserMessage')).toHaveCount(1);
21+
await expect(page.locator('.copilotKitMessage.copilotKitAssistantMessage').last()).toHaveText('four', { ignoreCase: true });
22+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test('renders initial message', async ({ page }) => {
4+
await page.goto('http://localhost:9999/langgraph-typescript/feature/agentic_chat');
5+
6+
await expect(page.getByText('Hi, I\'m an agent. Want to chat?')).toBeVisible();
7+
});
8+
9+
test('responds to user message', async ({ page }) => {
10+
await page.goto('http://localhost:9999/langgraph-typescript/feature/agentic_chat');
11+
12+
const textarea = page.getByPlaceholder('Type a message...');
13+
textarea.fill('How many sides are in a square? Please answer in one word. Do not use any punctuation, just the number in word form.');
14+
await page.keyboard.press('Enter');
15+
16+
page.locator('.copilotKitInputControls button.copilotKitInputControlButton').click();
17+
18+
await expect(page.locator('.copilotKitMessage')).toHaveCount(3);
19+
await expect(page.locator('.copilotKitMessage.copilotKitAssistantMessage')).toHaveCount(2);
20+
await expect(page.locator('.copilotKitMessage.copilotKitUserMessage')).toHaveCount(1);
21+
await expect(page.locator('.copilotKitMessage.copilotKitAssistantMessage').last()).toHaveText('four', { ignoreCase: true });
22+
});

typescript-sdk/apps/dojo/scripts/generate-content-json.ts

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,27 @@ import path from "path";
55
function parseAgentsFile(): Array<{id: string, agentKeys: string[]}> {
66
const agentsFilePath = path.join(__dirname, '../src/agents.ts');
77
const agentsContent = fs.readFileSync(agentsFilePath, 'utf8');
8-
8+
99
const agentConfigs: Array<{id: string, agentKeys: string[]}> = [];
10-
10+
1111
// Split the content to process each agent configuration individually
1212
const agentBlocks = agentsContent.split(/(?=\s*{\s*id:\s*["'])/);
13-
13+
1414
for (const block of agentBlocks) {
1515
// Extract the ID
1616
const idMatch = block.match(/id:\s*["']([^"']+)["']/);
1717
if (!idMatch) continue;
18-
18+
1919
const id = idMatch[1];
20-
20+
2121
// Find the return object by looking for the pattern and then manually parsing balanced braces
2222
const returnMatch = block.match(/agents:\s*async\s*\(\)\s*=>\s*{\s*return\s*{/);
2323
if (!returnMatch) continue;
24-
24+
2525
const startIndex = returnMatch.index! + returnMatch[0].length;
2626
const returnObjectContent = extractBalancedBraces(block, startIndex);
27-
28-
27+
28+
2929
// Extract keys from the return object - only capture keys that are followed by a colon and then 'new'
3030
// This ensures we only get the top-level keys like "agentic_chat: new ..." not nested keys like "url: ..."
3131
const keyRegex = /^\s*(\w+):\s*new\s+\w+/gm;
@@ -34,18 +34,18 @@ function parseAgentsFile(): Array<{id: string, agentKeys: string[]}> {
3434
while ((keyMatch = keyRegex.exec(returnObjectContent)) !== null) {
3535
keys.push(keyMatch[1]);
3636
}
37-
37+
3838
agentConfigs.push({ id, agentKeys: keys });
3939
}
40-
40+
4141
return agentConfigs;
4242
}
4343

4444
// Helper function to extract content between balanced braces
4545
function extractBalancedBraces(text: string, startIndex: number): string {
4646
let braceCount = 0;
4747
let i = startIndex;
48-
48+
4949
while (i < text.length) {
5050
if (text[i] === '{') {
5151
braceCount++;
@@ -58,7 +58,7 @@ function extractBalancedBraces(text: string, startIndex: number): string {
5858
}
5959
i++;
6060
}
61-
61+
6262
return '';
6363
}
6464

@@ -71,23 +71,23 @@ async function getFile(_filePath: string | undefined, _fileName?: string) {
7171
console.warn(`File path is undefined, skipping.`);
7272
return {}
7373
}
74-
74+
7575
const fileName = _fileName ?? _filePath.split('/').pop() ?? ''
7676
const filePath = _fileName ? path.join(_filePath, fileName) : _filePath;
77-
77+
7878
// Check if it's a remote URL
7979
const isRemoteUrl = _filePath.startsWith('http://') || _filePath.startsWith('https://');
80-
80+
8181
let content: string;
82-
82+
8383
try {
8484
if (isRemoteUrl) {
8585
// Convert GitHub URLs to raw URLs for direct file access
8686
let fetchUrl = _filePath;
8787
if (_filePath.includes('github.com') && _filePath.includes('/blob/')) {
8888
fetchUrl = _filePath.replace('github.com', 'raw.githubusercontent.com').replace('/blob/', '/');
8989
}
90-
90+
9191
// Fetch remote file content
9292
console.log(`Fetching remote file: ${fetchUrl}`);
9393
const response = await fetch(fetchUrl);
@@ -177,6 +177,15 @@ const agentFilesMapper: Record<string, (agentKeys: string[]) => Record<string, s
177177
]
178178
}), {})
179179
},
180+
'langgraph-typescript': (agentKeys: string[]) => {
181+
return agentKeys.reduce((acc, agentId) => ({
182+
...acc,
183+
[agentId]: [
184+
path.join(__dirname, integrationsFolderPath, `/langgraph/examples/python/agents/${agentId}/agent.py`),
185+
path.join(__dirname, integrationsFolderPath, `/langgraph/examples/typescript/src/agents/${agentId}/agent.ts`)
186+
]
187+
}), {})
188+
},
180189
'langgraph-fastapi': (agentKeys: string[]) => {
181190
return agentKeys.reduce((acc, agentId) => ({
182191
...acc,
@@ -234,6 +243,6 @@ async function runGenerateContent() {
234243
path.join(__dirname, "../src/files.json"),
235244
JSON.stringify(result, null, 2)
236245
);
237-
246+
238247
console.log("Successfully generated src/files.json");
239248
})();

typescript-sdk/apps/dojo/scripts/prep-dojo-everything.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const serverStarterAllFeatures = {
4545

4646
// Agno
4747
const agno = {
48-
command: 'uv venv --allow-existing && uv pip install -r requirements.txt',
48+
command: 'uv sync',
4949
name: 'Agno',
5050
cwd: path.join(integrationsRoot, 'agno/examples'),
5151
}
@@ -61,7 +61,10 @@ const crewai = {
6161
const langgraphFastapi = {
6262
command: 'poetry install',
6363
name: 'LG FastAPI',
64-
cwd: path.join(integrationsRoot, 'langgraph/python/ag_ui_langgraph/examples'),
64+
cwd: path.join(integrationsRoot, 'langgraph/examples/python'),
65+
env: {
66+
POETRY_VIRTUALENVS_IN_PROJECT: "false"
67+
}
6568
}
6669

6770
// Langgraph (Platorm {typescript})
@@ -115,7 +118,7 @@ async function main() {
115118
serverStarterAllFeatures,
116119
agno,
117120
crewai,
118-
// langgraphFastapi, // Disabled until build fixes
121+
langgraphFastapi,
119122
langgraphPlatformTypescript,
120123
llamaIndex,
121124
mastra,

typescript-sdk/apps/dojo/scripts/run-dojo-everything.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const serverStarterAllFeatures = {
4545

4646
// Agno
4747
const agno = {
48-
command: 'uv run agent.py',
48+
command: 'uv run dev',
4949
name: 'Agno',
5050
cwd: path.join(integrationsRoot, 'agno/examples'),
5151
env: {PORT: 8002},
@@ -63,8 +63,11 @@ const crewai = {
6363
const langgraphFastapi = {
6464
command: 'poetry run dev',
6565
name: 'LG FastAPI',
66-
cwd: path.join(integrationsRoot, 'langgraph/python/ag_ui_langgraph/examples'),
67-
env: {PORT: 8004},
66+
cwd: path.join(integrationsRoot, 'langgraph/examples/python'),
67+
env: {
68+
PORT: 8004,
69+
POETRY_VIRTUALENVS_IN_PROJECT: "false"
70+
},
6871
}
6972

7073
// Langgraph (Platform {python})
@@ -119,10 +122,8 @@ const dojo = {
119122
AGNO_URL: 'http://localhost:8002',
120123
CREW_AI_URL: 'http://localhost:8003',
121124
LANGGRAPH_FAST_API_URL: 'http://localhost:8004',
122-
// TODO: Move this to run 2 platforms for testing.
123-
LANGGRAPH_URL: 'http://localhost:8005',
124-
// LANGGRAPH_PLATFORM_PYTHON_URL: 'http://localhost:8005',
125-
// LANGGRAPH_PLATFORM_TYPESCRIPT_URL: 'http://localhost:8006',
125+
LANGGRAPH_PYTHON_URL: 'http://localhost:8005',
126+
LANGGRAPH_TYPESCRIPT_URL: 'http://localhost:8006',
126127
LLAMA_INDEX_URL: 'http://localhost:8007',
127128
MASTRA_URL: 'http://localhost:8008',
128129
PYDANTIC_AI_URL: 'http://localhost:8009',
@@ -135,9 +136,8 @@ const procs = [
135136
serverStarterAllFeatures,
136137
agno,
137138
crewai,
138-
// langgraphFastapi, // Disabled until it runs
139+
langgraphFastapi,
139140
langgraphPlatformPython,
140-
// TODO: Also run the typescript version of langgraph.
141141
langgraphPlatformTypescript,
142142
llamaIndex,
143143
mastra,

typescript-sdk/apps/dojo/src/agents.ts

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,29 +127,32 @@ export const agentsIntegrations: AgentIntegrationConfig[] = [
127127
agents: async () => {
128128
return {
129129
agentic_chat: new LangGraphAgent({
130-
deploymentUrl: envVars.langgraphUrl,
130+
deploymentUrl: envVars.langgraphPythonUrl,
131131
graphId: "agentic_chat",
132132
}),
133133
agentic_generative_ui: new LangGraphAgent({
134-
deploymentUrl: envVars.langgraphUrl,
134+
deploymentUrl: envVars.langgraphPythonUrl,
135135
graphId: "agentic_generative_ui",
136136
}),
137137
human_in_the_loop: new LangGraphAgent({
138-
deploymentUrl: envVars.langgraphUrl,
138+
deploymentUrl: envVars.langgraphPythonUrl,
139139
graphId: "human_in_the_loop",
140140
}),
141141
predictive_state_updates: new LangGraphAgent({
142-
deploymentUrl: envVars.langgraphUrl,
142+
deploymentUrl: envVars.langgraphPythonUrl,
143143
graphId: "predictive_state_updates",
144144
}),
145145
shared_state: new LangGraphAgent({
146-
deploymentUrl: envVars.langgraphUrl,
146+
deploymentUrl: envVars.langgraphPythonUrl,
147147
graphId: "shared_state",
148148
}),
149149
tool_based_generative_ui: new LangGraphAgent({
150-
deploymentUrl: envVars.langgraphUrl,
150+
deploymentUrl: envVars.langgraphPythonUrl,
151151
graphId: "tool_based_generative_ui",
152152
}),
153+
agentic_chat_reasoning: new LangGraphHttpAgent({
154+
url: `${envVars.langgraphPythonUrl}/agent/agentic_chat_reasoning`,
155+
}),
153156
};
154157
},
155158
},
@@ -175,6 +178,40 @@ export const agentsIntegrations: AgentIntegrationConfig[] = [
175178
tool_based_generative_ui: new LangGraphHttpAgent({
176179
url: `${envVars.langgraphFastApiUrl}/agent/tool_based_generative_ui`,
177180
}),
181+
agentic_chat_reasoning: new LangGraphHttpAgent({
182+
url: `${envVars.langgraphFastApiUrl}/agent/agentic_chat_reasoning`,
183+
}),
184+
};
185+
},
186+
},
187+
{
188+
id: "langgraph-typescript",
189+
agents: async () => {
190+
return {
191+
agentic_chat: new LangGraphAgent({
192+
deploymentUrl: envVars.langgraphTypescriptUrl,
193+
graphId: "agentic_chat",
194+
}),
195+
agentic_generative_ui: new LangGraphAgent({
196+
deploymentUrl: envVars.langgraphTypescriptUrl,
197+
graphId: "agentic_generative_ui",
198+
}),
199+
human_in_the_loop: new LangGraphAgent({
200+
deploymentUrl: envVars.langgraphTypescriptUrl,
201+
graphId: "human_in_the_loop",
202+
}),
203+
predictive_state_updates: new LangGraphAgent({
204+
deploymentUrl: envVars.langgraphTypescriptUrl,
205+
graphId: "predictive_state_updates",
206+
}),
207+
shared_state: new LangGraphAgent({
208+
deploymentUrl: envVars.langgraphTypescriptUrl,
209+
graphId: "shared_state",
210+
}),
211+
tool_based_generative_ui: new LangGraphAgent({
212+
deploymentUrl: envVars.langgraphTypescriptUrl,
213+
graphId: "tool_based_generative_ui",
214+
})
178215
};
179216
},
180217
},
@@ -183,7 +220,10 @@ export const agentsIntegrations: AgentIntegrationConfig[] = [
183220
agents: async () => {
184221
return {
185222
agentic_chat: new AgnoAgent({
186-
url: `${envVars.agnoUrl}/agui`,
223+
url: `${envVars.agnoUrl}/agentic_chat/agui`,
224+
}),
225+
tool_based_generative_ui: new AgnoAgent({
226+
url: `${envVars.agnoUrl}/tool_based_generative_ui/agui`,
187227
}),
188228
};
189229
},
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# 🤖 Agentic Chat with Reasoning
2+
3+
## What This Demo Shows
4+
5+
This demo showcases CopilotKit's **agentic chat** capabilities with **frontend
6+
tool integration**:
7+
8+
1. **Natural Conversation**: Chat with your Copilot in a familiar chat interface
9+
2. **Frontend Tool Execution**: The Copilot can directly interacts with your UI
10+
by calling frontend functions
11+
3. **Seamless Integration**: Tools defined in the frontend and automatically
12+
discovered and made available to the agent
13+
14+
## How to Interact
15+
16+
Try asking your Copilot to:
17+
18+
- "Can you change the background color to something more vibrant?"
19+
- "Make the background a blue to purple gradient"
20+
- "Set the background to a sunset-themed gradient"
21+
- "Change it back to a simple light color"
22+
23+
You can also chat about other topics - the agent will respond conversationally
24+
while having the ability to use your UI tools when appropriate.
25+
26+
## ✨ Frontend Tool Integration in Action
27+
28+
**What's happening technically:**
29+
30+
- The React component defines a frontend function using `useCopilotAction`
31+
- CopilotKit automatically exposes this function to the agent
32+
- When you make a request, the agent determines whether to use the tool
33+
- The agent calls the function with the appropriate parameters
34+
- The UI immediately updates in response
35+
36+
**What you'll see in this demo:**
37+
38+
- The Copilot understands requests to change the background
39+
- It generates CSS values for colors and gradients
40+
- When it calls the tool, the background changes instantly
41+
- The agent provides a conversational response about the changes it made
42+
43+
This technique of exposing frontend functions to your Copilot can be extended to
44+
any UI manipulation you want to enable, from theme changes to data filtering,
45+
navigation, or complex UI state management!

0 commit comments

Comments
 (0)