Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions typescript-sdk/apps/dojo/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {}
}
113 changes: 80 additions & 33 deletions typescript-sdk/apps/dojo/e2e/featurePages/ToolBaseGenUIPage.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,65 @@
import { Page, Locator, expect } from '@playwright/test';
import { Page, Locator, expect } from "@playwright/test";

export class ToolBaseGenUIPage {
readonly page: Page;
readonly haikuAgentIntro: Locator;
readonly messageBox: Locator;
readonly sendButton: Locator;
readonly applyButton: Locator;
readonly appliedButton: Locator;
readonly haikuBlock: Locator;
readonly japaneseLines: Locator;
readonly mainHaikuDisplay: Locator;

constructor(page: Page) {
this.page = page;
this.haikuAgentIntro = page.getByText("I'm a haiku generator 👋. How can I help you?");
this.messageBox = page.getByRole('textbox', { name: 'Type a message...' });
this.sendButton = page.locator('[data-test-id="copilot-chat-ready"]');
this.haikuAgentIntro = page.getByText("I'm a haiku generator 👋. How can I help you?").first();
this.messageBox = page.getByPlaceholder("Type a message...").first();
this.sendButton = page.locator('[data-test-id="copilot-chat-ready"]').first();
this.haikuBlock = page.locator('[data-testid="haiku-card"]');
this.applyButton = page.getByRole('button', { name: 'Apply' });
this.japaneseLines = page.locator('[data-testid="haiku-line"]');
this.applyButton = page.getByRole("button", { name: "Apply" });
this.japaneseLines = page.locator('[data-testid="haiku-japanese-line"]');
this.mainHaikuDisplay = page.locator('[data-testid="haiku-carousel"]');
}

async generateHaiku(message: string) {
// Wait for either sidebar or popup to be ready
await this.page.waitForTimeout(2000);
await this.messageBox.waitFor({ state: "visible", timeout: 15000 });
await this.messageBox.click();
await this.messageBox.fill(message);
await this.page.waitForTimeout(1000);
await this.sendButton.waitFor({ state: "visible", timeout: 15000 });
await this.sendButton.click();
await this.page.waitForTimeout(2000);
}

async checkGeneratedHaiku() {
await this.page.locator('[data-testid="haiku-card"]').last().isVisible();
const mostRecentCard = this.page.locator('[data-testid="haiku-card"]').last();
await mostRecentCard.locator('[data-testid="haiku-line"]').first().waitFor({ state: 'visible', timeout: 10000 });
await this.page.waitForTimeout(3000);
const cards = this.page.locator('[data-testid="haiku-card"]');
await cards.last().waitFor({ state: "visible", timeout: 20000 });
const mostRecentCard = cards.last();
await mostRecentCard
.locator('[data-testid="haiku-japanese-line"]')
.first()
.waitFor({ state: "visible", timeout: 20000 });

Check failure on line 44 in typescript-sdk/apps/dojo/e2e/featurePages/ToolBaseGenUIPage.ts

View workflow job for this annotation

GitHub Actions / adk-middleware

[chromium] › tests/adkMiddlewareTests/toolBasedGenUIPage.spec.ts:20:7 › Tool Based Generative UI Feature › [ADK Middleware] Haiku generation and UI consistency for two different prompts

2) [chromium] › tests/adkMiddlewareTests/toolBasedGenUIPage.spec.ts:20:7 › Tool Based Generative UI Feature › [ADK Middleware] Haiku generation and UI consistency for two different prompts Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── TimeoutError: locator.waitFor: Timeout 20000ms exceeded. Call log: - waiting for locator('[data-testid="haiku-card"]').last().locator('[data-testid="haiku-japanese-line"]').first() to be visible at ../featurePages/ToolBaseGenUIPage.ts:44 42 | .locator('[data-testid="haiku-japanese-line"]') 43 | .first() > 44 | .waitFor({ state: "visible", timeout: 20000 }); | ^ 45 | } 46 | 47 | async extractChatHaikuContent(page: Page): Promise<string> { at ToolBaseGenUIPage.checkGeneratedHaiku (/home/runner/work/ag-ui/ag-ui/typescript-sdk/apps/dojo/e2e/featurePages/ToolBaseGenUIPage.ts:44:8) at /home/runner/work/ag-ui/ag-ui/typescript-sdk/apps/dojo/e2e/tests/adkMiddlewareTests/toolBasedGenUIPage.spec.ts:36:5
}

