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: 2 additions & 2 deletions .gemini/GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The A2UI repository is organized into several key directories:
- `eval/`: Genkit-based evaluation framework.
- `samples/`: Contains sample implementations.
- `agent/`:
- `adk/`: Python-based ADK agent samples (e.g., `contact_lookup`, `restaurant_finder`, `rizzcharts`, `orchestrator`).
- `adk/`: Python-based ADK agent samples (e.g., `contact_lookup`, `restaurant_finder`, `orchestrator`).
- `mcp/`: MCP server sample (A2UI over MCP).
- `client/`: Web client implementations.
- `lit/`: Clients using Lit and Vite (e.g., `contact`, `shell`).
Expand Down Expand Up @@ -127,7 +127,7 @@ Then run the Angular client:
```bash
cd samples/client/angular
npm install
npm start -- contact # Replace 'contact' with the desired project name (e.g., restaurant, gallery, rizzcharts)
npm start -- contact # Replace 'contact' with the desired project name (e.g., restaurant, orchestrator)
```

### Running Tools
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/ng_build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ jobs:
working-directory: ./samples/client/angular
run: npm run build restaurant

- name: Build Rizzchart sample
working-directory: ./samples/client/angular
run: npm run build rizzcharts

- name: Build Orchestrator
working-directory: ./samples/client/angular
run: npm run build orchestrator
4 changes: 0 additions & 4 deletions .github/workflows/python_samples_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,3 @@ jobs:
- name: Build restaurant_finder
working-directory: samples/agent/adk/restaurant_finder
run: uv build .

- name: Build rizzcharts
working-directory: samples/agent/adk/rizzcharts
run: uv build .
8 changes: 3 additions & 5 deletions agent_sdks/agent_development.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ selected_catalog = schema_manager.get_selected_catalog()
try:
# Parse the LLM's JSON part
parsed_json = json.loads(json_string)

# Validate and fix against the schema
selected_catalog.payload_fixer.validate_and_fix(parsed_json)
except Exception as e:
Expand Down Expand Up @@ -146,19 +146,17 @@ agent = LlmAgent(instruction=instruction, ...)

Some agents may need to attach different catalogs or examples depending on the user's request, client capabilities, or conversational context. This is common for dashboard-style agents that support multiple distinct visualization types (e.g., Charts vs. Maps).

**Example Sample:** [rizzcharts](../../../samples/agent/adk/rizzcharts)

#### 2a. Injecting Catalogs into Session State
In a dynamic scenario, you don't provide a static catalog to the agent. Instead, you resolve the selected catalog at runtime (e.g., during session preparation) and store it in the session state.

