Reads model output written in the direct JSON format: conversational
text with A2UI message lists between <a2ui-json> and </a2ui-json>
tags.
{:ok, parser} = A2UIAgent.DirectJson.Parser.new(catalogs)
{:ok, parts} =
A2UIAgent.DirectJson.Parser.parse_response(parser, """
Here is your surface.
<a2ui-json>[{"version": "v1.0", "deleteSurface": {"surfaceId": "s1"}}]</a2ui-json>
""")
parts
#=> [{:text, "Here is your surface."},
# {:a2ui, [%{"version" => "v1.0", "deleteSurface" => %{"surfaceId" => "s1"}}]}]A block is read leniently (A2UIAgent.DirectJson.JsonReader: trailing
commas, raw line breaks in strings, curly quotes), a Markdown code
fence around it is dropped, and a single message is read as a list of
one. The messages are then checked with
A2UIAgent.PayloadValidator.
Parts are {:text, text} and {:a2ui, messages}. unwrap/1 returns
raw parts, {:text, text} and {:a2ui_raw, block, final?}, where
final? is false for a block whose closing tag has not arrived.
Summary
Functions
Compiles one block into validated messages.
Writes messages back out as the pretty-printed JSON a model would have
emitted, each message's keys in the protocol's order
(A2UICore.KeyOrder.message/1). Text in that order streams:
parse_chunk/3 can pass a message on as soon as its surfaceId has
arrived, and a component list one component at a time.
Whether content holds an <a2ui-json> block; with complete: true,
a block whose closing tag has arrived.
Creates a parser for catalogs (default catalog first). Option:
:progressive_keys — string properties the streaming reader may emit
before their closing quote.
Reads the next chunk of a streamed response and returns the parts it completes or changes.
Parses a complete model response into parts.
Splits model output into text and raw blocks, without compiling them.
Writes parts out as model output: text as it is, each block between tags, joined by newlines.
Encloses decompiled blocks in one pair of tags.
Types
A part of a parsed response.
A part of an unwrapped response: text or an uncompiled block.
Functions
@spec compile(t(), String.t()) :: {:ok, [map()]} | {:error, A2UICore.Error.t()}
Compiles one block into validated messages.
An empty block or one that does not read as JSON is a :parse error;
messages a renderer holding the catalogs would reject are a
:validation error.
Writes messages back out as the pretty-printed JSON a model would have
emitted, each message's keys in the protocol's order
(A2UICore.KeyOrder.message/1). Text in that order streams:
parse_chunk/3 can pass a message on as soon as its surfaceId has
arrived, and a component list one component at a time.
Whether content holds an <a2ui-json> block; with complete: true,
a block whose closing tag has arrived.
@spec new([A2UICore.Catalog.t()], keyword()) :: {:ok, t()} | {:error, A2UICore.Error.t()}
Creates a parser for catalogs (default catalog first). Option:
:progressive_keys — string properties the streaming reader may emit
before their closing quote.
The catalogs must be non-empty, have distinct ids and share a protocol version.
@spec parse_chunk(t(), String.t(), keyword()) :: {:ok, t(), [part()]} | {:error, A2UICore.Error.t()}
Reads the next chunk of a streamed response and returns the parts it completes or changes.
Text is emitted as it arrives, trimmed at the edges of each run of
text; a possible start of an <a2ui-json> tag is held back until the
next chunk settles it. Inside a block, each message is emitted as soon
as it reads (A2UIAgent.DirectJson.JsonReader.read_partial_items/3)
and validates, and again whenever it grows; a message that has not
changed is not emitted twice. A createSurface waits until its object
closes, and so do the messages after it. When the block closes it is
compiled in full, so a block that does not validate is an error then.
From v1.0 a component is held back until its object closes, so an
updateComponents message holds only closed components. Later chunks
cannot make such a message valid, so one that does not validate is an
error on the chunk that closes the offending component.
With wrapped: false the whole response is one block without tags. A
response is either wrapped or not: every chunk must say the same.
@spec parse_response(t(), String.t(), keyword()) :: {:ok, [part()]} | {:error, A2UICore.Error.t()}
Parses a complete model response into parts.
With wrapped: false the whole response is one block. Otherwise each
block is compiled in turn and the first failure is returned.
Splits model output into text and raw blocks, without compiling them.
Writes parts out as model output: text as it is, each block between tags, joined by newlines.
Encloses decompiled blocks in one pair of tags.