Expand description
Incremental parsing of a model’s A2UI output as it streams.
crate::toolkit::parser waits for the whole generation before it can hand
back a surface. That is the wrong shape for A2UI, whose entire component
model exists so a renderer can start painting as soon as root arrives.
StreamParser closes that gap: feed it token chunks and it emits renderable
A2UI as soon as enough of the tree has arrived to draw something.
use ag_ui_a2ui::catalog::Catalog;
use ag_ui_a2ui::toolkit::streaming::StreamParser;
let catalog = Catalog::basic();
let mut parser = StreamParser::new(catalog);
// Conversational text comes out immediately.
let parts = parser.process_chunk("Here you go. <a2ui-json>[").unwrap();
assert_eq!(parts[0].text, "Here you go. ");
// A message is emitted the moment it closes, mid-array.
let parts = parser
.process_chunk(r#"{"version":"v0.9","createSurface":{"surfaceId":"s","catalogId":"c"}},"#)
.unwrap();
assert_eq!(parts[0].a2ui.as_ref().unwrap().len(), 1);§What “as soon as possible” actually means
Four mechanisms do the work, and they are the reason this is not simply a JSON parser fed one byte at a time:
Healing cut tokens. A chunk boundary can land anywhere, including inside
a string. The parser closes open braces and brackets to make the fragment
parseable, but it will only close an open string for a key on the cuttable
list — text, label, hint and friends. Cutting "id" or "path" would
invent an identifier or a binding that the model never wrote, so those
fragments are held back until the next chunk instead.
Placeholder synthesis. A parent usually arrives before its children. Its
child references are rewritten to loading_<id> and a stand-in component is
emitted alongside, so the renderer can lay out the tree immediately and swap
in the real component when it lands.
Reachability filtering. Only components reachable from root are
emitted. A component that arrives before its parent is cached, not sent — it
would have nowhere to attach — and is re-sent as part of the tree once the
path from the root exists.
Validation as a filter, not a failure. Partial fragments are validated and silently dropped if they do not hold up. A placeholder of a type the catalog does not define, or a component still missing a required property, is simply not emitted yet. Structural failures that no further input can fix — a reference loop, a message that matches no envelope — are errors.
§State is per-stream
A parser instance carries the surface state for one generation: which surfaces exist, which components have been seen and emitted, and the data model so far. Create a new one per generation.
Structs§
- Stream
Parser - Parses a model’s A2UI output incrementally, one chunk at a time.
Constants§
- DEFAULT_
CUTTABLE_ KEYS - Keys whose string values may be closed early when a chunk cuts them.