Skip to content

Commit e330a76

Browse files
authored
Reintroduce createSurface, add evolution guide (#226)
1 parent b67bdbf commit e330a76

File tree

11 files changed

+1070
-652
lines changed

11 files changed

+1070
-652
lines changed

specification/0.9/docs/a2ui_protocol.md

Lines changed: 64 additions & 60 deletions
Large diffs are not rendered by default.
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
# A2UI Protocol Evolution Guide: v0.8 to v0.9
2+
3+
This document serves as a comprehensive guide to the changes between A2UI version 0.8 and version 0.9. It details the shifts in philosophy, architecture, and implementation, providing a reference for stakeholders and developers migrating between versions.
4+
5+
## 1. Executive Summary
6+
7+
**Version 0.9 represents a fundamental philosophical shift from "Structured Output First" to "Prompt First."**
8+
9+
- **v0.8** was designed to be generated by an LLM using Structured Output above all else, optimized for LLMs that support strict JSON mode or function calling (which is also a form of Structured Output). It relied on deep nesting and specific wrapper structures that were definable in the limited schema features but often confusing for an LLM to generate.
10+
- **v0.9** is designed to be **embedded directly in an LLM's system prompt**. The schema is refactored to be more human-readable and "token-efficient" for the model to understand. It prioritizes patterns that LLMs naturally excel at (like standard JSON objects for maps) over strict structured output-friendly structures (like arrays of key-value pairs).
11+
12+
### Summary Table
13+
14+
| Feature | v0.8 | v0.9 |
15+
| :-------------------- | :------------------------------------- | :--------------------------------------------------- |
16+
| **Philosophy** | Structured Output / Function Calling | Prompt-First / In-Context Schema |
17+
| **Message Types** | `beginRendering`, `surfaceUpdate`, ... | `createSurface`, `updateComponents`, ... |
18+
| **Surface Creation** | Explicit `beginRendering` | Explicit `createSurface` |
19+
| **Component Type** | Key-based wrapper (`{"Text": ...}`) | Property-based discriminator (`"component": "Text"`) |
20+
| **Data Model Update** | Array of Key-Value Pairs | Standard JSON Object |
21+
| **Data Binding** | `dataBinding` / `literalString` | `path` / Native JSON types |
22+
| **Button Context** | Array of Key-Value pairs | Standard JSON Object |
23+
| **Auxiliary Rules** | N/A | `standard_catalog_rules.txt` |
24+
| **Validation** | Basic Schema | Strict `ValidationFailed` feedback loop |
25+
26+
## 2. Architectural & Schema Changes
27+
28+
### 2.1. Modular Schema Architecture
29+
30+
**v0.8:**
31+
32+
- Monolithic tendencies. `server_to_client.json` often contained deep definitions or relied on complex `oneOf` structures that were hard to decompose.
33+
- `standard_catalog_definition.json` existed but was often implicitly coupled.
34+
35+
**v0.9:**
36+
37+
- **Modularization**: The schema is strictly split into:
38+
- `common_types.json`: Reusable primitives (IDs, paths, weights).
39+
- `server_to_client.json`: The "envelope" defining the message types.
40+
- `standard_catalog_definition.json`: The specific UI components.
41+
- **Benefit**: This allows developers to swap out the `standard_catalog_definition.json` for a `custom_catalog.json` without touching the core protocol envelope.
42+
43+
### 2.2. Strict Message Typing
44+
45+
**v0.8:**
46+
47+
- Messages were objects where properties like `surfaceUpdate` were optional keys.
48+
- Validation often relied on "minProperties: 1" constraints.
49+
50+
**v0.9:**
51+
52+
- Uses a top-level **`oneOf`** constraint in `server_to_client.json`.
53+
- **Reason**: This is a more natural way to express the schema to an LLM, and easier for the LLM to reason about. It's also a more natural form for the developer to read.
54+
55+
### 2.3. Auxiliary Rules File
56+
57+
**v0.9:**
58+
59+
- **New Artifact**: `standard_catalog_rules.txt`.
60+
- **Purpose**: A plain-text prompt fragment containing rules for using the catalog schema (e.g., "MUST provide 'action' for Button").
61+
- **Usage**: Designed to be included in the system prompt alongside the catalog schema.
62+
- **Reason**: Some constraints (like conditional requirements or specific property combinations) are difficult or verbose to express in JSON Schema but easy to express in natural language rules for an LLM, and it can be packaged with the catalog schema for ease of customizing the prompt for a particular catalog.
63+
64+
## 3. Protocol Lifecycle Changes
65+
66+
### 3.1. `beginRendering` Replaced by `createSurface`
67+
68+
**v0.8 (`beginRendering`):**
69+
70+
- **Explicit Signal**: The server sent a `beginRendering` message to tell the client "I am done sending the initial batch of components, you can draw now."
71+
- **Root Definition**: The root component ID was defined in this message.
72+
- **Style Information**: The message included style information for the surface.
73+
74+
**v0.9 (`createSurface`):**
75+
76+
- **Replacement**: `beginRendering` is **REPLACED** by `createSurface`.
77+
- **Purpose**: `createSurface` signals the client to create a new surface and prepare for rendering.
78+
- **Style Information Removed**: `createSurface` does **NOT** contain style information. Theming is now handled via the client styles, decoupling it from the message stream.
79+
- **Root Rule**: The rule is: "There must be exactly one component with the ID `root`." The "root" attribute that `beginRendering` had has been removed. The client is expected to render as soon as it has a valid tree with a root component.
80+
- **New Requirement**: `createSurface` now requires a **`catalogId`** (URI) to explicitly state which component set is being used.
81+
82+
**Example:**
83+
84+
**v0.8 (`beginRendering`)**:
85+
```json
86+
{
87+
"beginRendering": {
88+
"surfaceId": "user_profile_card",
89+
"root": "root",
90+
"styles": {
91+
"primaryColor": "#007bff",
92+
}
93+
}
94+
}
95+
```
96+
97+
**v0.9 (`createSurface`)**:
98+
```json
99+
{
100+
"createSurface": {
101+
"surfaceId": "user_profile_card",
102+
"catalogId": "https://a2ui.dev/specification/0.9/standard_catalog_definition.json"
103+
}
104+
}
105+
```
106+
107+
## 4. Message Structure Comparison
108+
109+
### 4.1. Component Updates
110+
111+
**v0.8 (`surfaceUpdate`)**:
112+
113+
- Components were wrapped in an object where the **key** was the component type.
114+
- **Structure**: `{ "id": "...", "component": { "Text": { "text": "..." } } }`
115+
116+
**v0.9 (`updateComponents`)**:
117+
118+
- **Renamed**: `surfaceUpdate` -> `updateComponents`.
119+
- **Refactored**: Components use a flattened structure with a const discriminator property `component`.
120+
- **Structure**: `{ "id": "...", "component": "Text", "text": "..." }`
121+
- **Reason**: This "flat" structure with a discriminator field (`component: "Text"`) is much easier for LLMs to generate consistently than a dynamic key (`"Text": {...}`). It also simplifies polymorphism in many JSON parsers.
122+
123+
Specifying an unknown surfaceId will cause an error. It is recommended that clients implement a namespacing scheme internally to prevent separate agents from creating surfaces with the same ID, and to prevent agents from modifying surfaces created by other agents.
124+
125+
#### Side-by-Side Example
126+
127+
**v0.8:**
128+
129+
```json
130+
{
131+
"surfaceUpdate": {
132+
"surfaceId": "main",
133+
"components": [
134+
{
135+
"id": "title",
136+
"component": {
137+
"Text": { "text": { "literalString": "Hello" } }
138+
}
139+
}
140+
]
141+
}
142+
}
143+
```
144+
145+
**v0.9:**
146+
147+
```json
148+
{
149+
"updateComponents": {
150+
"surfaceId": "main",
151+
"components": [
152+
{
153+
"id": "root",
154+
"component": "Column",
155+
"children": ["title"]
156+
},
157+
{
158+
"id": "title",
159+
"component": "Text",
160+
"text": "Hello"
161+
}
162+
]
163+
}
164+
}
165+
```
166+
167+
### 4.2. Data Model Updates
168+
169+
**v0.8 (`dataModelUpdate`)**:
170+
171+
- **Adjacency List**: The `contents` property was an **array** of key-value pair objects.
172+
- **Typed Values**: Each entry required explicit typing like `valueString`, `valueNumber`, `valueBoolean`.
173+
- **Structure**: `[{ "key": "name", "valueString": "Alice" }]`
174+
175+
**v0.9 (`updateDataModel`)**:
176+
177+
- **Renamed**: `dataModelUpdate` -> `updateDataModel`.
178+
- **Standard JSON**: The `value` property is now a standard **JSON object**.
179+
- **Op**: The `op` property is added to allow for more complex updates (e.g., `replace`, `remove`).
180+
- **Structure**: `{ "name": "Alice" }`
181+
- **Reason**: LLMs are trained to generate JSON objects. Forcing them to generate an "adjacency list" representation of a map was inefficient and error-prone.
182+
183+
## 5. Data Binding & State
184+
185+
### 5.1. Standardization of `path`
186+
187+
**v0.8:**
188+
189+
- Used `dataBinding` in `childrenProperty` templates.
190+
- Used `path` in `BoundValue` objects.
191+
- Inconsistent terminology.
192+
193+
**v0.9:**
194+
195+
- **Unified**: Everything is now **`path`**.
196+
- **Reason**: Reduces cognitive load for the LLM. "Path" always means "JSON Pointer to data."
197+
198+
### 5.2. Simplified Bound Values
199+
200+
**v0.8:**
201+
202+
- `{ "literalString": "foo" }` or `{ "path": "/foo" }`.
203+
- Explicit typing in keys (`literalNumber`, `literalBoolean`).
204+
205+
**v0.9:**
206+
207+
- **Implicit Typing**: `stringOrPath`, `numberOrPath`, etc. are defined in `common_types.json`.
208+
- **Structure**: The schema allows `string` OR `{ "path": "..." }`.
209+
- **Reason**: Much more natural JSON. `{ "text": "Hello" }` is valid. `{ "text": { "path": "/msg" } }` is valid. No need for `{ "text": { "literalString": "Hello" } }`.
210+
211+
## 6. Component-Specific Changes
212+
213+
### 6.1. Button Context
214+
215+
**v0.8:**
216+
217+
- **Array of Pairs**: `context: [{ "key": "id", "value": "123" }]`
218+
- **Reason**: Easy to parse, hard to generate.
219+
220+
**v0.9:**
221+
222+
- **Standard Map**: `context: { "id": "123" }`
223+
- **Reason**: Token efficiency. LLMs understand JSON objects as maps natively.
224+
225+
### 6.2. TextField
226+
227+
**v0.8:**
228+
229+
- Property: `textFieldType` (e.g., "email", "password").
230+
231+
**v0.9:**
232+
233+
- Property: **`usageHint`**.
234+
- **Reason**: Consistency with `Text` and `Image` components which already used `usageHint`.
235+
236+
### 6.3. ChoicePicker (vs MultipleChoice)
237+
238+
**v0.8:**
239+
240+
- Component: **`MultipleChoice`**.
241+
- Properties: `selections` (array), `maxAllowedSelections` (integer).
242+
243+
**v0.9:**
244+
245+
- Component: **`ChoicePicker`**.
246+
- Properties: **`value`** (array), **`usageHint`** (enum: `multipleSelection`, `mutuallyExclusive`). The `maxAllowedSelections` property was removed.
247+
- **Reason**: `ChoicePicker` is a more generic name that covers both radio buttons (mutually exclusive) and checkboxes (multiple selection). The `usageHint` controls the behavior, simplifying the component surface area.
248+
249+
### 6.4. Slider
250+
251+
**v0.8:**
252+
253+
- Properties: `minValue`, `maxValue`.
254+
255+
**v0.9:**
256+
257+
- Properties: **`min`**, **`max`**.
258+
- **Reason**: Standardizing on shorter, more common property names.
259+
260+
## 7. Error Handling
261+
262+
**v0.9** introduces a strict **`ValidationFailed`** error format in `client_to_server.json`.
263+
264+
- **Purpose**: To allow the "Prompt-Generate-Validate" loop to work effectively.
265+
- **Mechanism**: If the LLM generates invalid JSON, the system sends back a structured error:
266+
```json
267+
{
268+
"error": {
269+
"code": "VALIDATION_FAILED",
270+
"surfaceId": "...",
271+
"path": "/components/0/text",
272+
"message": "Expected string, got number"
273+
}
274+
}
275+
```
276+
- **Result**: The LLM sees this and can "self-correct" in the next turn.

specification/0.9/eval/src/evaluation_flow.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ Instructions:
9393
- If label text is similar but not exact, you can still pass the test as long as the meaning is the same. (e.g. "Cancel" vs "Cancel Order")
9494
- If the generated output is missing a component that is specified in the user request, it is required to exist in the output in order to pass the test. If it is not specified, it is not required.
9595
- If the request is vague about the contents of a label or other property, you can still pass the test as long as it can be construed as matching the intent.
96+
- Unless explicitly required to be absent by the user request, extra components or attributes are allowed.
9697
9798
Severity Definitions:
9899
- Minor: Merely cosmetic or a slight deviation from the request.

specification/0.9/eval/src/generation_flow.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,17 @@ export const componentGeneratorFlow = ai.defineFlow(
4141
The output MUST be a series of JSON objects, each enclosed in a markdown code block (or a single block with multiple objects).
4242
4343
Standard Instructions:
44-
1. Generate a 'updateComponents' message with surfaceId 'main' and catalogId 'https://a2ui.dev/specification/0.9/standard_catalog_definition.json' containing the requested UI.
45-
2. Ensure all component children are referenced by ID (using the 'children' or 'child' property with IDs), NOT nested inline as objects.
46-
3. If the request involves data binding, you may also generate 'updateDataModel' messages.
47-
4. Among the 'updateComponents' messages in the output, there MUST be one root component with id: 'root'.
48-
5. Components need to be nested within a root layout container (Column, Row). No need to add an extra container if the root is already a layout container.
49-
6. There shouldn't be any orphaned components: no components should be generated which don't have a parent, except for the root component.
50-
7. Do NOT output a list of lists (e.g. [[...]]). Output individual JSON objects separated by newlines.
51-
8. STRICTLY follow the JSON Schemas. Do NOT add any properties that are not defined in the schema. Ensure ALL required properties are present.
52-
9. Do NOT invent data bindings or action contexts. Only use them if the prompt explicitly asks for them.
53-
10. Read the 'description' field of each component in the schema carefully. It contains critical usage instructions (e.g. regarding labels, single child limits, and layout behavior) that you MUST follow.
54-
11. **CRITICAL: 'weight' is a TOP-LEVEL property.** Do NOT put 'weight' inside 'props'. It must be a sibling of 'id' and 'props'.
55-
INCORRECT: { "id": "...", "props": { "component": "...", "weight": 1 } }
56-
CORRECT: { "id": "...", "weight": 1, "props": { "component": "..." } }
44+
1. Generate a 'createSurface' message with surfaceId 'main' and catalogId 'https://a2ui.dev/specification/0.9/standard_catalog_definition.json'.
45+
2. Generate a 'updateComponents' message with surfaceId 'main' containing the requested UI.
46+
3. Ensure all component children are referenced by ID (using the 'children' or 'child' property with IDs), NOT nested inline as objects.
47+
4. If the request involves data binding, you may also generate 'updateDataModel' messages.
48+
5. Among the 'updateComponents' messages in the output, there MUST be one root component with id: 'root'.
49+
6. Components need to be nested within a root layout container (Column, Row). No need to add an extra container if the root is already a layout container.
50+
7. There shouldn't be any orphaned components: no components should be generated which don't have a parent, except for the root component.
51+
8. Do NOT output a list of lists (e.g. [[...]]). Output individual JSON objects separated by newlines.
52+
9. STRICTLY follow the JSON Schemas. Do NOT add any properties that are not defined in the schema. Ensure ALL required properties are present.
53+
10. Do NOT invent data bindings or action contexts. Only use them if the prompt explicitly asks for them.
54+
11. Read the 'description' field of each component in the schema carefully. It contains critical usage instructions (e.g. regarding labels, single child limits, and layout behavior) that you MUST follow.
5755
12. Do NOT define components inline inside 'child' or 'children'. Always use a string ID referencing a separate component definition.
5856
13. Do NOT use a 'style' property. Use standard properties like 'alignment', 'distribution', 'usageHint', etc.
5957
14. Do NOT invent properties that are not in the schema. Check the 'properties' list for each component type.

0 commit comments

Comments
 (0)