```python
# In your AgentExecutor subclass
async def _prepare_session(self, context, run_request, runner):
session = await super()._prepare_session(context, run_request, runner)

# 1. Determine client capabilities from metadata
capabilities = context.message.metadata.get("a2ui_client_capabilities")

# 2. Get selected catalog and load examples
a2ui_catalog = self.schema_manager.get_selected_catalog(
client_ui_capabilities=capabilities
Expand Down
11 changes: 4 additions & 7 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,21 @@ User interactions from the client can be treated as new user input.

The A2UI repository includes sample agents you can learn from:

- [Restaurant Finder](https://github.com/google/A2UI/tree/main/samples/agent/adk/restaurant_finder)
- [Restaurant Finder](https://github.com/google/A2UI/tree/main/samples/agent/adk/restaurant_finder)
- Table reservations with forms
- Written with the ADK
- [Contact Lookup](https://github.com/google/A2UI/tree/main/samples/agent/adk/contact_lookup)
- [Contact Lookup](https://github.com/google/A2UI/tree/main/samples/agent/adk/contact_lookup)
- Search with result lists
- Written with the ADK
- [Rizzcharts](https://github.com/google/A2UI/tree/main/samples/agent/adk/rizzcharts)
- A2UI Custom components demo
- Written with the ADK
- [Orchestrator](https://github.com/google/A2UI/tree/main/samples/agent/adk/orchestrator)
- [Orchestrator](https://github.com/google/A2UI/tree/main/samples/agent/adk/orchestrator)
- Passes A2UI messages from remote subagents
- Written with the ADK

## Different types of agents you will use A2A with

### 1. User Facing Agent (standalone)

A user facing agent is one that is directly interacted with by the user.
A user facing agent is one that is directly interacted with by the user.

### 2. User Facing Agent as a host for a Remote Agent

Expand Down
70 changes: 33 additions & 37 deletions docs/catalogs.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Whether you are building a simple prototype or a complex production application,

To help developers get started quickly, the A2UI team maintains the [Basic Catalog](../specification/v0_9/json/basic_catalog.json).

This is a pre-defined catalog file that contains a standard set of general-purpose components (Buttons, Inputs, Cards) and functions. It is not a special "type" of catalog; it is simply a version of a catalog that we have already written and have open source renderers for.
This is a pre-defined catalog file that contains a standard set of general-purpose components (Buttons, Inputs, Cards) and functions. It is not a special "type" of catalog; it is simply a version of a catalog that we have already written and have open source renderers for.

The basic catalog allows you to bootstrap an application or validate A2UI concepts without needing to write your own schema from scratch. It is intentionally sparse to remain easily implementable by different renderers.

Expand All @@ -77,8 +77,6 @@ By defining your own catalog, you restrict the agent to using exactly the compon

For simplicity we recommend building catalogs that directly reflect a client's design system rather than trying to map the Basic Catalog to it through an adapter. Since A2UI is designed for GenUI, we expect the LLM can interpret different catalogs for different clients.

[See an example Rizzcharts catalog](../samples/agent/adk/rizzcharts/rizzcharts_catalog_definition.json)

### Recommendations

| Usecase | Recommendation | Effort |
Expand Down Expand Up @@ -149,7 +147,7 @@ When the agent uses that catalog, it generates a payload strictly conforming to

### Freestanding Catalogs

A2UI Catalogs must be standalone (no references to external files) to simplify LLM inference and dependency management.
A2UI Catalogs must be standalone (no references to external files) to simplify LLM inference and dependency management.

While the final catalog must be freestanding, you may still author your catalogs modularly using JSON Schema `$ref` pointing to external documents during local development. Run `scripts/build_catalog.py` before distributing your catalog to bundle all external file references into a single, independent JSON Schema file:

Expand Down Expand Up @@ -202,7 +200,7 @@ This catalog imports only `Text` from the Basic Catalog to build a simple Popup
"allOf": [
{ "$ref": "basic_catalog.json#/components/Text" },
{
"Popup": {
"Popup": {
"type": "object",
"description": "A modal overlay that displays an icon and text.",
"properties": {
Expand All @@ -225,17 +223,17 @@ Client renderers implement the catalog by mapping the schema definition to actua
Example typescript renderer for the hello world catalog

```typescript
import { Catalog, DEFAULT_CATALOG } from '@a2ui/angular';
import { inputBinding } from '@angular/core';

export const RIZZ_CHARTS_CATALOG = {
...DEFAULT_CATALOG, // Include the basic catalog
HelloWorldBanner: {
type: () => import('./hello_world_banner').then((r) => r.HelloWorldBanner),
bindings: ({ properties }) => [
import { Catalog, DEFAULT_CATALOG } from '@a2ui/angular';
import { inputBinding } from '@angular/core';

export const RIZZ_CHARTS_CATALOG = {
...DEFAULT_CATALOG, // Include the basic catalog
HelloWorldBanner: {
type: () => import('./hello_world_banner').then((r) => r.HelloWorldBanner),
bindings: ({ properties }) => [
inputBinding('message', () => ('message' in properties && properties['message']) || undefined)
],
},
],
},
} as Catalog;
```

Expand All @@ -247,7 +245,7 @@ import { Component, Input } from '@angular/core';

@Component({
selector: 'hello-world-banner',
imports: [],
imports: [],
template: `
<div>
<h2>Hello World Banner</h2>
Expand All @@ -260,17 +258,15 @@ export class HelloWorldBanner extends DynamicComponent {
}
```

You can see a working example of a client renderer in the [Rizzcharts demo](../samples/client/angular/projects/rizzcharts/src/a2ui-catalog/catalog.ts).

## A2UI Catalog Negotiation

Because clients and agents can support multiple catalogs, they must agree on which catalog to use through a catalog negotiation handshake.

### Step 1: Agent advertises its support catalogs (optional)
### Step 1: Agent advertises its support catalogs (optional)

The agent may optionally advertise which catalogs it is capable of speaking (e.g., in the A2A Agent Card). This is informational; it helps the client know if the agent supports their specific features, but the client doesn’t have to use it.

Example of an A2A AgentCard advertising that the agent supports the basic and rizzcharts catalogs
Example of an A2A AgentCard advertising that the agent supports the basic and custom dashboard catalogs

```json
{
Expand All @@ -284,7 +280,7 @@ Example of an A2A AgentCard advertising that the agent supports the basic and ri
"params": {
"supportedCatalogIds": [
"https://a2ui.org/specification/v0_9/basic_catalog.json",
"https://github.com/.../rizzcharts_catalog_definition.json"
"https://github.com/.../dashboard_catalog_definition.json"
]
}
}
Expand All @@ -310,7 +306,7 @@ Example of A2A message containing the supportedCatalogIds in metadata
"a2uiClientCapabilities": {
"supportedCatalogIds": [
"https://a2ui.org/specification/v0_9/basic_catalog.json",
"https://github.com/.../rizzcharts_catalog_definition.json"
"https://github.com/.../dashboard_catalog_definition.json"
]
}
}
Expand Down Expand Up @@ -340,19 +336,19 @@ A2UI component catalogs require versioning because catalog definitions are often

