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
72 changes: 65 additions & 7 deletions specification/0.9/docs/a2ui_protocol.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<!-- markdownlint-disable MD041 -->
<!-- markdownlint-disable MD033 -->
<!-- markdownlint-disable MD034 -->
<div style="text-align: center;">
<div class="centered-logo-text-group">
<img src="../../../docs/assets/A2UI_dark.svg" alt="A2UI Protocol Logo" width="100">
Expand Down Expand Up @@ -38,6 +39,8 @@ This "prompt-first" approach offers several advantages:

The main disadvantage of this approach is that it requires more complex post-generation validation, as the LLM is not strictly constrained by the schema. This requires robust error handling and correction, so the system can identify discrepancies and attempt to fix them before rendering, or request a retry or correction from the LLM.

See [the evolution guide](evolution_guide.md) for a detailed explanation of the differences between v0.8 and v0.9.

## Protocol Overview & Data Flow

The A2UI protocol uses a unidirectional stream of JSON messages from the server to the client to describe and update the UI. The client consumes this stream, builds the UI, and renders it. User interactions are handled separately, typically by sending events to a different endpoint, which may in turn trigger new messages on the UI stream.
Expand Down Expand Up @@ -281,8 +284,6 @@ By default, all components operate in the **Root Scope**.
- The Root Scope corresponds to the top-level object of the `value` provided in `updateDataModel`.
- Paths starting with `/` (e.g., `/user/profile/name`) are **Absolute Paths**. They always resolve from the root of the Data Model, regardless of where the component is nested in the UI tree.



#### Collection Scopes (Relative Paths)

