Skip to content
Open
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
10 changes: 5 additions & 5 deletions dotnet/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@
<PackageVersion Include="System.Net.Http.Json" Version="10.0.0" />
<PackageVersion Include="System.Net.ServerSentEvents" Version="10.0.10" />
<!-- AG-UI .NET SDK packages (published by the AG-UI team). -->
<PackageVersion Include="AGUI.Abstractions" Version="0.0.5" />
<PackageVersion Include="AGUI.Formatting" Version="0.0.5" />
<PackageVersion Include="AGUI.Protobuf" Version="0.0.5" />
<PackageVersion Include="AGUI.Client" Version="0.0.5" />
<PackageVersion Include="AGUI.Server" Version="0.0.5" />
<PackageVersion Include="AGUI.Abstractions" Version="0.0.6" />
<PackageVersion Include="AGUI.Formatting" Version="0.0.6" />
<PackageVersion Include="AGUI.Protobuf" Version="0.0.6" />
<PackageVersion Include="AGUI.Client" Version="0.0.6" />
<PackageVersion Include="AGUI.Server" Version="0.0.6" />
<PackageVersion Include="System.Text.Json" Version="10.0.11" />
<PackageVersion Include="System.Threading.Channels" Version="10.0.11" />
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ public void Configure(JsonOptions options)
{
var chain = options.SerializerOptions.TypeInfoResolverChain;

// Agent Framework abstractions first to ensure M.E.AI types are handled via its resolver,
// followed by the AG-UI wire-format resolver for protocol types (the AG-UI context is needed
// on the net10 TypedResults.ServerSentEvents path, which serializes events through the
// configured ASP.NET Core JsonSerializerOptions).
chain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
chain.Add(AGUIJsonSerializerContext.Default.Options.TypeInfoResolver!);
// Both resolvers must go in front of the reflection-based resolver ASP.NET Core already
// placed at the head of the chain; appending leaves them unreachable for every type that
// reflection can handle, which silently discards the AG-UI wire-format rules.
//
// AGUIJsonUtilities.DefaultTypeInfoResolver is the AG-UI context plus the modifier that omits
// properties with no value. Without it the SSE events go out with explicit nulls for their
// optional fields ("parentRunId": null and similar), which receiving SDKs reject. The AG-UI
// resolver is needed on the net10 TypedResults.ServerSentEvents path, which serializes events
// through the configured ASP.NET Core JsonSerializerOptions.
//
// 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!);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ public void AddAGUIServer_ConfiguresJsonOptions_ResolvesAgentAbstractionsTypes()
options.Invoking(o => o.GetTypeInfo(typeof(ChatMessage))).Should().NotThrow();
}

[Fact]
public void AddAGUIServer_ConfiguresJsonOptions_OmitsOptionalFieldsWithNoValue()
{
JsonSerializerOptions options = BuildConfiguredSerializerOptions();

string json = JsonSerializer.Serialize<BaseEvent>(new RunStartedEvent { ThreadId = "thread", RunId = "run" }, options);

// AG-UI receivers declare the optional event fields as optional, not nullable, so writing them
// as explicit nulls fails validation client-side. The configured options must omit them.
json.Should().NotContain("null");
json.Should().Be("""{"type":"RUN_STARTED","threadId":"thread","runId":"run"}""");
}

private static JsonSerializerOptions BuildConfiguredSerializerOptions()
{
ServiceCollection services = new();
Expand Down
Loading