Skip to content
Open
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
15 changes: 15 additions & 0 deletions samples/agent/adk/rizzcharts/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,27 @@ def get_instructions(cls, readonly_context: ReadonlyContext) -> str:
else:
raise ValueError(f"Unsupported catalog uri: {catalog_uri if catalog_uri else 'None'}")

# Check for Google API Key
googlemaps_api_key = os.getenv("GOOGLEMAPS_API_KEY")
if googlemaps_api_key:
map_image_instruction = f"""
**Map Image URL:** When constructing map visualizations, you may use Google Maps Static API with the following API key: `{googlemaps_api_key}`.
Example URL format: `https://maps.googleapis.com/maps/api/staticmap?center=LAT,LNG&zoom=ZOOM&size=600x400&markers=...&key={googlemaps_api_key}`
"""
else:
map_image_instruction = """
**Map Image URL:** When constructing map visualizations, use a placeholder image URL exactly as shown in the example template. Do NOT attempt to use Google Maps Static API or any other external map service. Use exactly: `https://placehold.co/600x400?text=Map+Placeholder`
"""

final_prompt = f"""
### System Instructions

You are an expert A2UI Ecommerce Dashboard analyst. Your primary function is to translate user requests for ecommerce data into A2UI JSON payloads to display charts and visualizations. You MUST use the `send_a2ui_json_to_client` tool with the `a2ui_json` argument set to the A2UI JSON payload to send to the client. You should also include a brief text message with each response saying what you did and asking if you can help with anything else.

**Core Objective:** To provide a dynamic and interactive dashboard by constructing UI surfaces with the appropriate visualization components based on user queries.

{map_image_instruction}

**Key Components & Examples:**

You will be provided a schema that defines the A2UI message structure and two key generic component templates for displaying data.
Expand Down Expand Up @@ -113,6 +127,7 @@ def get_instructions(cls, readonly_context: ReadonlyContext) -> str:
* Use the **entire** JSON array from the chosen example as the base value for the `a2ui_json` argument.
* **Generate a new `surfaceId`:** You MUST generate a new, unique `surfaceId` for this request (e.g., `sales_breakdown_q3_surface`, `regional_outliers_northeast_surface`). This new ID must be used for the `surfaceId` in all three messages within the JSON array (`beginRendering`, `surfaceUpdate`, `dataModelUpdate`).
* **Update the title Text:** You MUST update the `literalString` value for the `Text` component (the component with `id: "page_header"`) to accurately reflect the specific user query. For example, if the user asks for "Q3" sales, update the generic template text to "Q3 2025 Sales by Product Category".
* **For Map Image URLs:** Follow the Map Image URL instructions above.
* Ensure the generated JSON perfectly matches the A2UI specification. It will be validated against the json_schema and rejected if it does not conform.
* If you get an error in the tool response apologize to the user and let them know they should try again.

Expand Down
10 changes: 10 additions & 0 deletions samples/agent/adk/rizzcharts/examples/standard_catalog/map.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@
"usageHint": "h2"
}
}
},
{
"id": "map-image",
"component": {
"Image": {
"url": {
"literalString": "https://placehold.co/600x400?text=Map+Placeholder"
}
}
}
},
{
"id": "location-list",
Expand Down
4 changes: 3 additions & 1 deletion samples/client/lit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
"scripts": {
"serve:agent:restaurant": "cd ../../agent/adk/restaurant_finder && uv run .",
"serve:agent:contact_lookup": "cd ../../agent/adk/contact_lookup && uv run .",
"serve:agent:rizzcharts": "cd ../../agent/adk/rizzcharts && uv run .",
"serve:agent:contact_multi_surface": "cd ../../agent/adk/contact_multiple_surfaces && uv run .",
"serve:shell": "cd shell && npm run dev",
"build:renderer": "cd ../../../renderers/lit && npm install && npm run build",
"demo:all": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,REST,CONT1\" -c \"magenta,blue,green\" \"npm run serve:shell\" \"npm run serve:agent:restaurant\" \"npm run serve:agent:contact_lookup\"",
"demo:restaurant": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,REST\" -c \"magenta,blue\" \"npm run serve:shell\" \"npm run serve:agent:restaurant\""
"demo:restaurant": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,REST\" -c \"magenta,blue\" \"npm run serve:shell\" \"npm run serve:agent:restaurant\"",
"demo:rizzcharts": "npm install && npm run build:renderer && concurrently -k -n \"SHELL,REST\" -c \"magenta,blue\" \"npm run serve:shell\" \"npm run serve:agent:rizzcharts\""
},
"devDependencies": {
"concurrently": "9.2.1"
Expand Down
2 changes: 2 additions & 0 deletions samples/client/lit/shell/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ import "./ui/ui.js";
import { AppConfig } from "./configs/types.js";
import { config as restaurantConfig } from "./configs/restaurant.js";
import { config as contactsConfig } from "./configs/contacts.js";
import { config as rizzchartsConfig } from "./configs/rizzcharts.js";
import { styleMap } from "lit/directives/style-map.js";

const configs: Record<string, AppConfig> = {
restaurant: restaurantConfig,
contacts: contactsConfig,
rizzcharts: rizzchartsConfig,
};

@customElement("a2ui-shell")
Expand Down
39 changes: 35 additions & 4 deletions samples/client/lit/shell/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Part, SendMessageSuccessResponse, Task } from "@a2a-js/sdk";
import { A2AClient } from "@a2a-js/sdk/client";
import { v0_8 } from "@a2ui/lit";

const A2AUI_MIME_TYPE = "application/json+a2aui";
const A2AUI_MIME_TYPE = "application/json+a2ui";

export class A2UIClient {
#serverUrl: string;
Expand Down Expand Up @@ -97,13 +97,44 @@ export class A2UIClient {
}

const result = (response as SendMessageSuccessResponse).result as Task;
if (result.kind === "task" && result.status.message?.parts) {
console.log("Full Server Response Result:", JSON.stringify(result, null, 2));

let responseParts = result.status.message?.parts;

// Fallback: If no parts in status.message, check the last agent message in history
if (!responseParts && result.history && result.history.length > 0) {
// Iterate backwards to find the last agent message
for (let i = result.history.length - 1; i >= 0; i--) {
const msg = result.history[i];
if (msg.role === 'agent' && msg.parts && msg.parts.length > 0) {
responseParts = msg.parts;
console.log("Found parts in history at index", i);
break;
}
}
}

if (result.kind === "task" && responseParts) {
const messages: v0_8.Types.ServerToClientMessage[] = [];
for (const part of result.status.message.parts) {
for (const part of responseParts) {
console.log("Client Received part:", JSON.stringify(part, null, 2));

if (part.kind === 'data') {
messages.push(part.data as v0_8.Types.ServerToClientMessage);
let data = part.data;
if (typeof data === 'string') {
try {
data = JSON.parse(data);
console.log("Parsed string data:", data);
} catch (e) {
console.error("Failed to parse part.data string:", e);
}
}
messages.push(data as v0_8.Types.ServerToClientMessage);
} else if (part.kind === 'text') {
console.log("Ignored text part:", part.text);
}
}
console.log("Final messages to process:", messages);
return messages;
}

Expand Down