.NET: Support reasoning events in AGUI#4953
.NET: Support reasoning events in AGUI#4953jeffinsibycoremont wants to merge 4 commits intomicrosoft:mainfrom
Conversation
232e036 to
8056b71
Compare
@microsoft-github-policy-service agree company="Coremont" |
8056b71 to
8aff11b
Compare
a7c0134 to
01a8291
Compare
|
MEAI uses a As such 01a8291 creates a new GUID for reasoning content. Similarly, despite ReasoningStart and ReasoningEnd events being no-ops, the protocol suggests the mesage ID of these should be unique as well.
Note that the current implementation in this PR means there is no messageId link between a reasoning message and text message as the protocol does not explicitly specify any such relationship e.g. ReasoningMessage has a unique GUID instead of being This image from the docs suggests that RasoningStart/RasoningEnd be traced back to the related ReasoningMessage via the messageId, but Im wary of making such a link as there is nothing explicitly defined and could lead to a breaking change if we were to change this in the future.
Happy to update if I have misunderstood :) |
…they are part of the same logical model response. Create a new GUID for reasoning messages to be consistent with AGUI protocol and establish no link between reasoning and text messages
01a8291 to
47135b2
Compare
…sequent POST, any accumulated role: "reasoning" messages fail deserialization in AGUIMessageJsonConverter because the role wasn't handled - causing the request to fail. This adds AGUIReasoningMessage with Content and EncryptedValue properties, registers it in the JSON converter and serializer context, and converts it to TextReasoningContent (with ProtectedData) in AsChatMessages.
a47f3d9 to
2191383
Compare
…soningContent to AGUIReasoningMessage for c# client


Motivation and Context
Relates to the .NET support for reasoning mentioned in #2619 and parent issue #2558.
Description
7 new events based on agui spec https://docs.ag-ui.com/concepts/reasoning (note 'thinking' is decpriated and the convention now is 'reasoning' https://docs.ag-ui.com/concepts/reasoning#deprecated-events)
Outbound path (AsAGUIEventStreamAsync)
TextReasoningContent from the IChatClient pipeline is now converted into the explicit AG-UI reasoning lifecycle
(REASONING_START → REASONING_MESSAGE_START → REASONING_MESSAGE_CONTENT → REASONING_MESSAGE_END → REASONING_END).
ProtectedData is emitted as REASONING_ENCRYPTED_VALUE inline after the content delta. Content with only ProtectedData (no visible text) is not dropped - the block is opened and the encrypted value emitted without a content delta.
Inbound path (AsChatResponseUpdatesAsync)
A new ReasoningMessageBuilder (following the TextMessageBuilder pattern) handles both the explicit lifecycle form and the chunk shorthand form (REASONING_MESSAGE_CHUNK).
ReasoningStartEvent/ReasoningEndEvent are consumed as no-op bracket markers. The builder enforces the same invariants as TextMessageBuilder - overlapping starts and mismatched ends throw InvalidOperationException.
Request payload path (AsChatMessages)
A new
AGUIReasoningMessagehandlesrole: "reasoning"messages in the frontend's POST payload. Without this, multi-turn conversations with reasoning enabled would fail with 400 Bad Request on the second request, becauseAGUIMessageJsonConverterhad no case for the "reasoning" role discriminator. The message is converted toTextReasoningContentpreserving both visible text and the encrypted thinking token (ProtectedData).AsAGUIEventStreamAsyncTextReasoningContent→REASONING_*eventsAsChatResponseUpdatesAsyncREASONING_*events →TextReasoningContentAsChatMessagesAGUIReasoningMessage→TextReasoningContentAsAGUIMessagesTextReasoningContent→AGUIReasoningMessageContribution Checklist