Skip to content

Commit 9ad9b28

Browse files
authored
Fix coalescing of TextReasoningContent with ProtectedData (#6936)
1 parent 46d0b49 commit 9ad9b28

File tree

2 files changed

+60
-1
lines changed

2 files changed

+60
-1
lines changed

src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatResponseExtensions.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,32 @@ internal static void CoalesceTextContent(IList<AIContent> contents)
193193
contents,
194194
mergeSingle: false,
195195
canMerge: static (r1, r2) => string.IsNullOrEmpty(r1.ProtectedData), // we allow merging if the first item has no ProtectedData, even if the second does
196-
static (contents, start, end) => new(MergeText(contents, start, end)) { AdditionalProperties = contents[start].AdditionalProperties?.Clone() });
196+
static (contents, start, end) =>
197+
{
198+
TextReasoningContent content = new(MergeText(contents, start, end))
199+
{
200+
AdditionalProperties = contents[start].AdditionalProperties?.Clone()
201+
};
202+
203+
#if DEBUG
204+
for (int i = start; i < end - 1; i++)
205+
{
206+
Debug.Assert(contents[i] is TextReasoningContent { ProtectedData: null }, "Expected all but the last to have a null ProtectedData");
207+
}
208+
#endif
209+
210+
if (((TextReasoningContent)contents[end - 1]).ProtectedData is { } protectedData)
211+
{
212+
content.ProtectedData = protectedData;
213+
}
214+
215+
return content;
216+
});
197217

198218
static string MergeText(IList<AIContent> contents, int start, int end)
199219
{
220+
Debug.Assert(end - start > 1, "Expected multiple contents to merge");
221+
200222
StringBuilder sb = new();
201223
for (int i = start; i < end; i++)
202224
{

test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/ChatCompletion/ChatResponseUpdateExtensionsTests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,43 @@ public async Task ToChatResponse_CoalescesTextContentAndTextReasoningContentSepa
631631
Assert.Equal("OP", Assert.IsType<TextReasoningContent>(message.Contents[7]).Text);
632632
}
633633

634+
[Theory]
635+
[InlineData(false)]
636+
[InlineData(true)]
637+
public async Task ToChatResponse_CoalescesTextReasoningContentUpToProtectedData(bool useAsync)
638+
{
639+
ChatResponseUpdate[] updates =
640+
{
641+
new() { Contents = [new TextReasoningContent("A") { ProtectedData = "1" }] },
642+
new() { Contents = [new TextReasoningContent("B") { ProtectedData = "2" }] },
643+
new() { Contents = [new TextReasoningContent("C")] },
644+
new() { Contents = [new TextReasoningContent("D")] },
645+
new() { Contents = [new TextReasoningContent("E") { ProtectedData = "3" }] },
646+
new() { Contents = [new TextReasoningContent("F") { ProtectedData = "4" }] },
647+
new() { Contents = [new TextReasoningContent("G")] },
648+
new() { Contents = [new TextReasoningContent("H")] },
649+
};
650+
651+
ChatResponse response = useAsync ? await YieldAsync(updates).ToChatResponseAsync() : updates.ToChatResponse();
652+
ChatMessage message = Assert.Single(response.Messages);
653+
Assert.Equal(5, message.Contents.Count);
654+
655+
Assert.Equal("A", Assert.IsType<TextReasoningContent>(message.Contents[0]).Text);
656+
Assert.Equal("1", ((TextReasoningContent)message.Contents[0]).ProtectedData);
657+
658+
Assert.Equal("B", Assert.IsType<TextReasoningContent>(message.Contents[1]).Text);
659+
Assert.Equal("2", ((TextReasoningContent)message.Contents[1]).ProtectedData);
660+
661+
Assert.Equal("CDE", Assert.IsType<TextReasoningContent>(message.Contents[2]).Text);
662+
Assert.Equal("3", ((TextReasoningContent)message.Contents[2]).ProtectedData);
663+
664+
Assert.Equal("F", Assert.IsType<TextReasoningContent>(message.Contents[3]).Text);
665+
Assert.Equal("4", ((TextReasoningContent)message.Contents[3]).ProtectedData);
666+
667+
Assert.Equal("GH", Assert.IsType<TextReasoningContent>(message.Contents[4]).Text);
668+
Assert.Null(((TextReasoningContent)message.Contents[4]).ProtectedData);
669+
}
670+
634671
[Theory]
635672
[InlineData(false)]
636673
[InlineData(true)]

0 commit comments

Comments
 (0)