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
4 changes: 4 additions & 0 deletions .github/workflows/dojo-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ jobs:
fail-fast: false
matrix:
include:
- suite: a2a-middleware
test_path: tests/a2aMiddlewareTests
services: ["dojo","a2a-middleware"]
wait_on: http://localhost:9999,tcp:localhost:8011,tcp:localhost:8012,tcp:localhost:8013,tcp:localhost:8014
- suite: adk-middleware
test_path: tests/adkMiddlewareTests
services: ["dojo","adk-middleware"]
Expand Down
6 changes: 3 additions & 3 deletions docs/concepts/agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ foundation for:
import { AbstractAgent } from "@ag-ui/client"

class MyAgent extends AbstractAgent {
protected run(input: RunAgentInput): RunAgent {
run(input: RunAgentInput): RunAgent {
// Implementation details
}
}
Expand Down Expand Up @@ -89,7 +89,7 @@ You can create custom agents to integrate with any AI service by extending
class CustomAgent extends AbstractAgent {
// Custom properties and methods

protected run(input: RunAgentInput): RunAgent {
run(input: RunAgentInput): RunAgent {
// Implement the agent's logic
}
}
Expand All @@ -113,7 +113,7 @@ import {
import { Observable } from "rxjs"

class SimpleAgent extends AbstractAgent {
protected run(input: RunAgentInput): RunAgent {
run(input: RunAgentInput): RunAgent {
const { threadId, runId } = input

return () =>
Expand Down
4 changes: 2 additions & 2 deletions docs/quickstart/middleware.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ import {
import { Observable } from "rxjs"

export class OpenAIAgent extends AbstractAgent {
protected run(input: RunAgentInput): Observable<BaseEvent> {
run(input: RunAgentInput): Observable<BaseEvent> {
const messageId = Date.now().toString()
return new Observable<BaseEvent>((observer) => {
observer.next({
Expand Down Expand Up @@ -290,7 +290,7 @@ export class OpenAIAgent extends AbstractAgent {
this.openai = openai ?? new OpenAI()
}

protected run(input: RunAgentInput): Observable<BaseEvent> {
run(input: RunAgentInput): Observable<BaseEvent> {
return new Observable<BaseEvent>((observer) => {
// Same as before - emit RUN_STARTED to begin
observer.next({
Expand Down
2 changes: 1 addition & 1 deletion docs/sdk/js/client/http-agent.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ Default implementation:
Implements the abstract `run()` method from `AbstractAgent` using HTTP requests.

```typescript
protected run(input: RunAgentInput): RunAgent
run(input: RunAgentInput): RunAgent
```

## Properties
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Page, Locator, expect } from '@playwright/test';

export class A2AChatPage {
readonly page: Page;
readonly mainChatTab: Locator;

constructor(page: Page) {
this.page = page;
this.mainChatTab = page.getByRole('tab', {name: 'Main Chat' });
}

async openChat() {
await this.mainChatTab.isVisible();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
test,
expect,
waitForAIResponse,
retryOnAIFailure,
} from "../../test-isolation-helper";
import { A2AChatPage } from "../../pages/a2aMiddlewarePages/A2AChatPage";

test.describe("A2A Chat Feature", () => {
test("[A2A Middleware] Tab bar exists", async ({
page,
}) => {
await retryOnAIFailure(async () => {
await page.goto(
"/a2a-middleware/feature/a2a_chat"
);

const chat = new A2AChatPage(page);

await chat.openChat();
// This should already be handled previously but we just need a base case
await chat.mainChatTab.waitFor({ state: "visible" });
});
});
});
3 changes: 3 additions & 0 deletions typescript-sdk/apps/dojo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"generate-content-json": "npx tsx scripts/generate-content-json.ts"
},
"dependencies": {
"@ag-ui/a2a-middleware": "workspace:*",
"@ag-ui/adk": "workspace:*",
"@ag-ui/agno": "workspace:*",
"@ag-ui/crewai": "workspace:*",
Expand Down Expand Up @@ -51,6 +52,7 @@
"@types/react-syntax-highlighter": "^15.5.13",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dedent": "^1.7.0",
"diff": "^7.0.0",
"fast-json-patch": "^3.1.1",
"lucide-react": "^0.477.0",
Expand All @@ -66,6 +68,7 @@
"rxjs": "7.8.1",
"tailwind-merge": "^3.0.2",
"tailwindcss-animate": "^1.0.7",
"untruncate-json": "^0.0.1",
"uuid": "^11.1.0",
"zod": "^3.25.67"
},
Expand Down
5 changes: 5 additions & 0 deletions typescript-sdk/apps/dojo/scripts/prep-dojo-everything.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ const ALL_TARGETS = {
name: 'ADK Middleware',
cwd: path.join(integrationsRoot, 'adk-middleware/python/examples'),
},
'a2a-middleware': {
command: 'uv sync',
name: 'A2A Middleware',
cwd: path.join(integrationsRoot, 'a2a-middleware/examples'),
},
'dojo': {
command: 'pnpm install --no-frozen-lockfile && pnpm build --filter=demo-viewer...',
name: 'Dojo',
Expand Down
80 changes: 54 additions & 26 deletions typescript-sdk/apps/dojo/scripts/run-dojo-everything.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,76 +47,100 @@ const integrationsRoot = path.join(gitRoot, 'typescript-sdk', 'integrations');

// Define all runnable services keyed by a stable id
const ALL_SERVICES = {
'server-starter': {
'server-starter': [{
command: 'poetry run dev',
name: 'Server Starter',
cwd: path.join(integrationsRoot, 'server-starter/server/python'),
env: { PORT: 8000 },
},
'server-starter-all': {
}],
'server-starter-all': [{
command: 'poetry run dev',
name: 'Server AF',
cwd: path.join(integrationsRoot, 'server-starter-all-features/server/python'),
env: { PORT: 8001 },
},
'agno': {
}],
'agno': [{
command: 'uv run dev',
name: 'Agno',
cwd: path.join(integrationsRoot, 'agno/examples'),
env: { PORT: 8002 },
},
'crew-ai': {
}],
'crew-ai': [{
command: 'poetry run dev',
name: 'CrewAI',
cwd: path.join(integrationsRoot, 'crewai/python'),
env: { PORT: 8003 },
},
'langgraph-fastapi': {
}],
'langgraph-fastapi': [{
command: 'poetry run dev',
name: 'LG FastAPI',
cwd: path.join(integrationsRoot, 'langgraph/examples/python'),
env: {
PORT: 8004,
POETRY_VIRTUALENVS_IN_PROJECT: 'false',
},
},
'langgraph-platform-python': {
}],
'langgraph-platform-python': [{
command: 'pnpx @langchain/langgraph-cli@latest dev --no-browser --host 127.0.0.1 --port 8005',
name: 'LG Platform Py',
cwd: path.join(integrationsRoot, 'langgraph/examples/python'),
env: { PORT: 8005 },
},
'langgraph-platform-typescript': {
}],
'langgraph-platform-typescript': [{
command: 'pnpx @langchain/langgraph-cli@latest dev --no-browser --host 127.0.0.1 --port 8006',
name: 'LG Platform TS',
cwd: path.join(integrationsRoot, 'langgraph/examples/typescript/'),
env: { PORT: 8006 },
},
'llama-index': {
}],
'llama-index': [{
command: 'uv run dev',
name: 'Llama Index',
cwd: path.join(integrationsRoot, 'llamaindex/server-py'),
env: { PORT: 8007 },
},
'mastra': {
}],
'mastra': [{
command: 'npm run dev',
name: 'Mastra',
cwd: path.join(integrationsRoot, 'mastra/example'),
env: { PORT: 8008 },
},
'pydantic-ai': {
}],
'pydantic-ai': [{
command: 'uv run dev',
name: 'Pydantic AI',
cwd: path.join(integrationsRoot, 'pydantic-ai/examples'),
env: { PORT: 8009 },
},
'adk-middleware': {
}],
'adk-middleware': [{
command: 'uv run dev',
name: 'ADK Middleware',
cwd: path.join(integrationsRoot, 'adk-middleware/python/examples'),
env: { PORT: 8010 },
}],
'a2a-middleware': [{
command: 'uv run buildings_management.py',
name: 'A2A Middleware: Buildings Management',
cwd: path.join(integrationsRoot, 'a2a-middleware/examples'),
env: { PORT: 8011 },
},
'dojo': {
{
command: 'uv run finance.py',
name: 'A2A Middleware: Finance',
cwd: path.join(integrationsRoot, 'a2a-middleware/examples'),
env: { PORT: 8012 },
},
{
command: 'uv run it.py',
name: 'A2A Middleware: IT',
cwd: path.join(integrationsRoot, 'a2a-middleware/examples'),
env: { PORT: 8013 },
},
{
command: 'uv run orchestrator.py',
name: 'A2A Middleware: Orchestrator',
cwd: path.join(integrationsRoot, 'a2a-middleware/examples'),
env: { PORT: 8014 },
}],
'dojo': [{
command: 'pnpm run start',
name: 'Dojo',
cwd: path.join(gitRoot, 'typescript-sdk/apps/dojo'),
Expand All @@ -133,9 +157,13 @@ const ALL_SERVICES = {
MASTRA_URL: 'http://localhost:8008',
PYDANTIC_AI_URL: 'http://localhost:8009',
ADK_MIDDLEWARE_URL: 'http://localhost:8010',
A2A_MIDDLEWARE_BUILDINGS_MANAGEMENT_URL: 'http://localhost:8011',
A2A_MIDDLEWARE_FINANCE_URL: 'http://localhost:8012',
A2A_MIDDLEWARE_IT_URL: 'http://localhost:8013',
A2A_MIDDLEWARE_ORCHESTRATOR_URL: 'http://localhost:8014',
NEXT_PUBLIC_CUSTOM_DOMAIN_TITLE: 'cpkdojo.local___CopilotKit Feature Viewer',
},
},
}],
};

function printDryRunServices(procs) {
Expand Down Expand Up @@ -169,12 +197,12 @@ async function main() {
// Build processes, warn for unknown keys
const procs = [];
for (const key of selectedKeys) {
const svc = ALL_SERVICES[key];
if (!svc) {
const svcs = ALL_SERVICES[key];
if (!svcs || svcs.length === 0) {
console.warn(`Skipping unknown service: ${key}`);
continue;
}
procs.push(svc);
procs.push(...svcs);
}

if (dryRun) {
Expand Down
34 changes: 33 additions & 1 deletion typescript-sdk/apps/dojo/src/agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import getEnvVars from "./env";
import { mastra } from "./mastra";
import { PydanticAIAgent } from "@ag-ui/pydantic-ai";
import { ADKAgent } from "@ag-ui/adk";
import { HttpAgent } from "@ag-ui/client";
import { A2AMiddlewareAgent } from "@ag-ui/a2a-middleware";

const envVars = getEnvVars();
export const agentsIntegrations: AgentIntegrationConfig[] = [
Expand Down Expand Up @@ -225,7 +227,7 @@ export const agentsIntegrations: AgentIntegrationConfig[] = [
subgraphs: new LangGraphAgent({
deploymentUrl: envVars.langgraphTypescriptUrl,
graphId: "subgraphs",
})
}),
};
},
},
Expand Down Expand Up @@ -286,4 +288,34 @@ export const agentsIntegrations: AgentIntegrationConfig[] = [
};
},
},
{
id: "a2a",
agents: async () => {
// A2A agents: building management, finance, it agents
const agentUrls = [envVars.a2aMiddlewareBuildingsManagementUrl, envVars.a2aMiddlewareFinanceUrl, envVars.a2aMiddlewareItUrl];
// AGUI orchestration/routing agent
const orchestrationAgent = new HttpAgent({
url: envVars.a2aMiddlewareOrchestratorUrl,
});
return {
a2a_chat: new A2AMiddlewareAgent({
description: "Middleware that connects to remote A2A agents",
agentUrls,
orchestrationAgent,
instructions: `
You are an HR agent. You are responsible for hiring employees and other typical HR tasks.

It's very important to contact all the departments necessary to complete the task.
For example, to hire an employee, you must contact all 3 departments: Finance, IT and Buildings Management. Help the Buildings Management department to find a table.

You can make tool calls on behalf of other agents.
DO NOT FORGET TO COMMUNICATE BACK TO THE RELEVANT AGENT IF MAKING A TOOL CALL ON BEHALF OF ANOTHER AGENT!!!

When choosing a seat with the buildings management agent, You MUST use the \`pickTable\` tool to have the user pick a seat.
The buildings management agent will then use the \`pickSeat\` tool to pick a seat.
`,
}),
};
},
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# 🤖 A2A Chat
Loading
Loading