From d53147aea8b9d67f8f997ad60febe261e0056ada Mon Sep 17 00:00:00 2001 From: Adam Holm Date: Fri, 19 Sep 2025 20:15:58 -0500 Subject: [PATCH 1/4] Add optional role parameter to 'ResponseContentPart' conversion methods Introduce an optional `AuthorRole? role` parameter to the `ToResponseContentPart` methods in `KernelContentExtensions` . Update the `ToResponseItem` method in `ChatContentMessageExtensions` to utilize this role for determining the correct content part type (Input or Output). Modify tests in `ChatContentMessageExtensionsTests` to ensure accurate validation of content part types based on the author role. --- .../OpenAI/Extensions/ChatContentMessageExtensions.cs | 3 ++- .../OpenAI/Extensions/KernelContentExtensions.cs | 11 ++++++++--- .../Extensions/ChatContentMessageExtensionsTests.cs | 10 +++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/dotnet/src/Agents/OpenAI/Extensions/ChatContentMessageExtensions.cs b/dotnet/src/Agents/OpenAI/Extensions/ChatContentMessageExtensions.cs index c627d9517c81..882be1d794ff 100644 --- a/dotnet/src/Agents/OpenAI/Extensions/ChatContentMessageExtensions.cs +++ b/dotnet/src/Agents/OpenAI/Extensions/ChatContentMessageExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.SemanticKernel.Agents.OpenAI.Internal; +using Microsoft.SemanticKernel.ChatCompletion; using OpenAI.Assistants; using OpenAI.Responses; @@ -44,7 +45,7 @@ public static IEnumerable ToThreadInitializationMes public static ResponseItem ToResponseItem(this ChatMessageContent message) { var items = message.Items; - IEnumerable contentParts = items.Select(item => item.ToResponseContentPart()); + IEnumerable contentParts = items.Select(item => item.ToResponseContentPart(message.Role == AuthorRole.Assistant ? message.Role : null)); return message.Role.Label.ToUpperInvariant() switch { "SYSTEM" => ResponseItem.CreateSystemMessageItem(contentParts), diff --git a/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs b/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs index 59f11f0f68cd..590d21a87523 100644 --- a/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs +++ b/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using Microsoft.SemanticKernel.ChatCompletion; using OpenAI.Responses; namespace Microsoft.SemanticKernel.Agents.OpenAI; @@ -10,11 +11,11 @@ namespace Microsoft.SemanticKernel.Agents.OpenAI; /// internal static class KernelContentExtensions { - internal static ResponseContentPart ToResponseContentPart(this KernelContent content) + internal static ResponseContentPart ToResponseContentPart(this KernelContent content, AuthorRole? role = null) { return content switch { - TextContent textContent => textContent.ToResponseContentPart(), + TextContent textContent => textContent.ToResponseContentPart(role), ImageContent imageContent => imageContent.ToResponseContentPart(), BinaryContent binaryContent => binaryContent.ToResponseContentPart(), FileReferenceContent fileReferenceContent => fileReferenceContent.ToResponseContentPart(), @@ -22,8 +23,12 @@ internal static ResponseContentPart ToResponseContentPart(this KernelContent con }; } - internal static ResponseContentPart ToResponseContentPart(this TextContent content) + internal static ResponseContentPart ToResponseContentPart(this TextContent content, AuthorRole? role = null) { + if (role is not null && role == AuthorRole.Assistant) + { + return ResponseContentPart.CreateOutputTextPart(content.Text, []); + } return ResponseContentPart.CreateInputTextPart(content.Text); } diff --git a/dotnet/src/Agents/UnitTests/OpenAI/Extensions/ChatContentMessageExtensionsTests.cs b/dotnet/src/Agents/UnitTests/OpenAI/Extensions/ChatContentMessageExtensionsTests.cs index 62c87daad7ed..e9d26a2daa02 100644 --- a/dotnet/src/Agents/UnitTests/OpenAI/Extensions/ChatContentMessageExtensionsTests.cs +++ b/dotnet/src/Agents/UnitTests/OpenAI/Extensions/ChatContentMessageExtensionsTests.cs @@ -44,10 +44,14 @@ public void VerifyToResponseItemWithUserChatMessageContent(string roleLabel) Assert.Equal(role.Label.ToUpperInvariant(), messageResponseItem.Role.ToString().ToUpperInvariant()); Assert.Equal(4, messageResponseItem.Content.Count); - // Validate TextContent conversion - should create InputText part - var textContent = messageResponseItem.Content.FirstOrDefault(p => p.Kind == ResponseContentPartKind.InputText); + // Determine expected kind for text content based on role + var expectedTextKind = roleLabel.Equals("assistant", StringComparison.OrdinalIgnoreCase) + ? ResponseContentPartKind.OutputText + : ResponseContentPartKind.InputText; + + // Validate TextContent conversion - should create appropriate Input/OutputText part + var textContent = messageResponseItem.Content.FirstOrDefault(p => p.Kind == expectedTextKind); Assert.NotNull(textContent); - //Assert.IsType<>(textContent); Assert.Equal("What is in this image?", textContent.Text); // Validate ImageContent conversion - should create InputImage part From 30c1aaa1c1ce918c6f51c378ee8b7b6844c6f8a6 Mon Sep 17 00:00:00 2001 From: HillPhelmuth Date: Mon, 13 Oct 2025 19:24:43 -0500 Subject: [PATCH 2/4] Update dotnet/src/Agents/OpenAI/Extensions/ChatContentMessageExtensions.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Agents/OpenAI/Extensions/ChatContentMessageExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/Agents/OpenAI/Extensions/ChatContentMessageExtensions.cs b/dotnet/src/Agents/OpenAI/Extensions/ChatContentMessageExtensions.cs index 882be1d794ff..e1b61e64b7f9 100644 --- a/dotnet/src/Agents/OpenAI/Extensions/ChatContentMessageExtensions.cs +++ b/dotnet/src/Agents/OpenAI/Extensions/ChatContentMessageExtensions.cs @@ -45,7 +45,7 @@ public static IEnumerable ToThreadInitializationMes public static ResponseItem ToResponseItem(this ChatMessageContent message) { var items = message.Items; - IEnumerable contentParts = items.Select(item => item.ToResponseContentPart(message.Role == AuthorRole.Assistant ? message.Role : null)); + IEnumerable contentParts = items.Select(item => item.ToResponseContentPart(message.Role)); return message.Role.Label.ToUpperInvariant() switch { "SYSTEM" => ResponseItem.CreateSystemMessageItem(contentParts), From 047795140789083e411e91540f6aa3c95b5e6e4c Mon Sep 17 00:00:00 2001 From: HillPhelmuth Date: Mon, 20 Oct 2025 14:33:46 -0500 Subject: [PATCH 3/4] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs b/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs index 590d21a87523..bc7c3cba6fa7 100644 --- a/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs +++ b/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs @@ -25,7 +25,7 @@ internal static ResponseContentPart ToResponseContentPart(this KernelContent con internal static ResponseContentPart ToResponseContentPart(this TextContent content, AuthorRole? role = null) { - if (role is not null && role == AuthorRole.Assistant) + if (role == AuthorRole.Assistant) { return ResponseContentPart.CreateOutputTextPart(content.Text, []); } From e09fac095c2919a0d63154f228422b91c52083f0 Mon Sep 17 00:00:00 2001 From: HillPhelmuth Date: Mon, 20 Oct 2025 14:51:16 -0500 Subject: [PATCH 4/4] Refactor ToResponseContentPart method for clarity --- .../Agents/OpenAI/Extensions/KernelContentExtensions.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs b/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs index bc7c3cba6fa7..b99f8bb1aeb9 100644 --- a/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs +++ b/dotnet/src/Agents/OpenAI/Extensions/KernelContentExtensions.cs @@ -25,11 +25,9 @@ internal static ResponseContentPart ToResponseContentPart(this KernelContent con internal static ResponseContentPart ToResponseContentPart(this TextContent content, AuthorRole? role = null) { - if (role == AuthorRole.Assistant) - { - return ResponseContentPart.CreateOutputTextPart(content.Text, []); - } - return ResponseContentPart.CreateInputTextPart(content.Text); + return role == AuthorRole.Assistant + ? ResponseContentPart.CreateOutputTextPart(content.Text, annotations: []) + : ResponseContentPart.CreateInputTextPart(content.Text); } internal static ResponseContentPart ToResponseContentPart(this ImageContent content)