[Blazor] Add Components.AI server tool rendering - #68327
Open
javiercn wants to merge 5 commits into
Open
Conversation
Map server-owned function calls into provider-neutral content blocks and pair each result by call ID, even when multiple invocations remain active. Custom Blazor renderers can observe the loading-to-result transition while unmatched server tools remain hidden by default. Keep UI actions ahead of generic function mapping so browser-owned tools preserve their execution path. The runtime continues to depend only on Microsoft.Extensions.AI and exposes no AG-UI protocol types. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 323b0ef8-7905-4041-8388-a08586c0bd34
Add ToolBlock, ToolParameter, and ToolResult contracts plus an incremental generator that emits strongly typed function handlers and consumer-local registration. Generated handlers bind call arguments and results, pair results by call ID, and fall back to the generic server-tool block when no typed tool matches. Diagnose invalid declarations at compile time, preserve incremental-generator cacheability, support escaped consumer namespaces, and package the analyzer with Components.AI. Deserialization failures surface instead of being silently swallowed, keeping malformed tool payloads observable. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 323b0ef8-7905-4041-8388-a08586c0bd34
Rationale: demonstrate typed server-tool rendering across the real AG-UI HTTP/SSE boundary without moving execution into the client. Implementation: add the backend_tool_rendering endpoint and keyed client, execute get_weather on the API, bind its streamed JSON result to a generated WeatherToolBlock, and render the canonical weather card. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 323b0ef8-7905-4041-8388-a08586c0bd34
Rationale: lock down call-id pairing, generated consumer compatibility, and the real dual-host weather path before adding its recorded model payload. Coverage: add concurrent server-tool mapping tests, generator diagnostics and compilation tests, and a browser scenario that replaces only the API model while preserving AG-UI HTTP/SSE transport. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 323b0ef8-7905-4041-8388-a08586c0bd34
Rationale: keep the deterministic model transcript separate from handwritten server-tool and browser-test code. Recording: capture the get_weather call and its continuation so the dual-host scenario runs without external credentials while retaining real API execution and AG-UI transport. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 323b0ef8-7905-4041-8388-a08586c0bd34
javiercn
force-pushed
the
javiercn-components-ai-04-server-tools
branch
from
August 11, 2026 06:59
df3bcda to
8c3be52
Compare
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.
Overview
This is position 4 in the native stack tracked by #68340 and depends on #68325. Relative to
javiercn-components-ai-03-client-tools(0c83d0c84beaa93b7d5c2a44687675288b4f9093), this layer adds provider-neutral server-function blocks, a typed tool-block source generator, and the canonicalget_weatherdojo scenario through the existing two-process AG-UI HTTP/SSE boundary. The governing constraint is that shipping Components.AI code depends only onMicrosoft.Extensions.AI; AG-UI endpoint, transport, replay, and weather presentation code remain in test assets.Design
The generic contract preserves the original
Microsoft.Extensions.AIcall and result, while deriving stable block identity fromCallId. A renderer can show an active invocation immediately and update the same instance when its result arrives.Typed blocks are an opt-in compile-time layer over that generic contract.
[ToolBlock]selects the function name;[ToolParameter]maps call arguments;[ToolResult]maps the returned payload.ToolParameterAttribute.NameandToolResultAttribute.Nameare equivalent optional key overrides, so the representative declaration below covers both property-mapping classes without repeating them.The generator is packaged as an analyzer, so NuGet consumers receive typed binding without a runtime reflection registry. It emits consumer-local handlers and one local
AddGeneratedToolBlocksextension; this is necessary because analyzer project references do not flow transitively between projects.Handler precedence is deliberate: consumer-generated typed handlers run first, browser-owned UI actions retain priority over the generic server fallback, and only then does the built-in function handler claim an otherwise-unhandled call. Unregistered server calls are kept out of the unknown-block fallback, so applications opt into their presentation rather than exposing raw payloads. Generator diagnostics compress into declaration-shape classes: non-partial/wrong-base/abstract/generic/nested types, empty or duplicate tool names, duplicate argument keys, and read-only mapped properties. Deserialization errors are not swallowed, making malformed server payloads observable.
Implementation
The runtime first emits a block for any unhandled server call. Later updates are offered to all active blocks, and only the block with the exact matching
CallIdconsumes the result; this is what makes simultaneous calls and reverse result ordering safe.The source generator specializes that state machine by tool name and property types. The excerpt below is representative generated output, de-templatized to the actual
WeatherToolBlock: one[ToolResult]complex object accepts both an in-processJsonElementand the JSON string carried by AG-UI. Multiple result properties use the same lifecycle but read named properties from a JSON object; primitive arguments/results use the correspondingJsonElementgetter or conversion.The consumer surface remains a small partial block declaration; generated registration installs its handler before the generic fallback.
The canonical scenario keeps execution on the API.
AGUIDojoApiinjects the executable function intoChatOptionsand wraps either the configured model or credential-free scripted model with function invocation.DojoClienthas only a keyedAGUIChatClientfor this endpoint; it registers the generated renderer but never declares or executesget_weatherlocally.The browser test preserves the production boundary: it replaces only the model inside
AGUIDojoApi, starts API and UI hosts separately, and drives the realAGUIChatClienttransport. The generated recording is isolated in the final commit and contains only the deterministic call/continuation transcript.Outcome
get_weather; AG-UI streams call/result/final text over HTTP/SSE; the client renders one completed typed weather card.Validation on head
8c3be521014a60563c56701b37e3bf2f9700d1d8:BackendToolRenderingScenarioTests: 1/1 passedReview guidance: read the public block/attribute contracts, handler ordering, exact-ID completion, generated consumer compilation, and server-only execution closely. Spot-check the repetitive diagnostic cases, primitive conversion branches, weather-card CSS, and generated recording.
Acceptance criteria:
get_weatheris executable only inAGUIDojoApi; a call produces a typed loading block, its matching result completes that same block even with other calls active, and DojoClient renders the San Francisco20°C / sunnycard before the final assistant summary through the real AG-UI HTTP/SSE path.