async extractChatHaikuContent(page: Page): Promise<string> {
await page.waitForTimeout(3000);
await page.locator('[data-testid="haiku-card"]').first().waitFor({ state: 'visible' });
await page.waitForTimeout(4000);
const allHaikuCards = page.locator('[data-testid="haiku-card"]');
await allHaikuCards.first().waitFor({ state: "visible", timeout: 15000 });
const cardCount = await allHaikuCards.count();
let chatHaikuContainer;
let chatHaikuLines;

for (let cardIndex = cardCount - 1; cardIndex >= 0; cardIndex--) {
chatHaikuContainer = allHaikuCards.nth(cardIndex);
chatHaikuLines = chatHaikuContainer.locator('[data-testid="haiku-line"]');
chatHaikuLines = chatHaikuContainer.locator('[data-testid="haiku-japanese-line"]');
const linesCount = await chatHaikuLines.count();

if (linesCount > 0) {
try {
await chatHaikuLines.first().waitFor({ state: 'visible', timeout: 5000 });
await chatHaikuLines.first().waitFor({ state: "visible", timeout: 8000 });
break;
} catch (error) {
continue;
Expand All @@ -56,59 +68,94 @@
}

if (!chatHaikuLines) {
throw new Error('No haiku cards with visible lines found');
throw new Error("No haiku cards with visible lines found");
}

const count = await chatHaikuLines.count();
const lines: string[] = [];

for (let i = 0; i < count; i++) {
const haikuLine = chatHaikuLines.nth(i);
const japaneseText = await haikuLine.locator('p').first().innerText();
const japaneseText = await haikuLine.innerText();
lines.push(japaneseText);
}

const chatHaikuContent = lines.join('').replace(/\s/g, '');
const chatHaikuContent = lines.join("").replace(/\s/g, "");
return chatHaikuContent;
}

async extractMainDisplayHaikuContent(page: Page): Promise<string> {
const mainDisplayLines = page.locator('[data-testid="main-haiku-line"]');
await page.waitForTimeout(2000);
const carousel = page.locator('[data-testid="haiku-carousel"]');
await carousel.waitFor({ state: "visible", timeout: 10000 });

// Find the visible carousel item (the active slide)
const carouselItems = carousel.locator('[data-testid^="carousel-item-"]');
const itemCount = await carouselItems.count();
let activeCard = null;

// Find the visible/active carousel item
for (let i = 0; i < itemCount; i++) {
const item = carouselItems.nth(i);
const isVisible = await item.isVisible();
if (isVisible) {
activeCard = item.locator('[data-testid="haiku-card"]');
break;
}
}

if (!activeCard) {
// Fallback to first card if none found visible
activeCard = carousel.locator('[data-testid="haiku-card"]').first();
}

const mainDisplayLines = activeCard.locator('[data-testid="haiku-japanese-line"]');
const mainCount = await mainDisplayLines.count();
const lines: string[] = [];

if (mainCount > 0) {
for (let i = 0; i < mainCount; i++) {
const haikuLine = mainDisplayLines.nth(i);
const japaneseText = await haikuLine.locator('p').first().innerText();
const japaneseText = await haikuLine.innerText();
lines.push(japaneseText);
}
}

const mainHaikuContent = lines.join('').replace(/\s/g, '');
const mainHaikuContent = lines.join("").replace(/\s/g, "");
return mainHaikuContent;
}

async checkHaikuDisplay(page: Page): Promise<void> {
const chatHaikuContent = await this.extractChatHaikuContent(page);

await page.waitForTimeout(5000);
await page.waitForTimeout(3000);

const mainHaikuContent = await this.extractMainDisplayHaikuContent(page);
// Check that the haiku exists somewhere in the carousel
const carousel = page.locator('[data-testid="haiku-carousel"]');
await carousel.waitFor({ state: "visible", timeout: 10000 });

if (mainHaikuContent === '') {
expect(chatHaikuContent.length).toBeGreaterThan(0);
return;
}
const allCarouselCards = carousel.locator('[data-testid="haiku-card"]');
const cardCount = await allCarouselCards.count();

if (chatHaikuContent === mainHaikuContent) {
expect(mainHaikuContent).toBe(chatHaikuContent);
} else {
await page.waitForTimeout(3000);
let foundMatch = false;
for (let i = 0; i < cardCount; i++) {
const card = allCarouselCards.nth(i);
const lines = card.locator('[data-testid="haiku-japanese-line"]');
const lineCount = await lines.count();
const cardLines: string[] = [];

const updatedMainContent = await this.extractMainDisplayHaikuContent(page);
for (let j = 0; j < lineCount; j++) {
const text = await lines.nth(j).innerText();
cardLines.push(text);
}

expect(updatedMainContent).toBe(chatHaikuContent);
const cardContent = cardLines.join("").replace(/\s/g, "");
if (cardContent === chatHaikuContent) {
foundMatch = true;
break;
}
}

expect(foundMatch).toBe(true);

Check failure on line 159 in typescript-sdk/apps/dojo/e2e/featurePages/ToolBaseGenUIPage.ts

View workflow job for this annotation

GitHub Actions / adk-middleware

[chromium] › tests/adkMiddlewareTests/toolBasedGenUIPage.spec.ts:20:7 › Tool Based Generative UI Feature › [ADK Middleware] Haiku generation and UI consistency for two different prompts

2) [chromium] › tests/adkMiddlewareTests/toolBasedGenUIPage.spec.ts:20:7 › Tool Based Generative UI Feature › [ADK Middleware] Haiku generation and UI consistency for two different prompts Error: expect(received).toBe(expected) // Object.is equality Expected: true Received: false at ../featurePages/ToolBaseGenUIPage.ts:159 157 | } 158 | > 159 | expect(foundMatch).toBe(true); | ^ 160 | } 161 | } 162 | at ToolBaseGenUIPage.checkHaikuDisplay (/home/runner/work/ag-ui/ag-ui/typescript-sdk/apps/dojo/e2e/featurePages/ToolBaseGenUIPage.ts:159:24) at /home/runner/work/ag-ui/ag-ui/typescript-sdk/apps/dojo/e2e/tests/adkMiddlewareTests/toolBasedGenUIPage.spec.ts:32:5

Check failure on line 159 in typescript-sdk/apps/dojo/e2e/featurePages/ToolBaseGenUIPage.ts

View workflow job for this annotation

GitHub Actions / langgraph-typescript

[chromium] › tests/langgraphTypescriptTests/toolBasedGenUIPage.spec.ts:7:5 › [LangGraph] Haiku generation and display verification

1) [chromium] › tests/langgraphTypescriptTests/toolBasedGenUIPage.spec.ts:7:5 › [LangGraph] Haiku generation and display verification Error: expect(received).toBe(expected) // Object.is equality Expected: true Received: false at ../featurePages/ToolBaseGenUIPage.ts:159 157 | } 158 | > 159 | expect(foundMatch).toBe(true); | ^ 160 | } 161 | } 162 | at ToolBaseGenUIPage.checkHaikuDisplay (/home/runner/work/ag-ui/ag-ui/typescript-sdk/apps/dojo/e2e/featurePages/ToolBaseGenUIPage.ts:159:24) at /home/runner/work/ag-ui/ag-ui/typescript-sdk/apps/dojo/e2e/tests/langgraphTypescriptTests/toolBasedGenUIPage.spec.ts:17:3
}
}
}
7 changes: 4 additions & 3 deletions typescript-sdk/apps/dojo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@ag-ui/server-starter": "workspace:*",
"@ag-ui/server-starter-all-features": "workspace:*",
"@ag-ui/vercel-ai-sdk": "workspace:*",
"@ai-sdk/openai": "^2.0.42",
"@copilotkit/react-core": "1.10.5",
"@copilotkit/react-ui": "1.10.5",
"@copilotkit/runtime": "1.10.5",
Expand All @@ -34,15 +35,14 @@
"@mastra/libsql": "^0.15.0",
"@mastra/loggers": "^0.10.14",
"@mastra/memory": "^0.15.4",
"@ai-sdk/openai": "^2.0.42",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/mdx": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@monaco-editor/react": "^4.7.0",
"@next/mdx": "^15.2.3",
"@phosphor-icons/react": "^2.1.10",
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tabs": "^1.1.3",
"@tiptap/extension-color": "^2.11.5",
"@tiptap/extension-placeholder": "^2.11.5",
Expand All @@ -54,6 +54,7 @@
"clsx": "^2.1.1",
"dedent": "^1.7.0",
"diff": "^7.0.0",
"embla-carousel-react": "^8.6.0",
"fast-json-patch": "^3.1.1",
"lucide-react": "^0.477.0",
"markdown-it": "^14.1.0",
Expand All @@ -66,7 +67,7 @@
"react-markdown": "^10.1.0",
"react-syntax-highlighter": "^15.6.1",
"rxjs": "7.8.1",
"tailwind-merge": "^3.0.2",
"tailwind-merge": "^3.3.0",
"tailwindcss-animate": "^1.0.7",
"untruncate-json": "^0.0.1",
"uuid": "^11.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,25 @@ const Chat = () => {
});

return (
<div className="flex justify-center items-center h-full w-full" data-testid="background-container" style={{ background }}>
<div
className="flex justify-center items-center h-full w-full"
data-testid="background-container"
style={{ background }}
>
<div className="h-full w-full md:w-8/10 md:h-8/10 rounded-lg">
<CopilotChat
className="h-full rounded-2xl"
labels={{ initial: "Hi, I'm an agent. Want to chat?" }}
suggestions={[
{
title: "Change background",
message: "Change the background to something new.",
},
{
title: "Generate sonnet",
message: "Write a short sonnet about AI.",
},
]}
/>
</div>
</div>
Expand Down
Loading
Loading