The `catalogId` is a unique text identifier used for negotiation between the client and the agent.

* **Format:** While the `catalogId` is technically a string, the A2UI convention is to use a **URI** (e.g., `https://example.com/catalogs/mysurface/v1/catalog.json`).
* **Purpose:** We use URIs to make the ID globally unique and easy for human developers to inspect in a browser.
* **Format:** While the `catalogId` is technically a string, the A2UI convention is to use a **URI** (e.g., `https://example.com/catalogs/mysurface/v1/catalog.json`).
* **Purpose:** We use URIs to make the ID globally unique and easy for human developers to inspect in a browser.
* **No Runtime Fetching:** This URI does not imply that the agent or client downloads the catalog at runtime. **The catalog definition must be known to the agent and client beforehand (at compile/deploy time)**. The URI serves only as a stable identifier.

### Versioning Guidelines

Unlike standard JSON parsers where extra data is safely ignored, A2UI requires strict versioning to prevent semantic errors. If a client silently drops a new component (like a new "Itinerary" component) because it doesn't recognize it, the user misses critical information.
Unlike standard JSON parsers where extra data is safely ignored, A2UI requires strict versioning to prevent semantic errors. If a client silently drops a new component (like a new "Itinerary" component) because it doesn't recognize it, the user misses critical information.

To ensure the agent only generates UI the client can fully render, any structural change—even purely additive ones—requires a new catalog version. This is enforced by the A2UI JSON Schema which generally does not allow for unrecognized properties.

* **Structural Changes (New Version Required)** Any change that alters the semantic meaning of the A2UI JSON payload requires a new catalog version. This ensures the Agent never sends a component the Client cannot render. A new catalog version is required for:
* **Adding a new component:** (e.g., adding `FacePile` or `Itinerary`).
* **Adding a new property:** Even if optional, if the Agent generates it, it expects it to be rendered.
* **Adding a new property:** Even if optional, if the Agent generates it, it expects it to be rendered.
* **Renaming/Removing fields:** These are standard breaking changes.

* **Metadata Changes (Same Version Allowed)** You may keep the same catalog version only if the change has no impact on the generated JSON structure or the renderer's behavior. You can keep the version when
Expand All @@ -367,28 +363,28 @@ We recommend including the version in the catalogId. This allows using A2UI cata

| Change Type | URI Example | Description |
| :--- | :--- | :--- |
| **Current** | .../rizzcharts/v1/catalog.json | The stable, production schema. |
| **Breaking** | .../rizzcharts/v2/catalog.json | A new schema introducing renamed components or structural changes. |
| **Current** | .../dashboard/v1/catalog.json | The stable, production schema. |
| **Breaking** | .../dashboard/v2/catalog.json | A new schema introducing renamed components or structural changes. |

### Handling Migrations

To upgrade a catalog without breaking active agents, use A2UI Catalog Negotiation:

1. **Client Update:** The client updates its list of supportedCatalogIds to include *both* the old and new versions (e.g., [".../v2/...", ".../v1/..."]).
2. **Agent Update:** Agents are rebuilt with the v2 schema. When they see the client supports v2, they prefer it.
1. **Client Update:** The client updates its list of supportedCatalogIds to include *both* the old and new versions (e.g., [".../v2/...", ".../v1/..."]).
2. **Agent Update:** Agents are rebuilt with the v2 schema. When they see the client supports v2, they prefer it.
3. **Legacy Support:** Older agents that have not yet been rebuilt will continue to match against v1 in the client's list, ensuring they remain functional.

