Repository navigation
feat(agent): introduce a2ui-xml inference format - #3989
jacobsimionato wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
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.
| 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) | ||
| ): |
There was a problem hiding this comment.
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
- 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.
- 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.
- 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 |
There was a problem hiding this comment.
| 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 {} | ||
| ) | ||
| ) |
There was a problem hiding this comment.
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 += 1References
- 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.
| 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"] |
There was a problem hiding this comment.
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
- 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.
a79c523 to
748b8f8
Compare
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
expressprovides 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:<Card><Header .../><Body .../></Card>) matches XML's native structure, eliminating boilerplate variable assignments (c1 = ...,root = Column(children=[...])) and variable resolution tracking.@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:
express(Baseline)a2ui-xmlModels 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)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 A2UIcreateSurface,updateComponents, andupdateDataModelmessages. Supports lenient attribute quote healing,<Check>assertions, and$Event(...)bindings.A2uiXmlDecompiler: Lossless decompilation of A2UI component trees back into clean XML withpreserve_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.A2uiXmlFormatintotests/test_specification_roundtrip.py, confirming 100% round-trip fidelity across all 43 specification example payloads.a2ui.inference_formats.experimentalalongsideatomandelemental, and re-exported ina2ui.inference_formats.__init__.py.python/a2ui_agent/CHANGELOG.mdunder## Unreleased.Cross-Language Conformance Suites (
conformance/)conformance/conformance_schema.jsonto register"a2ui-xml"inInferenceFormatName.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/)a2ui_xmlintoeval/a2ui_eval/runner.pyandeval/a2ui_eval/strategies/.eval/tests/test_runner.pyandeval/tests/test_strategies.py.Verification & Testing
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).python/a2ui_agent/tests/a2ui_xml/: 119 unit tests covering compilation, decompilation, expression parsing, scanner laws, progressive streaming, and tuned configurations (119 passed).python/a2ui_agentpytest suite: 1,891 passed, 2 xfailed, 0 failures.python/a2ui_agent/tests/conformance/test_conformance.py: All 5 conformance suites executed againsta2ui-xml(536 passed, 2 xfailed, 0 failures).eval/tests/: All unit tests passed (218 passed).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.