feat(dotnet): native A2UI for the ag-ui .NET SDK + dojo demos - #2262
Open
ranst91 wants to merge 7 commits into
Open
feat(dotnet): native A2UI for the ag-ui .NET SDK + dojo demos#2262ranst91 wants to merge 7 commits into
ranst91 wants to merge 7 commits into
Conversation
…streaming)
Adds A2UI (agent-generated UI) support to the ag-ui .NET SDK natively, so a
.NET agent can produce A2UI v0.9 surfaces over the AG-UI protocol without any
external framework dependency. Delivers all four A2UI pillars: auto-inject,
progressive streaming, error recovery, and subagent-based generation.
- New AGUI.A2UI package:
- Toolkit/ : framework-agnostic validator, validate-and-retry recovery loop
(structured a2ui_recovery_exhausted envelope on exhaustion), prior-surface
walk, envelope assembly. Ported behaviour-for-behaviour from the TS/Python
siblings (validation codes, MaxA2UIAttempts, envelope shape all aligned).
- A2UIChatClient : a DelegatingChatClient that auto-injects generate_a2ui and
drives a forced render_a2ui subagent through the recovery loop, streaming the
inner argument fragments so surfaces paint progressively. USER-PREVAILS
(leaves a dev-wired generate_a2ui untouched), per-run injectA2UITool gate
from forwarded RunAgentInput, error classification (transient -> retryable,
cancellation/programmer errors rethrow), and try/finally wire-balancing.
- Sanitizes the subagent conversation to drop the in-flight generate_a2ui tool
call, which providers reject as an unbalanced tool_calls message.
- AGUI.Server: provider-neutral incremental tool-call argument streaming via a
new AGUIStreamOptions.MapStreamingToolCallArguments hook + AGUIToolCallArgumentFragment.
Keeps AGUI.Server free of any provider SDK dependency; callers register an
extractor for their provider.
- AGUIDojoServer sample: /a2ui_dynamic_schema, /a2ui_recovery, /a2ui_advanced
endpoints, with an OpenAI fragment extractor isolated to the sample.
Verified end to end against the AG-UI Dojo (aimock + real OpenAI): surfaces
generate and stream progressively, recovery recovers, wire stays balanced.
Wires the AG-UI Dojo for three A2UI demos on the ag-ui-dotnet integration, served by the .NET AGUIDojoServer sample. - menu.ts: a2ui_dynamic_schema, a2ui_advanced, a2ui_recovery features. - agents.ts: plain HttpAgent endpoints. The .NET adapter auto-injects and handles generate_a2ui server-side, so the dojo runtime only attaches the A2UI painting middleware (injectsA2UITool is false for this integration); no client-side tool injection and no route.ts change needed. - files.json: regenerated. - e2e specs for the three features (aimock fixtures are prompt-matched and integration-agnostic).
CR-loop fixes over the native .NET A2UI work: house-rule compliance, streaming retry protocol-balance, public-API tracking, and test coverage. - Toolkit: convert all record types to sealed classes and split to one-type-per-file (AGUI.A2UI house rules); demote XML docs on internal members; ThrowIfNull consistency. - AGUI.A2UI: add PublicAPI.Shipped/Unshipped.txt so the public surface is analyzer-tracked. - Streaming tap (AGUI.Server): close a stale provider-index tool call when a new first fragment reuses the index, so a retried render_a2ui after a mid-stream failure is not misattributed and never doubles a TOOL_CALL_START; mirror the atomic path's MapCall mapping on the streaming-close branch. - A2UIChatClient: capture the render call id from streamed fragments (new StreamingToolCallArgumentExtractor option) so a mid-stream-failed attempt still emits a balancing tool result, keeping the persisted conversation valid. - Tests: new AGUI.A2UI.UnitTests (ported toolkit suite) resolving the dangling solution reference; StreamingToolCallArgsTest for the tap; a decorator mid-stream-failure test. - Sample: correct the recovery demo comment; robuster OpenAI fragment unwrap. - Dojo: order the recovery e2e assertions after the hard-failure signal so the no-faulty-paint check is not trivially true.
Contributor
Python Preview PackagesVersion
Install with uvAdd the TestPyPI index to your [[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
explicit = trueThen install the packages you need: # Core SDK
uv add 'ag-ui-protocol==0.0.0.dev1785945329' --index testpypi
# Integrations (each already depends on the matching ag-ui-protocol preview)
uv add 'ag-ui-langgraph==0.0.0.dev1785945329' --index testpypi
uv add 'ag-ui-crewai==0.0.0.dev1785945329' --index testpypi
# NOTE: ag-ui-agent-spec depends on pyagentspec (git-only, not on PyPI).
# You will need to install pyagentspec separately from its git repo.
uv add 'ag-ui-agent-spec==0.0.0.dev1785945329' --index testpypi
uv add 'ag_ui_adk==0.0.0.dev1785945329' --index testpypi
uv add 'ag_ui_strands==0.0.0.dev1785945329' --index testpypiInstall with pippip install \
--index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
ag-ui-protocol==0.0.0.dev1785945329
Commit: d00a68b |
@ag-ui/a2a-middleware
@ag-ui/a2ui-middleware
@ag-ui/event-throttle-middleware
@ag-ui/mcp-apps-middleware
@ag-ui/mcp-middleware
@ag-ui/a2a
@ag-ui/adk
@ag-ui/ag2
@ag-ui/agno
@ag-ui/aws-strands
@ag-ui/claude-agent-sdk
@ag-ui/claude-managed-agents
@ag-ui/crewai
@ag-ui/langchain
@ag-ui/langgraph
@ag-ui/llamaindex
@ag-ui/mastra
@ag-ui/pydantic-ai
@ag-ui/vercel-ai-sdk
@ag-ui/watsonx
@ag-ui/a2ui-toolkit
create-ag-ui-app
@ag-ui/client
@ag-ui/core
@ag-ui/encoder
@ag-ui/proto
commit: |
Adds the fourth A2UI demo (a2ui_fixed_schema) for the ag-ui-dotnet integration, completing parity with the other frameworks. Fixed-schema is the author-owns-layout / agent-owns-data pattern: search_flights / search_hotels are plain backend tools whose return value IS a pre-authored a2ui_operations envelope (createSurface + updateComponents with a fixed FlightCard/HotelCard schema + updateDataModel from the tool args). The A2UI painting middleware detects the envelope in the tool result; no generate_a2ui, no subagent, no recovery loop. - AGUIDojoServer sample: A2UIFixedSchemaTools.cs (ported), the /a2ui_fixed_schema endpoint, and JsonArray/JsonObject registrations in the serializer context (the tools take JsonArray params). Component names + catalog id match the dojo's fixedSchemaCatalog. - Dojo: a2ui_fixed_schema wired for ag-ui-dotnet (plain HttpAgent; route.ts already treats it paint-only). Regenerated files.json. Added the e2e spec. Verified on aimock (flight + hotel + multi-surface specs) and against real OpenAI (the tool return envelope survives MEAI serialization and the surface paints; no unbalanced-history 400).
…t split, gate test Injection semantics (Finding 1): - Default injection to OFF, matching the "no injectA2UITool, no injection" contract of the ADK/Strands/Mastra/CrewAI siblings. Turns on only when the run forwards injectA2UITool (bool or custom name) or the backend opts in via A2UIChatClientOptions.InjectA2UITool; a client-forwarded value wins. - Drop the middleware's injected render_a2ui proxy from the planner's tool list so the model calls generate_a2ui instead of painting a surface directly and bypassing the subagent + recovery loop. Custom proxy name via InjectedRenderToolName or the forwarded string form. - ResolveInjection/TryReadForwardedFlag replace ShouldInject. - Sample backend opts in (InjectA2UITool ?? true) so the dojo demos still inject. Gate test: - A2UIInjectionGateTest.cs — 13 cases pinning flag precedence + the proxy drop through the production ToChatRequestContext mapping (98 -> 111 tests). Packaging split (north-star parity with langgraph/adk/strands): - New AGUI.A2UI.Toolkit: pure-BCL validator/recovery/envelope/prompt primitives, no Microsoft.Extensions.AI or framework dependency, so it can back a hand-rolled integration like ag-ui-a2ui-toolkit / @ag-ui/a2ui-toolkit. - AGUI.A2UI keeps only the IChatClient decorator and references the toolkit. - PublicAPI, AGUI.slnx, and unit-dotnet-sdk.yml updated for both projects.
…r history The A2UIChatClient planner loop hand-picked only TextContent and generate_a2ui calls into the assistant turn it fed back next round, dropping any of the developer's own tool call/result pairs that the inner function-invocation layer resolved in the same turn. A round-2 planner was then stranded without the tool context it had just produced. Coalesce the planner's full streamed turn into whole messages (plannerUpdates.ToChatResponse().Messages) and append all of them to history, so narration, the terminal generate_a2ui call, and balanced dev tool call/result pairs all survive. New test RoundTwoHistory_PreservesDeveloperToolCallAndResult pins it (112 tests).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds A2UI (agent-generated UI) to the ag-ui .NET SDK natively — no external framework dependency — plus four AG-UI Dojo demos for the
ag-ui-dotnetintegration. A .NET agent can now produce A2UI v0.9 surfaces over the AG-UI protocol, cross-language-aligned with the TypeScript (@ag-ui/a2ui-toolkit) and Python (ag-ui-a2ui-toolkit) siblings.The four A2UI pillars (all delivered and verified)
A2UIChatClient(aDelegatingChatClient) auto-injectsgenerate_a2ui. Injection is off by default and turns on when the run forwardsinjectA2UITool(boolean or a custom render-tool name) or the backend opts in viaA2UIChatClientOptions.InjectA2UITool— matching the "noinjectA2UITool, no injection" contract of the ADK / Strands / Mastra / CrewAI siblings. A client-forwarded value wins over the backend opt-in (a clientfalse/""opts out); USER-PREVAILS (a dev-wiredgenerate_a2uiis left untouched). The middleware's injectedrender_a2uiproxy is dropped from the planner's tool list so the model callsgenerate_a2uiinstead of painting a surface directly and bypassing the subagent + recovery loop. Customizable viaA2UIChatClientOptions/A2UIToolParams.render_a2uiargument fragments reach the wire as incrementalTOOL_CALL_START/ARGS/ENDvia a provider-neutral tap onAGUI.Server(MapStreamingToolCallArguments). Verified: 172 incremental frames on real gpt-4o; unit-tested.a2ui_recovery_exhaustedenvelope rendered as a tasteful failure. Verified end to end + unit-tested.generate_a2uidelegates to a forcedrender_a2uistructured-output subagent through the toolkit recovery loop.Packages
AGUI.A2UI.Toolkit(new): framework-agnostic primitives (validator, recovery loop, envelope builder, prior-surface walk) ported behavior-for-behavior from the TS/Python siblings. Pure BCL — noMicrosoft.Extensions.AIor framework dependency — so it can back a hand-rolled integration the wayag-ui-a2ui-toolkit/@ag-ui/a2ui-toolkitdo.AGUI.A2UI(new): theA2UIChatClientIChatClientdecorator built on the toolkit.AGUI.Serverstays provider-neutral; the OpenAI fragment extractor lives only in the sample.AGUIDojoServersample:/a2ui_dynamic_schema,/a2ui_recovery,/a2ui_advanced,/a2ui_fixed_schemaendpoints. The fixed-schema demo is the agent-owns-data / author-owns-layout path: plain backend tools (search_flights/search_hotels) return a pre-authoreda2ui_operationsenvelope — nogenerate_a2ui, subagent, or recovery.ag-ui-dotnet(paint-only middleware; the backend auto-injects and handles generation).Testing
AGUI.A2UI.UnitTests(112 — ported toolkit suite, decorator mid-stream-failure + planner-history-preservation tests, and a 13-case injection gate test pinning the flag precedence + render-proxy drop) andAGUI.Server.UnitTestsstreaming-tap tests. e2e Playwright specs per feature. Full solution builds clean; therender_a2uistreaming path is at parity with the LangGraph reference (0 protocol-balance warnings across repeated runs).AGUI.A2UI.Toolkit+AGUI.A2UInow build and test inunit-dotnet-sdk.yml.Related - microsoft/agent-framework#6494