When a container component (such as `Column`, `Row`, or `List`) utilizes the **Template** feature of `ChildList`, it creates a new **Child Scope** for each item in the bound array.
Expand Down Expand Up @@ -339,6 +340,55 @@ When a container component (such as `Column`, `Row`, or `List`) utilizes the **T
}
```

### String Interpolation

A2UI supports embedding dynamic expressions directly within string properties (any property defined as a `DynamicString` in the catalog). This allowing for mixing static text with data model values and function results.

#### Syntax

Interpolated expressions are enclosed in `${...}`. To include a literal `${` in a string, it must be escaped as `\${`.

#### Data Model Binding

Values from the data model can be interpolated using their JSON Pointer path.

- `${/user/profile/name}`: Absolute path.
- `${firstName}`: Relative path (resolved against the current collection scope).

**Example:**

```json
{
"id": "user_welcome",
"component": "Text",
"text": "Hello, ${/user/firstName}! Welcome back to ${/appName}."
}
```

#### Client-Side Functions

Results of client-side functions can be interpolated. Function calls are identified by the presence of parentheses `()`.

- `${now()}`: A function with no arguments.
- `${formatDate(${/currentDate}, 'yyyy-MM-dd')}`: A function with positional arguments.

Arguments can be **Literals** (quoted strings, numbers, or booleans), or **Nested Expressions**.

#### Nested Interpolation

Expressions can be nested using additional `${...}` wrappers inside an outer expression to make bindings explicit or to chain function calls.

- **Explicit Binding**: `${formatDate(${/currentDate}, 'yyyy-MM-dd')}`
- **Nested Functions**: `${upper(${now()})}`

#### Type Conversion

When a non-string value is interpolated, the client converts it to a string:

- **Numbers/Booleans**: Standard string representation.
- **Null/Undefined**: An empty string `""`.
- **Objects/Arrays**: Stringified as JSON to ensure consistency across different client implementations.

### Two-Way Binding & Input Components

Interactive components that accept user input (`TextField`, `CheckBox`, `Slider`, `ChoicePicker`, `DateTimeInput`) establish a **Two-Way Binding** with the Data Model.
Expand Down Expand Up @@ -381,7 +431,6 @@ It is critical to note that Two-Way Binding is **local to the client**.

4. **Send:** When clicked, the client resolves `/formData/email` (getting "[email protected]") and sends it in the `action` payload.


## Client-Side Logic & Validation

A2UI v0.9 generalizes client-side logic into **Functions**. These can be used for validation, data transformation, and dynamic property binding.
Expand Down Expand Up @@ -421,11 +470,20 @@ Buttons can also define `checks`. If any check fails, the button is automaticall
"checks": [
{
"and": [
{ "call": "required", "args": { "value": { "path": "/formData/terms" } } },
{
"call": "required",
"args": { "value": { "path": "/formData/terms" } }
},
{
"or": [
{ "call": "required", "args": { "value": { "path": "/formData/email" } } },
{ "call": "required", "args": { "value": { "path": "/formData/phone" } } }
{
"call": "required",
"args": { "value": { "path": "/formData/email" } }
},
{
"call": "required",
"args": { "value": { "path": "/formData/phone" } }
}
]
}
],
Expand Down Expand Up @@ -529,7 +587,7 @@ This message is sent by the client upon connection to inform the server of its c

This message is used to report a client-side error to the server.

[`standard_function_catalog.json`]: ../json/standard_function_catalog.json
[`standard_catalog_definition.json`]: ../json/standard_catalog_definition.json
[`common_types.json`]: ../json/common_types.json
[`server_to_client.json`]: ../json/server_to_client.json
[`client_to_server.json`]: ../json/client_to_server.json
Expand Down
19 changes: 18 additions & 1 deletion specification/0.9/docs/evolution_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This document serves as a comprehensive guide to the changes between A2UI versio

## 1. Executive Summary

**Version 0.9 represents a fundamental philosophical shift from "Structured Output First" to "Prompt First."**
Version 0.9 represents a fundamental philosophical shift from "Structured Output First" to "Prompt First."

- **v0.8.1** 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.
- **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).
Expand All @@ -22,6 +22,7 @@ This document serves as a comprehensive guide to the changes between A2UI versio
| **Button Context** | Array of Key-Value pairs | Standard JSON Object |
| **Auxiliary Rules** | N/A | `standard_catalog_rules.txt` |
| **Validation** | Basic Schema | Strict `ValidationFailed` feedback loop |
| **Interpolation** | N/A (Object wrappers only) | Native `${expression}` syntax |

## 2. Architectural & Schema Changes

Expand Down Expand Up @@ -210,6 +211,20 @@ Specifying an unknown surfaceId will cause an error. It is recommended that clie
- **Structure**: The schema allows `string` OR `{ "path": "..." }`.
- **Reason**: Much more natural JSON. `{ "text": "Hello" }` is valid. `{ "value": { "path": "/msg" } }` is valid. No need for `{ "text": { "literalString": "Hello" } }`.

### 5.3. String Interpolation

**v0.8.1:**

- **Strict Envelopes**: Static text and data model references had to be separate or wrapped in explicit objects. Mixing literal text and dynamic values in a single string was not officially supported at the protocol level without custom logic.
- **Structure**: `{ "text": "static" }` OR `{ "text": { "path": "/var" } }`.

**v0.9:**

- **Native Interpolation**: Introduced the `${expression}` syntax for all `DynamicString` properties.
- **Unified Expression Language**: Allows embedding JSON Pointer paths (absolute and relative) and client-side function calls directly within literal strings.
- **Nesting**: Supports recursive nesting of expressions (e.g., `${formatDate(${/timestamp}, 'yyyy-MM-dd')}`).
- **Reason**: Improves "token efficiency" and readability for LLMs. Instead of generating complex JSON objects to combine strings and data, the model can write natural-looking template literals.

## 6. Component-Specific Changes

### 6.1. Button Context
Expand Down Expand Up @@ -267,6 +282,7 @@ Specifying an unknown surfaceId will cause an error. It is recommended that clie

- **Purpose**: To allow the "Prompt-Generate-Validate" loop to work effectively.
- **Mechanism**: If the LLM generates invalid JSON, the system sends back a structured error:

```json
{
"error": {
Expand All @@ -277,6 +293,7 @@ Specifying an unknown surfaceId will cause an error. It is recommended that clie
}
}
```

- **Result**: The LLM sees this and can "self-correct" in the next turn.

## 8. Property Rename Summary (Migration Quick Reference)
Expand Down
2 changes: 1 addition & 1 deletion specification/0.9/json/common_types.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"required": ["call"]
},
"DynamicString": {
"description": "Represents a value that can be either a literal string, a path to a string in the data model, or a function call returning a string.",
"description": "Represents a string that can contain interpolated expressions in the `${expression}` format. Supported expression types include: 1) JSON Pointer paths to the data model (e.g., `${/absolute/path}` or `${relative/path}`), and 2) client-side function calls (e.g., `${now()}`). Function arguments must be literals (quoted strings, numbers, booleans) or nested expressions (e.g., `${formatDate(${/currentDate}, 'MM-dd')}`). To include a literal `${` sequence, escape it as `\\${`.",
"oneOf": [
{ "type": "string" },
{
Expand Down
Loading