Skip to content

feat(agent): introduce a2ui-xml inference format - #3989

Open
jacobsimionato wants to merge 7 commits into
mainfrom
feat/a2ui-xml-format
Open

jacobsimionato wants to merge 7 commits into
mainfrom
feat/a2ui-xml-format

Conversation

@jacobsimionato

@jacobsimionato jacobsimionato commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary & Rationale

This pull request introduces a2ui-xml, a high-performance XML-based inference format for A2UI in the Python Agent SDK (a2ui.inference_formats.experimental.a2ui_xml) and cross-language conformance specifications.

While express provides a terse Python-like syntax for UI definitions, LLM inference analysis on large evaluation suites revealed that models exhibit lower cognitive load and significantly reduced syntax failures when generating structured component trees using an XML paradigm tailored for A2UI:

  1. Natural Tree Alignment: Hierarchical nesting (<Card><Header .../><Body .../></Card>) matches XML's native structure, eliminating boilerplate variable assignments (c1 = ..., root = Column(children=[...])) and variable resolution tracking.
  2. Compact System Prompts: Catalog-agnostic rule generation with depth-3 item expansions and micro-anchors reduces system prompt overhead from ~1,411 tokens to 876 tokens (-37.9%).
  3. Resilient Progressive Streaming: Built-in attribute quote healing, unclosed tag recovery, and token-level streaming emit valid A2UI renderer messages incrementally without waiting for turn completion.
  4. First-Class Protocol Support: Full fidelity for data model bindings (@path), action expressions ($Event(...)), inline check assertions (<Check .../>), and surface lifecycle directives across both v0.9 and v1.0 specifications.

Benchmark Results (Full A2UI Eval Suite)

Comprehensive evaluation on the complete 93-case A2UI benchmark suite (1,422 live inference trials covering complex forms, dashboards, dynamic data bindings, and action handlers) demonstrates substantial gains across reliability, token efficiency, and latency:

Metric express (Baseline) a2ui-xml Relative Delta
Pass Rate (N=93) 91.4% (85/93) 100.0% (93/93) +8.6% (Zero Failures)
Generation Tokens / Pass 2,274.5 1,915.1 -15.8%
Output Tokens 400.3 378.4 -5.5%
Thinking / Reasoning Tokens 1,874.2 1,536.7 -18.0%
Time to First Frame (TTFF) 10.63s 9.22s -13.3% faster
System Prompt Size 1,411 tokens 876 tokens -37.9% reduction

Models spend ~18% fewer thinking tokens reasoning about scoping and intermediate variables, leading directly to faster time-to-first-frame and flawless completion rates across challenging edge cases.


Key Changes

Python Agent SDK (python/a2ui_agent)

  • Experimental Inference Format Package (a2ui.inference_formats.experimental.a2ui_xml):
    • A2uiXmlFormat & A2uiXmlFormatFactory: Primary format integration decorated with @experimental, supporting both v0.9 and v1.0 protocols with progressive streaming enabled by default.
    • A2uiXmlCompiler: Compiles XML tags and inline attributes into standard A2UI createSurface, updateComponents, and updateDataModel messages. Supports lenient attribute quote healing, <Check> assertions, and $Event(...) bindings.
    • A2uiXmlDecompiler: Lossless decompilation of A2UI component trees back into clean XML with preserve_ids=True.
    • A2uiXmlParser: Delimited response block extractor unwrapping <a2ui> tags.
    • A2uiXmlResponseStream: Incremental chunk parser supporting token-level progressive rendering and tag-closing heuristics.
    • A2uiXmlPromptGenerator: Catalog-agnostic prompt generator producing minimal, highly structured grammar guides.
    • A2uiXmlScanner: AST scanner enforcing structural validity and Six-Laws syntax invariants without code generation dependencies.
  • Specification Round-Trip Suite:
    • Integrated A2uiXmlFormat into tests/test_specification_roundtrip.py, confirming 100% round-trip fidelity across all 43 specification example payloads.
  • Package Exports:
    • Exported in a2ui.inference_formats.experimental alongside atom and elemental, and re-exported in a2ui.inference_formats.__init__.py.
  • Changelog:
    • Added public release notes in python/a2ui_agent/CHANGELOG.md under ## Unreleased.

