a2ui-project / a2ui-project/a2ui
Demonstrate streaming generated UI surfaces widget-by-widget in breadth-first order
- 主要言語
- TypeScript
- スター
- 16.4k
- フォーク
- 1.3k
- 平均マージ
- 2日 13時間
- マージ済み PR(30日)
- 134
説明
_↴ Ported from [flutter/genui#282](https://github.com/flutter/genui/issues/282) — originally opened by [jacobsimionato](https://github.com/jacobsimionato) on 2025-09-07._
_Original labels: P2_
_Original assignees: [jacobsimionato](https://github.com/jacobsimionato)_
---
In order to reduce perceived latency, we should support streaming of UI responses.
## Behavior
- User sends query as usual
- Developer integrates Gen UI as usual, with some minor modifications to hook up streamed responses or function calls
- Behind the scenes, Gen UI should progressively build the widget tree, so new elements appear one-by-one as they stream back.
- We should have this functionality behind some configuration flag that the developers sets initially. In the future, this may become the default approach.
## Technical approach
- We need some permissive JSON parser for partial responses that can close brackets etc.
- We should consider how to handle IDs references, which must be able to be temporarily broken. Maybe we can have an optional 'Placeholder' widget which is rendered when IDs refer to widgets that haven't been loaded yet. Alternatively, we can have children refer to their parents by ID, but that makes it difficult to represent ordering in lists etc.
- The schema we already have should be close what is necessary to support streamed results because the widget list is flat
- We may need to switch from a function call integration to structured output integration due to Gemini limitations - see below
- Ideally, we'd still provide the JSON schema and have the LLM require conformance
- We may need to do some tweaks to make sure the fields are outputted in the right order, or make the schema not care. For example, we have a "root" field with the root widget ID which is adjacent to the actual widgets, so it is possible that we don't know the root widget initially and therefore may not be able to start rendering. We could somehow enforce field order (I think @gspencergoog had an idea on how to achieve this?) or else change the format, e.g. have the first element implicitly be the root, have `root=true` as a field of the root element etc.
## Gemini streaming function calls limitation
Once complication here is that we currently integrate with Gemini API via function calls / tool use, but it seems that Gemini does not support streamed function calls - see https://github.com/google-gemini/cookbook/issues/47.
Hopefully, we can work around this by giving the developer the option of using structured output instead.
It sounds like OpenAI and Anthropic models don't have this limitation, so we could try them too.
---
### 2 comment(s) from the original issue
**[gspencergoog](https://github.com/gspencergoog)** commented on 2025-09-08:
> We need some permissive JSON parser for partial responses that can close brackets etc.
I actually don't think we do. We just need to define the format in such a way that it can be expressed using small objects and a way to do incremental changes, and then use JSONL to stream them. Having a permissive parser is only one part of doing this: you have to end up with not only a valid object, but a fully specified layout. For instance, sending just a widget id:
```json
{
"id": "my_widget"
}
```
doesn't do anything useful if your object needs at least a type to be able to be rendered, and even then having only part of the parameters for an object often doesn't make it renderable, and will probably result in a bunch of jumping around even if it is, since later updates will include the missing settings that change the alignment, size, etc.
Better is to have it send whole objects in each line, and progressively build the UI:
```json
{ "id": "my_widget", "type": "Column", "children": []}
{ "id": "my_title", "type": "Text", "value": "My Title"}
{ "id": "my_widget", "type": "Column", "children": ["my_title"]}
```
Or, as a patch protocol:
```json
{ "id": "my_widget", "type": "Column", "children": []}
{ "id": "my_title", "type": "Text", "value": "My Title"}
{ "op": "add_children", "path": "/my_widget", "children": ["my_title"]}
```
---
**[jacobsimionato](https://github.com/jacobsimionato)** commented on 2025-11-03:
This is now theoretically possible via A2A protocol, but we need to demonstrate it.
コントリビューションガイド
評価
この issue はまだ評価されていません。