## A2UI Schema Validation & Fallback
## A2UI Schema Validation & Fallback

To ensure a stable user experience, A2UI employs a two-phase validation strategy. This "defense in depth" approach catches errors as early as possible while ensuring clients remain robust when facing unexpected payloads.

### Two-Phase Validation

1. **Agent-Side (Pre-Send):** Before transmitting any UI payload, the agent runtime validates the generated JSON against the catalog definition.
* Purpose: To catch hallucinated properties or malformed structures at the source.
* Outcome: If validation fails, the agent can attempt to fix or regenerate the A2UI JSON, or it can do graceful degradation such as falling back to text in a conversational app.
2. **Client-Side:** Upon receiving the payload, the client library validates the JSON against its local definition of the catalog.
* Purpose: Security and stability. This ensures that the code executing on the user's device strictly conforms to the expected contract, protecting against version mismatches or compromised agent outputs.
1. **Agent-Side (Pre-Send):** Before transmitting any UI payload, the agent runtime validates the generated JSON against the catalog definition.
* Purpose: To catch hallucinated properties or malformed structures at the source.
* Outcome: If validation fails, the agent can attempt to fix or regenerate the A2UI JSON, or it can do graceful degradation such as falling back to text in a conversational app.
2. **Client-Side:** Upon receiving the payload, the client library validates the JSON against its local definition of the catalog.
* Purpose: Security and stability. This ensures that the code executing on the user's device strictly conforms to the expected contract, protecting against version mismatches or compromised agent outputs.
* Outcome: Failures here are reported back to the agent using the “error” client message

### Graceful Degradation
Expand All @@ -397,7 +393,7 @@ Even if a payload passes schema validation, the renderer may encounter runtime i

Clients should not crash when encountering these errors. Instead, they should employ Graceful Degradation:

* **Unknown Components:** If a component is recognized in the schema but not implemented in the renderer, render a "safe" fallback (e.g., a generic card with the component's debug name) or skip rendering that specific node entirely.
* **Unknown Components:** If a component is recognized in the schema but not implemented in the renderer, render a "safe" fallback (e.g., a generic card with the component's debug name) or skip rendering that specific node entirely.
* **Text Fallback:** If the entire surface fails to render, display the raw text description (if available) or a generic error message: *"This interface could not be displayed."*

### Client-to-Server Error Reporting
Expand Down
16 changes: 5 additions & 11 deletions samples/agent/adk/orchestrator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,20 @@ Subagents are configured using RemoteA2aAgent which translates ADK events to A2A
```

```bash
cd samples/agent/adk/contact_lookup
uv run . --port=10004
```

```bash
cd samples/agent/adk/rizzcharts
uv run . --port=10005
```
cd samples/agent/adk/contact_lookup
uv run . --port=10004
```

3. Run the orchestrator agent:

```bash
cd samples/agent/adk/orchestrator
uv run . --port=10002 --subagent_urls=http://localhost:10003 --subagent_urls=http://localhost:10004 --subagent_urls=http://localhost:10005
uv run . --port=10002 --subagent_urls=http://localhost:10003 --subagent_urls=http://localhost:10004
```

4. Try commands that work with any agent:
4. Try commands that work with any agent:
a. "Who is Alex Jordan?" (routed to contact lookup agent)
b. "Show me chinese food restaurants in NYC" (routed to restaurant finder agent)
c. "Show my sales data for Q4" (routed to rizzcharts)

## Disclaimer

Expand Down
2 changes: 1 addition & 1 deletion samples/agent/adk/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ url = "https://pypi.org/simple"
default = true

[tool.uv.workspace]
members = ["contact_lookup", "contact_multiple_surfaces", "orchestrator", "restaurant_finder", "rizzcharts"]
members = ["contact_lookup", "contact_multiple_surfaces", "orchestrator", "restaurant_finder"]

[tool.uv.sources]
a2ui-agent = { path = "../../../agent_sdks/python", editable = true }
Expand Down
7 changes: 0 additions & 7 deletions samples/agent/adk/rizzcharts/.env.example

This file was deleted.

Loading
Loading