Cross-Language Conformance Suites (conformance/)

  • Updated conformance/conformance_schema.json to register "a2ui-xml" in InferenceFormatName.
  • Added test suites under conformance/agent/a2ui_xml/:
    • compiler.yaml: Root surfaces, nested children, data model bindings, action events, escaped dollar paths, currency literals, surface update directives, and syntax error cases.
    • decompiler.yaml: Component-to-XML decompilation, nested columns, data bindings, action events, data models, and round-trip preservation.
    • prompt_generator.yaml: Multi-catalog prompt generation and component pruning.
    • response_parser.yaml: Tag unwrapping, mixed text responses, and multi-turn payloads.
    • response_streaming.yaml: Incremental chunk streaming and partial block validation.

Evaluation Suite (eval/)

  • Integrated a2ui_xml into eval/a2ui_eval/runner.py and eval/a2ui_eval/strategies/.
  • Added unit tests in eval/tests/test_runner.py and eval/tests/test_strategies.py.

Verification & Testing

  • Specification Roundtrip:
    • python/a2ui_agent/tests/test_specification_roundtrip.py: 43/43 passed (100% roundtrip pass rate across all v1.0 and v0.9 basic catalog examples).
  • Unit Tests:
    • python/a2ui_agent/tests/a2ui_xml/: 119 unit tests covering compilation, decompilation, expression parsing, scanner laws, progressive streaming, and tuned configurations (119 passed).
    • Full python/a2ui_agent pytest suite: 1,891 passed, 2 xfailed, 0 failures.
  • Conformance Suite:
    • python/a2ui_agent/tests/conformance/test_conformance.py: All 5 conformance suites executed against a2ui-xml (536 passed, 2 xfailed, 0 failures).
  • Eval Suite:
    • eval/tests/: All unit tests passed (218 passed).
  • Type Checking & Code Formatting:
    • uv run mypy .: 0 issues found in 204 source files.
    • python3 scripts/fix_licenses.py --check: All license and copyright checks passed successfully.
    • uv run pyink .: Cleanly formatted, 0 diffs.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the new a2ui-xml inference format, providing comprehensive support for XML-based UI generation, decompilation, parsing, and progressive streaming. The feedback suggests optimizing schema resolution in the decompiler, refactoring nested helper functions to the module level in response_stream.py to prevent redundant recreation, removing an unused import, and enhancing validation to recursively inspect mapped values (including lists) for complex non-binding structures.

Comment on lines +1030 to +1036
if any(
isinstance(v, (dict, list))
and not self._is_data_binding_dict(v)
and not self._is_function_call_dict(v)
for v in ctx_val.values()
if isinstance(v, dict)
):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The generator expression filter if isinstance(v, dict) ignores list values. If ctx_val contains a list of complex objects, it won't be checked here and might fail to trigger the <Action> fence fallback. To ensure no nested data bindings bypass the validation check, recursively inspect the mapped value (including nested lists and dictionaries). Additionally, to avoid recreating helper functions inside nested functions, define a recursive helper at the module level, and ensure we verify the type (e.g., isinstance(v, dict)) before performing key-based lookups to prevent TypeError crashes.

            if any(
                self._has_complex_non_binding(v)
                for v in ctx_val.values()
            ):
References
  1. When validating that a property schema disallows dynamic data bindings (paths), recursively inspect the mapped value (including nested lists and dictionaries) to ensure no nested data bindings bypass the validation check.
  2. When validation checks or function arguments can be represented as either a dictionary (named) or a list (positional), always verify the type (e.g., using isinstance(args, dict)) before performing key-based lookups or assignments to prevent TypeError crashes.
  3. Avoid defining helper functions inside loops or nested functions where they are redundantly recreated on every iteration. Define them at the module level to improve performance and maintainability.

)
from a2ui.utils import RendererState

from .compiler import A2uiXmlCompiler, _CompileContext, _normalize_identifier_key

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The import of _normalize_identifier_key is unused in this module and can be safely removed to keep the imports clean.

Suggested change
from .compiler import A2uiXmlCompiler, _CompileContext, _normalize_identifier_key
from .compiler import A2uiXmlCompiler, _CompileContext

