Skip to content

Commit c8a362b

Browse files
Merge branch 'ag-ui-protocol:main' into issue-278
2 parents 4e8140d + 77a7405 commit c8a362b

File tree

111 files changed

+3342
-1511
lines changed

Some content is hidden

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

111 files changed

+3342
-1511
lines changed

.github/workflows/dojo-e2e.yml

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
jobs:
1010
e2e:
1111
name: E2E Tests
12-
runs-on: ubuntu-latest
12+
runs-on: depot-ubuntu-latest-8
1313

1414
steps:
1515
- name: Checkout code
@@ -52,10 +52,9 @@ jobs:
5252
run: node ./scripts/prep-dojo-everything.js -e2e
5353

5454
- name: Install e2e dependencies
55-
working-directory: typescript-sdk/apps/dojo/e2e2
55+
working-directory: typescript-sdk/apps/dojo/e2e
5656
run: |
57-
pnpm install --frozen-lockfile
58-
pnpm dlx playwright install --with-deps
57+
pnpm install
5958
6059
- name: write langgraph env files
6160
working-directory: typescript-sdk/integrations/langgraph
@@ -70,21 +69,38 @@ jobs:
7069
echo "OPENAI_API_KEY=${OPENAI_API_KEY}" > python/ag_ui_langgraph/.env
7170
echo "LANGSMITH_API_KEY=${LANGSMITH_API_KEY}" >> python/ag_ui_langgraph/.env
7271
73-
- name: Run dojo+agents and tests
74-
working-directory: typescript-sdk/apps/dojo/e2e2
72+
- name: Run dojo+agents
73+
uses: JarvusInnovations/background-action@v1
7574
env:
7675
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
7776
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
78-
run: |
79-
node ../scripts/run-dojo-everything.js &
80-
npx wait-port 9999
81-
sleep 10
82-
pnpm exec playwright test --reporter=dot
77+
with:
78+
run: |
79+
node ../scripts/run-dojo-everything.js
80+
working-directory: typescript-sdk/apps/dojo/e2e
81+
wait-on: |
82+
http://localhost:9999
83+
tcp:localhost:8000
84+
tcp:localhost:8001
85+
tcp:localhost:8002
86+
tcp:localhost:8003
87+
tcp:localhost:8004
88+
tcp:localhost:8005
89+
tcp:localhost:8006
90+
tcp:localhost:8007
91+
tcp:localhost:8008
92+
tcp:localhost:8009
93+
94+
- name: Run tests
95+
working-directory: typescript-sdk/apps/dojo/e2e
96+
env:
97+
BASE_URL: http://localhost:9999
98+
run: pnpm test
8399

