From ceaf9f09d83010d3c573f118ea10238f2614bd28 Mon Sep 17 00:00:00 2001 From: Naveen Chatlapalli Date: Sat, 3 Oct 2026 00:09:27 -0500 Subject: [PATCH] fix: register AG-UI interrupt content in ASP.NET JSON options --- .../ConfigureAGUIJsonOptions.cs | 4 ++ .../ConfigureAGUIJsonOptionsTests.cs | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/ConfigureAGUIJsonOptions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/ConfigureAGUIJsonOptions.cs index 78eccec92fa..a96cc0e8289 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/ConfigureAGUIJsonOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/ConfigureAGUIJsonOptions.cs @@ -29,5 +29,9 @@ public void Configure(JsonOptions options) // 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!); + + // AG-UI interrupt request/response content is polymorphic AIContent that M.E.AI does not know + // about, so it must be registered explicitly or ChatMessage round-trips drop it. + AGUIJsonUtilities.RegisterInterruptContentTypes(options.SerializerOptions); } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/ConfigureAGUIJsonOptionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/ConfigureAGUIJsonOptionsTests.cs index 8a6e353f0ba..8ccdb48d095 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/ConfigureAGUIJsonOptionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/ConfigureAGUIJsonOptionsTests.cs @@ -45,6 +45,48 @@ public void AddAGUIServer_ConfiguresJsonOptions_OmitsOptionalFieldsWithNoValue() Assert.Equal("""{"type":"RUN_STARTED","threadId":"thread","runId":"run"}""", json); } + [Fact] + public void AddAGUIServer_ConfiguresJsonOptions_RoundTripsInterruptRequest() + { + // Arrange + JsonSerializerOptions options = BuildConfiguredSerializerOptions(); + ChatMessage message = new(ChatRole.Assistant, [new InterruptRequestContent("request-1") + { + Reason = "confirmation", + Message = "Approve this action?", + }]); + + // Act + string json = JsonSerializer.Serialize(message, options); + ChatMessage restored = JsonSerializer.Deserialize(json, options)!; + + // Assert + InterruptRequestContent content = Assert.IsType(Assert.Single(restored.Contents)); + Assert.Equal("request-1", content.RequestId); + Assert.Equal("confirmation", content.Reason); + Assert.Equal("Approve this action?", content.Message); + } + + [Fact] + public void AddAGUIServer_ConfiguresJsonOptions_RoundTripsInterruptResponse() + { + // Arrange + JsonSerializerOptions options = BuildConfiguredSerializerOptions(); + ChatMessage message = new(ChatRole.User, [new InterruptResponseContent("request-1") + { + Payload = JsonSerializer.SerializeToElement(new { approved = true }), + }]); + + // Act + string json = JsonSerializer.Serialize(message, options); + ChatMessage restored = JsonSerializer.Deserialize(json, options)!; + + // Assert + InterruptResponseContent content = Assert.IsType(Assert.Single(restored.Contents)); + Assert.Equal("request-1", content.RequestId); + Assert.True(Assert.IsType(content.Payload).GetProperty("approved").GetBoolean()); + } + private static JsonSerializerOptions BuildConfiguredSerializerOptions() { ServiceCollection services = new();