Skip to content

.NET: Fix AG-UI SSE events written with explicit nulls - #7920

Open
Atharva Vichare (atty57) wants to merge 1 commit into
microsoft:mainfrom
atty57:fix/7919-agui-json-resolver-order
Open

.NET: Fix AG-UI SSE events written with explicit nulls#7920
Atharva Vichare (atty57) wants to merge 1 commit into
microsoft:mainfrom
atty57:fix/7919-agui-json-resolver-order

Conversation

@atty57

Copy link
Copy Markdown
Contributor

Motivation & Context

AddAGUIServer() configures the ASP.NET Core JsonOptions used to serialize AG-UI events, but it appends its resolvers to TypeInfoResolverChain. ASP.NET Core already places a reflection-based resolver at the head of that chain, so both appended resolvers are unreachable for every type reflection can handle — the AG-UI wire-format rules never applied on this path.

That was invisible with AGUI 0.0.5, whose omit-empty rule rides on attributes the reflection resolver honors. AGUI 0.0.6 moved the rule to a type-info modifier on AGUIJsonUtilities.DefaultTypeInfoResolver, which only applies when that resolver is actually consulted. Bumping the pin without fixing the ordering makes every SSE event go out with explicit nulls for its optional fields:

data: {"type":"RUN_STARTED","threadId":"…","runId":"…","parentRunId":null,"input":null,"timestamp":null,"rawEvent":null,"metadata":null}

@ag-ui/client declares those fields optional, not nullable, so it rejects the first event with a ZodError and the whole run fails client-side.

Description & Review Guide

  • What are the major changes?

    • ConfigureAGUIJsonOptions now Inserts both resolvers ahead of the reflection resolver instead of appending them, and composes AGUIJsonUtilities.DefaultTypeInfoResolver rather than AGUIJsonSerializerContext.Default directly — the composition the AGUI docs direct integrators to use.
    • AGUI pins move 0.0.5 → 0.0.6 in the same commit, since DefaultTypeInfoResolver is new in 0.0.6.
    • A regression test asserts the serialized event carries no explicit nulls.
  • What is the impact of these changes?

    • The wire format is unchanged from what 0.0.5 emitted today, so this is a fix rather than a behavior change for existing clients. Without it, the moment the AGUI pin moves the emitted events break AG-UI receivers.
  • What do you want reviewers to focus on?

    • Insert(0, …) places the AG-UI resolver ahead of any resolver an application registered itself. That is what the wire format requires and matches the workaround in the issue, but the precedence call is worth a maintainer's eye.
    • Separately, and left alone here: ChatMessage also serializes with explicit nulls through these options for the same ordering reason. That affects request/response bodies rather than the AG-UI event stream, so it felt out of scope for this fix — happy to fold it in if you would rather it move together.

Related Issue

Fixes #7919

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Verified locally: 19/19 unit tests pass on net8.0 and net9.0, 13/13 on net10.0. The new test fails against the previous Add(...) ordering and passes with the fix.

ConfigureAGUIJsonOptions appended its resolvers to the ASP.NET Core
TypeInfoResolverChain, which already has a reflection-based resolver at
its head. Both appended resolvers were therefore unreachable for every
type reflection can handle, so the AG-UI wire-format rules never applied.

This was invisible with AGUI 0.0.5, whose omit-empty rule rides on
attributes the reflection resolver honors. AGUI 0.0.6 moved that rule to
a type-info modifier on AGUIJsonUtilities.DefaultTypeInfoResolver, which
only applies when that resolver is actually consulted. Bumping the pin
without fixing the ordering makes every SSE event go out with explicit
nulls for its optional fields ("parentRunId": null and similar), which
@ag-ui/client rejects with a ZodError.

Insert both resolvers ahead of the reflection resolver and compose
AGUIJsonUtilities.DefaultTypeInfoResolver rather than the context
directly, as the AGUI docs direct. The AGUI pin moves to 0.0.6 in the
same change since DefaultTypeInfoResolver is new in that version.

Adds a regression test asserting the serialized event carries no explicit
nulls; it fails against the previous ordering.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes AG-UI SSE serialization so optional fields are omitted rather than emitted as null.

Changes:

  • Prioritizes AG-UI serialization metadata.
  • Updates AG-UI packages to 0.0.6.
  • Adds null-omission regression coverage.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
ConfigureAGUIJsonOptions.cs Reorders and updates JSON resolvers.
ConfigureAGUIJsonOptionsTests.cs Tests optional-field omission.
Directory.Packages.props Updates AG-UI package versions.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

//
// Agent Framework abstractions follow so that M.E.AI types are handled via its resolver.
chain.Insert(0, AGUIJsonUtilities.DefaultTypeInfoResolver);
chain.Insert(1, AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

.NET Usage: [Issues, PRs], Target: .Net

Projects

None yet

Development

Successfully merging this pull request may close these issues.

.NET: [Bug]: AddAGUIServer writes explicit nulls on the AG-UI wire with AGUI 0.0.6 (rejected by @ag-ui/client)

2 participants