84100
- name: Upload traces
85101
if: always() # Uploads artifacts even if tests fail
86102
uses: actions/upload-artifact@v4
87103
with:
88104
name: playwright-traces
89-
path: typescript-sdk/apps/dojo/e2e2/test-results/
105+
path: typescript-sdk/apps/dojo/e2e/test-results/
90106
retention-days: 7

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ AG-UI was born from CopilotKit's initial partnership with LangGraph and CrewAI -
9797
| [LlamaIndex](https://github.com/run-llama/llama_index) | ✅ Supported | ➡️ [Docs](https://docs.copilotkit.ai/llamaindex) | 1st party |
9898
| [Pydantic AI](https://github.com/pydantic/pydantic-ai) | ✅ Supported | ➡️ [Docs](https://docs.copilotkit.ai/pydantic-ai) | 1st party |
9999
| [Vercel AI SDK](https://github.com/vercel/ai) | 🛠️ In Progress || Community |
100-
| [Google ADK](https://google.github.io/adk-docs/get-started/) | 🛠️ In Progress || Community |
100+
| [Google ADK](https://google.github.io/adk-docs/get-started/) | 🛠️ [PR](https://github.com/ag-ui-protocol/ag-ui/pull/274) || Community |
101101
| [OpenAI Agent SDK](https://openai.github.io/openai-agents-python/) | 🛠️ In Progress || Community |
102102
| [AWS Bedrock Agents](https://aws.amazon.com/bedrock/agents/) | 🛠️ In Progress || 1st party |
103103
| [Cloudflare Agents](https://developers.cloudflare.com/agents/) | 💡 Open to Contributions || Community |
@@ -113,7 +113,7 @@ AG-UI was born from CopilotKit's initial partnership with LangGraph and CrewAI -
113113
| [Nim]() | 🛠️ In Progress | ➡️ [PR](https://github.com/ag-ui-protocol/ag-ui/pull/29) |
114114
| [Golang]() | 🛠️ In Progress | ➡️ [Issue](https://github.com/ag-ui-protocol/ag-ui/issues/156) |
115115
| [Rust]() | 🛠️ In Progress | ➡️ [Issue](https://github.com/ag-ui-protocol/ag-ui/issues/239) |
116-
| [Java]() | 💡 Open to Contributions | ➡️ [Issue](https://github.com/ag-ui-protocol/ag-ui/issues/20) |
116+
| [Java]() | 🛠️ In Progress | ➡️ [GitHub Source](https://github.com/work-m8/ag-ui-4j) |
117117

118118

119119
[View all supported frameworks →](https://ag-ui.com/frameworks)
@@ -153,4 +153,3 @@ Check out the [AG-UI Roadmap](https://github.com/orgs/ag-ui-protocol/projects/1)
153153
## 📄 License
154154

155155
AG-UI is open source software [licensed as MIT](https://opensource.org/licenses/MIT).
156-
Maintained by [AG Protocol](https://www.agprotocol.ai).

typescript-sdk/apps/dojo/e2e/clean-reporter.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
function getTimestamp() {
2+
return (process.env.CI || process.env.VERBOSE)
3+
? new Date().toLocaleTimeString('en-US', { hour12: false })
4+
: '';
5+
}
6+
7+
function logStamp(...args) {
8+
console.log(getTimestamp(), ...args);
9+
}
10+
111
class CleanReporter {
212
onBegin(config, suite) {
313
console.log(`\n🎭 Running ${suite.allTests().length} tests...\n`);
@@ -15,9 +25,9 @@ class CleanReporter {
1525
.trim();
1626

1727
if (result.status === "passed") {
18-
console.log(`✅ ${cleanSuite}: ${testName}`);
28+
logStamp(`✅ ${cleanSuite}: ${testName}`);
1929
} else if (result.status === "failed") {
20-
console.log(`❌ ${cleanSuite}: ${testName}`);
30+
logStamp(`❌ ${cleanSuite}: ${testName}`);
2131

2232
// Extract the most relevant error info
2333
const error = result.error || result.errors?.[0];
@@ -60,15 +70,14 @@ class CleanReporter {
6070

6171
onEnd(result) {
6272
console.log("\n" + "=".repeat(60));
63-
console.log(`📊 TEST SUMMARY`);
73+
logStamp(`📊 TEST SUMMARY`);
6474
console.log("=".repeat(60));
6575

66-
console.log(`\n🔍 FAILURE ANALYSIS:`);
67-
console.log(`• Most failures appear to be AI service related`);
68-
console.log(`• Check API keys and service availability`);
69-
console.log(
70-
`• Run 'pnpm exec playwright show-report' for detailed HTML report`
71-
);
76+
if (!process.env.CI) {
77+
console.log(
78+
`• Run 'pnpm exec playwright show-report' for detailed HTML report`
79+
);
80+
}
7281

7382
console.log("=".repeat(60) + "\n");
7483
}

typescript-sdk/apps/dojo/e2e/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"private": true,
55
"description": "Scheduled Playwright smoke tests for CopilotKit demo apps",
66
"scripts": {
7+
"postinstall": "playwright install --with-deps",
78
"test": "playwright test",
89
"test:ui": "playwright test --ui"
910
},
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { Page, Locator, expect } from '@playwright/test';
2+
3+
export class ToolBaseGenUIPage {
4+
readonly page: Page;
5+
readonly haikuAgentIntro: Locator;
6+
readonly messageBox: Locator;
7+
readonly sendButton: Locator;
8+
readonly applyButton: Locator;
9+
readonly appliedButton: Locator;
10+
readonly haikuBlock: Locator;
11+
readonly japaneseLines: Locator;
12+
13+
constructor(page: Page) {
14+
this.page = page;
15+
this.haikuAgentIntro = page.getByText("I'm a haiku generator 👋. How can I help you?");
16+
this.messageBox = page.getByRole('textbox', { name: 'Type a message...' });
17+
this.sendButton = page.locator('[data-test-id="copilot-chat-ready"]');
18+
this.haikuBlock = page.locator('[data-testid="haiku-card"]');
19+
this.applyButton = page.getByRole('button', { name: 'Apply' });
20+
this.japaneseLines = page.locator('[data-testid="haiku-line"]');
21+
}
22+
23+
async generateHaiku(message: string) {
24+
await this.messageBox.click();
25+
await this.messageBox.fill(message);
26+
await this.sendButton.click();
27+
}
28+
29+
async checkGeneratedHaiku() {
30+
await this.page.locator('[data-testid="haiku-card"]').last().isVisible();
31+
const mostRecentCard = this.page.locator('[data-testid="haiku-card"]').last();
32+
await mostRecentCard.locator('[data-testid="haiku-line"]').first().waitFor({ state: 'visible', timeout: 10000 });
33+
}
34+
35+
async extractChatHaikuContent(page: Page): Promise<string> {
36+
await page.waitForTimeout(3000);
37+
await page.locator('[data-testid="haiku-card"]').first().waitFor({ state: 'visible' });
38+
const allHaikuCards = page.locator('[data-testid="haiku-card"]');
39+
const cardCount = await allHaikuCards.count();
40+
let chatHaikuContainer;
41+
let chatHaikuLines;
42+
43+
for (let cardIndex = cardCount - 1; cardIndex >= 0; cardIndex--) {
44+
chatHaikuContainer = allHaikuCards.nth(cardIndex);
45+
chatHaikuLines = chatHaikuContainer.locator('[data-testid="haiku-line"]');
46+
const linesCount = await chatHaikuLines.count();
47+
48+
if (linesCount > 0) {
49+
try {
50+
await chatHaikuLines.first().waitFor({ state: 'visible', timeout: 5000 });
51+
break;
52+
} catch (error) {
53+
continue;
54+
}
55+
}
56+
}
57+
58+
if (!chatHaikuLines) {
59+
throw new Error('No haiku cards with visible lines found');
60+
}
61+
62+
const count = await chatHaikuLines.count();
63+
const lines: string[] = [];
64+
65+
for (let i = 0; i < count; i++) {
66+
const haikuLine = chatHaikuLines.nth(i);
67+
const japaneseText = await haikuLine.locator('p').first().innerText();
68+
lines.push(japaneseText);
69+
}
70+
71+
const chatHaikuContent = lines.join('').replace(/\s/g, '');
72+
return chatHaikuContent;
73+
}
74+
75+
async extractMainDisplayHaikuContent(page: Page): Promise<string> {
76+
const mainDisplayLines = page.locator('[data-testid="main-haiku-line"]');
77+
const mainCount = await mainDisplayLines.count();
78+
const lines: string[] = [];
79+
80+
if (mainCount > 0) {
81+
for (let i = 0; i < mainCount; i++) {
82+
const haikuLine = mainDisplayLines.nth(i);
83+
const japaneseText = await haikuLine.locator('p').first().innerText();
84+
lines.push(japaneseText);
85+
}
86+
}
87+
88+
const mainHaikuContent = lines.join('').replace(/\s/g, '');
89+
return mainHaikuContent;
90+
}
91+
92+
async checkHaikuDisplay(page: Page): Promise<void> {
93+
const chatHaikuContent = await this.extractChatHaikuContent(page);
94+
95+
await page.waitForTimeout(5000);
96+
97+
const mainHaikuContent = await this.extractMainDisplayHaikuContent(page);
98+
99+
if (mainHaikuContent === '') {
100+
expect(chatHaikuContent.length).toBeGreaterThan(0);
101+
return;
102+
}
103+
104+
if (chatHaikuContent === mainHaikuContent) {
105+
expect(mainHaikuContent).toBe(chatHaikuContent);
106+
} else {
107+
await page.waitForTimeout(3000);
108+
109+
const updatedMainContent = await this.extractMainDisplayHaikuContent(page);
110+
111+
expect(updatedMainContent).toBe(chatHaikuContent);
112+
}
113+
}
114+
}

typescript-sdk/apps/dojo/e2e/pages/crewAIPages/AgenticUIGenPage.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,16 @@ export class AgenticGenUIPage {
1313
constructor(page: Page) {
1414
this.page = page;
1515
this.planTaskButton = page.getByRole('button', { name: 'Agentic Generative UI' });
16-
17-
// Remove iframe references
1816
this.chatInput = page.getByRole('textbox', { name: 'Type a message...' });
1917
this.sendButton = page.locator('[data-test-id="copilot-chat-ready"]');
2018
this.agentMessage = page.locator('.copilotKitAssistantMessage');
2119
this.userMessage = page.locator('.copilotKitUserMessage');
2220
this.agentGreeting = page.getByText('This agent demonstrates');
23-
this.agentPlannerContainer = page.locator('div.bg-gray-100.rounded-lg.w-\\[500px\\].p-4.text-black.space-y-2');
21+
this.agentPlannerContainer = page.getByTestId('task-progress');
2422
}
2523

2624
async plan() {
27-
const stepItems = this.agentPlannerContainer.locator('div.text-sm');
25+
const stepItems = this.agentPlannerContainer.getByTestId('task-step-text');
2826
const count = await stepItems.count();
2927
expect(count).toBeGreaterThan(0);
3028
for (let i = 0; i < count; i++) {
@@ -44,16 +42,14 @@ export class AgenticGenUIPage {
4442
}
4543

4644
getPlannerButton(name: string | RegExp) {
47-
// Remove iframe reference
4845
return this.page.getByRole('button', { name });
4946
}
5047

5148
async assertAgentReplyVisible(expectedText: RegExp) {
52-
await expect(this.agentMessage.getByText(expectedText)).toBeVisible();
49+
await expect(this.agentMessage.last().getByText(expectedText)).toBeVisible();
5350
}
5451

5552
async getUserText(textOrRegex) {
56-
// Remove iframe reference
5753
return await this.page.getByText(textOrRegex).isVisible();
5854
}
5955

0 commit comments

Comments
 (0)