Comment on lines +723 to +739
obj_array_count = sum(
1
for p in properties
if isinstance(
helper.resolve_item_schema(
helper.get_property_schema(comp_name, p)
),
dict,
)
and "properties"
in (
helper.resolve_item_schema(
helper.get_property_schema(comp_name, p)
)
or {}
)
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The obj_array_count calculation resolves the item schema twice per property. We can optimize this by resolving the schema once and checking both conditions on the resolved schema. Ensure we verify the type (using isinstance(item_s, dict)) before performing key-based lookups to prevent TypeError crashes.

                obj_array_count = 0
                for p in properties:
                    item_s = helper.resolve_item_schema(
                        helper.get_property_schema(comp_name, p)
                    )
                    if isinstance(item_s, dict) and "properties" in item_s:
                        obj_array_count += 1
References
  1. When validation checks or function arguments can be represented as either a dictionary (named) or a list (positional), always verify the type (e.g., using isinstance(args, dict)) before performing key-based lookups or assignments to prevent TypeError crashes.

Comment on lines +453 to +462
def walk_and_collect_closed(elem: ElementNode, *, is_top: bool) -> None:
nonlocal has_closed_data
# Reserve this element's own ID in exact compiler preorder
if is_top and "id" not in elem.attrs:
# Top-level element gets either "root" or "r<N>", never `c<N>`
pass
elif "id" not in elem.attrs:
own_id = ctx.allocate_id()
else:
own_id = elem.attrs["id"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The helper function walk_and_collect_closed is currently defined as a nested function, which means it is redundantly recreated on every invocation of the outer function. To improve performance and maintainability, define it at the module level. Additionally, ensure that own_id is safely initialized to avoid potential unbound local variable warnings.

def _walk_and_collect_closed(elem: ElementNode, ctx: _CompileContext, closed_ids: list, *, is_top: bool) -> None:
    own_id = None
    if is_top and "id" not in elem.attrs:
        pass
    elif "id" not in elem.attrs:
        own_id = ctx.allocate_id()
    else:
        own_id = elem.attrs["id"]
References
  1. Avoid defining helper functions inside loops or nested functions where they are redundantly recreated on every iteration. Define them at the module level to improve performance and maintainability.

Adds the `a2ui-xml` inference format to the Python Agent SDK and conformance suite:
- Implements `A2uiXmlFormat`, `A2uiXmlFormatFactory`, `A2uiXmlCompiler`, `A2uiXmlDecompiler`, `A2uiXmlParser`, `A2uiXmlPromptGenerator`, and `A2uiXmlResponseStream`
- Supports concise XML-based UI generation with inline component hierarchies, progressive streaming with automatic attribute/tag healing, JSON Pointer data-model binding, and catalog-driven schema pruning
- Adds comprehensive cross-language conformance test suites for compiler, decompiler, response parser, streaming, and prompt generation
- Registers `a2ui-xml` strategy in evaluation harness and updates CHANGELOG
…onformance tests

- Move `a2ui_xml` package to `a2ui.inference_formats.experimental.a2ui_xml` alongside `atom` and `elemental`
- Decorate `A2uiXmlFormat` with `@experimental`
- Add `A2uiXmlFormat` specification roundtrip testing to `test_specification_roundtrip.py` (100% pass across all 43 spec examples)
- Add new conformance test cases for literal currency, backslash escaped dollar paths, surface update directives, data bindings, actions, and data models
- Update exports and CHANGELOG
Address gaps identified during comprehensive feature parity audit against express:
- Prevent component properties named data/dataModel from being hijacked into surface dataModel
- Preserve slot component property when declared in component schema
- Support digit-starting relative data paths (bash, bash/title) in expressions and decompiler
- Fix numeric coercion on unconstrained {} schemas in <Check> and function calls
- Fix scanner crash on trailing escaped backslash in attribute values
- Fix literal string escaping, leading whitespace expression matching, and JSX brace unwrapping
- Emit explicit children="[]" and items="[]" for empty child/item lists in decompiler
- Enforce v0.9 catalogId rejection on per-component, per-function, and per-check calls
- Preserve author-specified catalogId in v1.0 single-catalog mode
- Validate Event(...) kwargs, rejecting unknown kwargs
- Prevent conversational prose mentioning </a2ui> from being parsed as UI surfaces
- Support progressive_keys parameter across A2uiXmlFormat, A2uiXmlFormatFactory, and A2uiXmlParser
- Expose .version property on A2uiXmlFormat and inherit A2uiCompilationError
- Support surface-level catalogId validation in single-catalog mode
- Add 40 remediation unit tests and 14 new conformance test cases
…ner harnesses

Register agent/a2ui_xml/*.yaml in Python ACTIVE_SUITES and Dart/TS unsupported suites.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant