diff --git a/dotnet/src/Agents/OpenAI/Extensions/OpenAIResponseExtensions.cs b/dotnet/src/Agents/OpenAI/Extensions/OpenAIResponseExtensions.cs
index dc1ec795cae0..d7c27923142d 100644
--- a/dotnet/src/Agents/OpenAI/Extensions/OpenAIResponseExtensions.cs
+++ b/dotnet/src/Agents/OpenAI/Extensions/OpenAIResponseExtensions.cs
@@ -202,7 +202,7 @@ private static ChatMessageContentItemCollection ToChatMessageContentItemCollecti
{
if (part is ReasoningSummaryTextPart text)
{
- collection.Add(new TextContent(text.Text, innerContent: text));
+ collection.Add(new ReasoningContent(text.Text) { InnerContent = text });
}
}
return collection;
diff --git a/dotnet/src/Agents/UnitTests/Extensions/ResponseItemExtensionsTests.cs b/dotnet/src/Agents/UnitTests/Extensions/ResponseItemExtensionsTests.cs
index d547b27be0fa..2860251dedc2 100644
--- a/dotnet/src/Agents/UnitTests/Extensions/ResponseItemExtensionsTests.cs
+++ b/dotnet/src/Agents/UnitTests/Extensions/ResponseItemExtensionsTests.cs
@@ -110,6 +110,9 @@ public void VerifyToChatMessageContentFromReasoning()
// Assert
Assert.NotNull(messageContent);
- Assert.Equal("Foo", messageContent.Content);
+ Assert.Single(messageContent.Items);
+ var reasoningContent = messageContent.Items[0] as ReasoningContent;
+ Assert.NotNull(reasoningContent);
+ Assert.Equal("Foo", reasoningContent.Text);
}
}
diff --git a/dotnet/src/Agents/UnitTests/OpenAI/Extensions/OpenAIResponseExtensionsTests.cs b/dotnet/src/Agents/UnitTests/OpenAI/Extensions/OpenAIResponseExtensionsTests.cs
index 8bd86c950e7b..1687a13d88e9 100644
--- a/dotnet/src/Agents/UnitTests/OpenAI/Extensions/OpenAIResponseExtensionsTests.cs
+++ b/dotnet/src/Agents/UnitTests/OpenAI/Extensions/OpenAIResponseExtensionsTests.cs
@@ -155,6 +155,83 @@ public void VerifyToFunctionCallContent()
Assert.NotNull(content.Arguments);
}
+ ///
+ /// Verify that ReasoningResponseItem with SummaryParts generates ReasoningContent correctly.
+ ///
+ [Fact]
+ public void VerifyToChatMessageContentWithReasoningResponseItem()
+ {
+ // Arrange
+ var reasoningResponseItem = this.CreateReasoningResponseItem("Let me think about this step by step...");
+
+ // Act
+ ChatMessageContent? chatMessageContent = reasoningResponseItem.ToChatMessageContent();
+
+ // Assert
+ Assert.NotNull(chatMessageContent);
+ Assert.Equal(AuthorRole.Assistant, chatMessageContent.Role);
+ Assert.Single(chatMessageContent.Items);
+
+ var reasoningContent = chatMessageContent.Items[0] as ReasoningContent;
+ Assert.NotNull(reasoningContent);
+ Assert.Equal("Let me think about this step by step...", reasoningContent.Text);
+ }
+
+ ///
+ /// Verify that ReasoningResponseItem converts to correct ChatMessageContentItemCollection with ReasoningContent.
+ ///
+ [Fact]
+ public void VerifyToChatMessageContentItemCollectionWithReasoningResponseItem()
+ {
+ // Arrange
+ var reasoningResponseItem = this.CreateReasoningResponseItem("Analyzing the problem...");
+
+ // Act
+ ChatMessageContentItemCollection collection = reasoningResponseItem.ToChatMessageContentItemCollection();
+
+ // Assert
+ Assert.NotNull(collection);
+ Assert.Single(collection);
+ Assert.IsType(collection[0]);
+
+ var reasoningContent = collection[0] as ReasoningContent;
+ Assert.Equal("Analyzing the problem...", reasoningContent?.Text);
+ }
+
+ ///
+ /// Verify that OpenAIResponse with both ReasoningResponseItem and MessageResponseItem generates correct content types.
+ ///
+ [Fact]
+ public void VerifyToChatMessageContentWithMixedResponseItems()
+ {
+ // Arrange
+ var reasoningResponseItem = this.CreateReasoningResponseItem("Thinking about the answer...");
+ var messageResponseItem = ResponseItem.CreateAssistantMessageItem("Here is my response.");
+
+ OpenAIResponse mockResponse = this.CreateMockOpenAIResponse("gpt-4o-mini",
+ [
+ reasoningResponseItem,
+ messageResponseItem
+ ]);
+
+ // Act
+ ChatMessageContent chatMessageContent = mockResponse.ToChatMessageContent();
+
+ // Assert
+ Assert.NotNull(chatMessageContent);
+ Assert.Equal(2, chatMessageContent.Items.Count);
+
+ // First item should be ReasoningContent
+ Assert.IsType(chatMessageContent.Items[0]);
+ var reasoningContent = chatMessageContent.Items[0] as ReasoningContent;
+ Assert.Equal("Thinking about the answer...", reasoningContent?.Text);
+
+ // Second item should be TextContent
+ Assert.IsType(chatMessageContent.Items[1]);
+ var textContent = chatMessageContent.Items[1] as TextContent;
+ Assert.Equal("Here is my response.", textContent?.Text);
+ }
+
#region private
private OpenAIResponse CreateMockOpenAIResponse(string model, IEnumerable outputItems)
{
@@ -250,5 +327,112 @@ private OpenAIResponse CreateMockOpenAIResponse(string id, DateTimeOffset create
throw new InvalidOperationException("Constructor not found.");
}
+ private ReasoningResponseItem CreateReasoningResponseItem(string? reasoningText = null, IReadOnlyList? summaryParts = null)
+ {
+ Type reasoningResponseItemType = typeof(ReasoningResponseItem);
+ Type reasoningSummaryTextPartType = typeof(ReasoningSummaryTextPart);
+
+ // If reasoningText is provided and summaryParts is not, create summaryParts with the text
+ if (reasoningText != null && summaryParts == null)
+ {
+ // Try to find any public static factory method or constructor that can create ReasoningSummaryTextPart
+ var createTextPartMethod = typeof(ReasoningSummaryPart).GetMethod(
+ "CreateTextPart",
+ BindingFlags.Static | BindingFlags.Public,
+ null,
+ [typeof(string)],
+ null);
+
+ if (createTextPartMethod != null)
+ {
+ var textPart = createTextPartMethod.Invoke(null, [reasoningText]) as ReasoningSummaryTextPart;
+ summaryParts = textPart != null ? new List { textPart } : new List();
+ }
+ else
+ {
+ // Try to find constructor - search for all constructors
+ var textPartConstructors = reasoningSummaryTextPartType.GetConstructors(
+ BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
+
+ ConstructorInfo? textPartConstructor = null;
+ foreach (var ctor in textPartConstructors)
+ {
+ var parameters = ctor.GetParameters();
+ if (parameters.Length >= 1 && parameters[0].ParameterType == typeof(string))
+ {
+ textPartConstructor = ctor;
+ break;
+ }
+ }
+
+ if (textPartConstructor != null)
+ {
+ var ctorParams = textPartConstructor.GetParameters();
+ var args = new object?[ctorParams.Length];
+ args[0] = reasoningText;
+ // Fill in any additional parameters with null or default values
+ for (int i = 1; i < ctorParams.Length; i++)
+ {
+ args[i] = null;
+ }
+
+ var textPart = textPartConstructor.Invoke(args) as ReasoningSummaryTextPart;
+ summaryParts = textPart != null ? new List { textPart } : new List();
+ }
+ else
+ {
+ throw new InvalidOperationException("Could not find a way to create ReasoningSummaryTextPart.");
+ }
+ }
+ }
+
+ // Convert null summaryParts to empty list for method calls
+ var partsToPass = summaryParts ?? new List();
+
+ // Try to find a static factory method first
+ var createReasoningItemMethod = typeof(ResponseItem).GetMethod(
+ "CreateReasoningItem",
+ BindingFlags.Static | BindingFlags.Public,
+ null,
+ [typeof(IEnumerable)],
+ null);
+
+ if (createReasoningItemMethod != null)
+ {
+ return (ReasoningResponseItem)createReasoningItemMethod.Invoke(null, [partsToPass])!;
+ }
+
+ // If no factory method, look for constructors
+ var constructors = reasoningResponseItemType.GetConstructors(
+ BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
+
+ foreach (var ctor in constructors)
+ {
+ var parameters = ctor.GetParameters();
+
+ // Look for constructor that takes IReadOnlyList or similar
+ if (parameters.Length >= 1)
+ {
+ var firstParamType = parameters[0].ParameterType;
+ if (firstParamType.IsAssignableFrom(typeof(List)) ||
+ firstParamType.IsAssignableFrom(typeof(IReadOnlyList)) ||
+ firstParamType.IsAssignableFrom(typeof(IEnumerable)))
+ {
+ var args = new object?[parameters.Length];
+ args[0] = partsToPass;
+ // Fill in any additional parameters with null
+ for (int i = 1; i < parameters.Length; i++)
+ {
+ args[i] = null;
+ }
+
+ return (ReasoningResponseItem)ctor.Invoke(args);
+ }
+ }
+ }
+
+ throw new InvalidOperationException("Constructor not found for ReasoningResponseItem.");
+ }
+
#endregion
}