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
28 changes: 10 additions & 18 deletions agent_sdks/python/src/a2ui/core/parser/streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
"text",
}

# A safe placeholder used when referencing a child component that hasn't yet streamed in.
PLACEHOLDER_COMPONENT = {
"Row": {
"children": {"explicitList": []},
}
}


class A2uiStreamParser:
"""Parses a stream of text for A2UI JSON messages with fine-grained component yielding."""
Expand Down Expand Up @@ -1230,12 +1237,7 @@ def traverse(obj, parent_key=None):
valid_children.append(placeholder_id)
placeholder_comp = {
"id": placeholder_id,
"component": {
"Text": {
"text": {"literalString": f"Loading {child_id}..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
}
# Avoid duplicates in extra_components
if not any(ec["id"] == placeholder_id for ec in extra_components):
Expand All @@ -1253,12 +1255,7 @@ def traverse(obj, parent_key=None):
valid_children.append(placeholder_id)
placeholder_comp = {
"id": placeholder_id,
"component": {
"Text": {
"text": {"literalString": "Loading children..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
}
if not any(ec["id"] == placeholder_id for ec in extra_components):
extra_components.append(placeholder_comp)
Expand All @@ -1270,12 +1267,7 @@ def traverse(obj, parent_key=None):
obj[field] = placeholder_id
placeholder_comp = {
"id": placeholder_id,
"component": {
"Text": {
"text": {"literalString": f"Loading {child_id}..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
}
if not any(ec["id"] == placeholder_id for ec in extra_components):
extra_components.append(placeholder_comp)
Expand Down
109 changes: 30 additions & 79 deletions agent_sdks/python/tests/core/parser/test_streaming_v08.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
MSG_TYPE_DATA_MODEL_UPDATE,
)
from a2ui.core.schema.catalog import A2uiCatalog
from a2ui.core.parser.streaming import A2uiStreamParser
from a2ui.core.parser.streaming import A2uiStreamParser, PLACEHOLDER_COMPONENT
from a2ui.core.parser.response_part import ResponsePart


Expand Down Expand Up @@ -145,6 +145,22 @@ def mock_catalog():
"Text": {"type": "object", "additionalProperties": True},
"Loading": {"type": "object", "additionalProperties": True},
"List": {"type": "object", "additionalProperties": True},
"Row": {
"type": "object",
"properties": {
"children": {
"type": "object",
"properties": {
"explicitList": {
"type": "array",
"items": {"type": "string"},
},
"required": ["componentId", "dataBinding"],
},
}
},
"required": ["children"],
},
"Column": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -319,12 +335,7 @@ def test_incremental_yielding_v08(mock_catalog):
},
{
"id": "loading_children_root-column",
"component": {
"Text": {
"text": {"literalString": "Loading children..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
],
}
Expand All @@ -345,12 +356,7 @@ def test_incremental_yielding_v08(mock_catalog):
},
{
"id": "loading_c1",
"component": {
"Text": {
"text": {"literalString": "Loading c1..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
],
}
Expand All @@ -373,21 +379,11 @@ def test_incremental_yielding_v08(mock_catalog):
},
{
"id": "loading_c1",
"component": {
"Text": {
"text": {"literalString": "Loading c1..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
{
"id": "loading_c2",
"component": {
"Text": {
"text": {"literalString": "Loading c2..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
],
}
Expand Down Expand Up @@ -426,12 +422,7 @@ def test_incremental_yielding_v08(mock_catalog):
},
{
"id": "loading_c2",
"component": {
"Text": {
"text": {"literalString": "Loading c2..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
],
}
Expand Down Expand Up @@ -811,12 +802,7 @@ def test_partial_single_child_string(mock_catalog):
},
{
"id": "loading_c1",
"component": {
"Text": {
"text": {"literalString": "Loading c1..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
],
}
Expand Down Expand Up @@ -884,12 +870,7 @@ def test_partial_template_componentId(mock_catalog):
},
{
"id": "loading_c1",
"component": {
"Text": {
"text": {"literalString": "Loading c1..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
],
}
Expand Down Expand Up @@ -957,30 +938,15 @@ def test_partial_children_lists(mock_catalog):
},
{
"id": "loading_c1",
"component": {
"Text": {
"text": {"literalString": "Loading c1..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
{
"id": "loading_c2",
"component": {
"Text": {
"text": {"literalString": "Loading c2..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
{
"id": "loading_c3",
"component": {
"Text": {
"text": {"literalString": "Loading c3..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
],
}
Expand Down Expand Up @@ -1013,21 +979,11 @@ def test_partial_children_lists(mock_catalog):
},
{
"id": "loading_c2",
"component": {
"Text": {
"text": {"literalString": "Loading c2..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
{
"id": "loading_c3",
"component": {
"Text": {
"text": {"literalString": "Loading c3..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
},
],
}
Expand Down Expand Up @@ -2073,12 +2029,7 @@ def test_sniff_partial_component_discards_empty_children_dict(mock_catalog):
"id": "root-column",
},
{
"component": {
"Text": {
"text": {"literalString": "Loading item-list..."},
"usageHint": "caption",
}
},
"component": PLACEHOLDER_COMPONENT,
"id": "loading_item-list",
},
],
Expand Down
Loading