.NET: fix: preserve AG-UI event metadata#5816
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the .NET AG-UI adapter layer to preserve AIContent.AdditionalProperties as AG-UI “passthrough” JSON fields when converting between ChatResponseUpdate streams and AG-UI event streams, while filtering reserved protocol-owned field names so they can’t be shadowed by metadata.
Changes:
- Add
JsonExtensionDatasupport to AG-UI events (BaseEvent) to carry arbitrary passthrough fields. - Preserve passthrough metadata across conversions (
TextContent↔TEXT_MESSAGE_*events,DataContent↔STATE_*events) with reserved-field filtering. - Add unit tests validating passthrough emission, filtering of reserved fields, and round-tripping back into
AIContent.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| dotnet/src/Microsoft.Agents.AI.AGUI/Shared/BaseEvent.cs | Adds JsonExtensionData-backed AdditionalProperties for AG-UI passthrough fields on all events. |
| dotnet/src/Microsoft.Agents.AI.AGUI/Shared/ChatResponseUpdateAGUIExtensions.cs | Copies AdditionalProperties between AIContent and events and filters reserved protocol fields during event emission. |
| dotnet/tests/Microsoft.Agents.AI.AGUI.UnitTests/ChatResponseUpdateAGUIExtensionsTests.cs | Adds coverage for preserving passthrough metadata and ensuring reserved fields aren’t shadowed in serialized output. |
| public void AddTextStart(TextMessageStartEvent textStart) | ||
| { | ||
| if (this._currentRole != default || this._currentMessageId != null) | ||
| { | ||
| throw new InvalidOperationException("Received TextMessageStartEvent while another message is being processed."); | ||
| } | ||
|
|
||
| this._currentRole = AGUIChatMessageExtensions.MapChatRole(textStart.Role); | ||
| this._currentMessageId = textStart.MessageId; | ||
| } | ||
|
|
||
| internal ChatResponseUpdate EmitTextUpdate(TextMessageContentEvent textContent) | ||
| { | ||
| TextContent content = new(textContent.Delta) | ||
| { | ||
| AdditionalProperties = CopyAdditionalProperties(textContent) | ||
| }; |
| @@ -660,12 +718,13 @@ | |||
| #if !NET | |||
| Delta = (JsonElement?)JsonSerializer.Deserialize( | |||
| dataContent.Data.ToArray(), | |||
| jsonSerializerOptions.GetTypeInfo(typeof(JsonElement))) | |||
| jsonSerializerOptions.GetTypeInfo(typeof(JsonElement))), | |||
| #else | |||
| Delta = (JsonElement?)JsonSerializer.Deserialize( | |||
| dataContent.Data.Span, | |||
| jsonSerializerOptions.GetTypeInfo(typeof(JsonElement))) | |||
| jsonSerializerOptions.GetTypeInfo(typeof(JsonElement))), | |||
| #endif | |||
| AdditionalProperties = CopyAdditionalProperties(dataContent, "type", "delta") | |||
| }; | |||
|
Addressed both review comments in a follow-up commit. Changes:
Local validation: dotnet build dotnet\tests\Microsoft.Agents.AI.AGUI.UnitTests\Microsoft.Agents.AI.AGUI.UnitTests.csproj --no-restore
dotnet dotnet\tests\Microsoft.Agents.AI.AGUI.UnitTests\bin\Debug\net10.0\Microsoft.Agents.AI.AGUI.UnitTests.dll --filter-method '*AsChatResponseUpdatesAsync_WithTextStartAdditionalProperties_PreservesAndMergesMetadataAsync' --filter-method '*BaseEventJsonConverter_WithStateDeltaEvent_DeserializesStateDeltaEvent' --filter-method '*AsAGUIEventStreamAsync_ConvertsDataContentWithJsonPatch_ToStateDeltaEventAsync' --filter-method '*AsChatResponseUpdatesAsync_WithTextEventAdditionalProperties_PreservesTextContentMetadataAsync' --no-progress
dotnet format dotnet\agent-framework-dotnet.slnx --verify-no-changes --no-restore --include dotnet\src\Microsoft.Agents.AI.AGUI\Shared\ChatResponseUpdateAGUIExtensions.cs dotnet\src\Microsoft.Agents.AI.AGUI\Shared\BaseEventJsonConverter.cs dotnet\tests\Microsoft.Agents.AI.AGUI.UnitTests\ChatResponseUpdateAGUIExtensionsTests.cs
git diff --checkResult: build succeeded with 0 warnings/errors; 4 targeted tests passed; format and diff checks passed. Note: |
3c5586b to
b04daf6
Compare
|
Rebased this branch onto current main and force-pushed with lease. Re-ran the focused AG-UI validation after the rebase: AGUI unit-test project build passed with 0 warnings/errors; the text START metadata test, StateDelta BaseEvent converter test, JSON Patch StateDelta emission test, and existing text-event metadata test all passed; dotnet format --verify-no-changes passed for the touched files; git diff --check passed. |
Summary
TextContent.AdditionalPropertiesas AG-UI passthrough fields on text start/content eventsTextContentDataContent.AdditionalPropertiesthrough state snapshot/delta events as the same passthrough metadata pathtype,messageId,delta, andsnapshotso metadata cannot shadow AG-UI protocol fieldsFixes #4923
Validation
dotnet run --project dotnet\tests\Microsoft.Agents.AI.AGUI.UnitTests\Microsoft.Agents.AI.AGUI.UnitTests.csproj --framework net10.0 -- --filter-class Microsoft.Agents.AI.AGUI.UnitTests.ChatResponseUpdateAGUIExtensionsTestsdotnet run --project dotnet\tests\Microsoft.Agents.AI.AGUI.UnitTests\Microsoft.Agents.AI.AGUI.UnitTests.csproj --framework net10.0dotnet format dotnet\agent-framework-dotnet.slnx --verify-no-changes --include dotnet\src\Microsoft.Agents.AI.AGUI\Shared\BaseEvent.cs dotnet\src\Microsoft.Agents.AI.AGUI\Shared\ChatResponseUpdateAGUIExtensions.cs dotnet\tests\Microsoft.Agents.AI.AGUI.UnitTests\ChatResponseUpdateAGUIExtensionsTests.csgit diff --check