diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AIContent.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AIContent.cs
index af8b19c8d84..1bfc61e0a38 100644
--- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AIContent.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AIContent.cs
@@ -32,6 +32,8 @@ namespace Microsoft.Extensions.AI;
// [JsonDerivedType(typeof(McpServerToolApprovalResponseContent), typeDiscriminator: "mcpServerToolApprovalResponse")]
// [JsonDerivedType(typeof(CodeInterpreterToolCallContent), typeDiscriminator: "codeInterpreterToolCall")]
// [JsonDerivedType(typeof(CodeInterpreterToolResultContent), typeDiscriminator: "codeInterpreterToolResult")]
+// [JsonDerivedType(typeof(AdditionalDetailsRequestContent), typeDiscriminator: "additionalDetailsRequestContent")]
+// [JsonDerivedType(typeof(AdditionalDetailsResponseContent), typeDiscriminator: "additionalDetailsResponseContent")]
public class AIContent
{
diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AdditionalDetailsRequestContent.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AdditionalDetailsRequestContent.cs
new file mode 100644
index 00000000000..704d4cba0c2
--- /dev/null
+++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AdditionalDetailsRequestContent.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.AI;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Extensions.AI;
+
+///
+/// Represents a request for additional details from the user.
+///
+[Experimental("MEAI001")]
+public sealed class AdditionalDetailsRequestContent : UserInputRequestContent
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The ID that uniquely identifies the additional details request/response pair.
+ /// The additional details request.
+ /// is .
+ /// is empty or composed entirely of whitespace.
+ /// is .
+ public AdditionalDetailsRequestContent(string id, AIContent request)
+ : base(id)
+ {
+ Request = Throw.IfNull(request);
+ }
+
+ ///
+ /// Gets the additional details request.
+ ///
+ public AIContent Request { get; }
+
+ ///
+ /// Creates a to provide the requested additional details.
+ ///
+ /// The containing the requested additional details.
+ /// The representing the response.
+ /// is .
+ public AdditionalDetailsResponseContent CreateResponse(AIContent response) => new(Id, response);
+}
diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AdditionalDetailsResponseContent.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AdditionalDetailsResponseContent.cs
new file mode 100644
index 00000000000..a20980c2aeb
--- /dev/null
+++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AdditionalDetailsResponseContent.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.AI;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Extensions.AI;
+
+///
+/// Represents a response for additional details request from the user.
+///
+[Experimental("MEAI001")]
+public sealed class AdditionalDetailsResponseContent : UserInputResponseContent
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The ID that uniquely identifies the additional details request/response pair.
+ /// The additional details response.
+ /// is .
+ /// is empty or composed entirely of whitespace.
+ /// is .
+ public AdditionalDetailsResponseContent(string id, AIContent response)
+ : base(id)
+ {
+ Response = Throw.IfNull(response);
+ }
+
+ ///
+ /// Gets the additional details response.
+ ///
+ public AIContent Response { get; }
+}
diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UserInputRequestContent.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UserInputRequestContent.cs
index b2a2e0e6e95..267bfe3c4dc 100644
--- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UserInputRequestContent.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UserInputRequestContent.cs
@@ -15,6 +15,7 @@ namespace Microsoft.Extensions.AI;
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")]
[JsonDerivedType(typeof(FunctionApprovalRequestContent), "functionApprovalRequest")]
[JsonDerivedType(typeof(McpServerToolApprovalRequestContent), "mcpServerToolApprovalRequest")]
+[JsonDerivedType(typeof(AdditionalDetailsRequestContent), "additionalDetailsRequestContent")]
public class UserInputRequestContent : AIContent
{
///
diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UserInputResponseContent.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UserInputResponseContent.cs
index 6902f047282..980f84dcb61 100644
--- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UserInputResponseContent.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UserInputResponseContent.cs
@@ -15,6 +15,7 @@ namespace Microsoft.Extensions.AI;
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")]
[JsonDerivedType(typeof(FunctionApprovalResponseContent), "functionApprovalResponse")]
[JsonDerivedType(typeof(McpServerToolApprovalResponseContent), "mcpServerToolApprovalResponse")]
+[JsonDerivedType(typeof(AdditionalDetailsResponseContent), "additionalDetailsResponseContent")]
public class UserInputResponseContent : AIContent
{
///
diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Defaults.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Defaults.cs
index d01294836bc..8b1c1fb1996 100644
--- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Defaults.cs
+++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Utilities/AIJsonUtilities.Defaults.cs
@@ -59,6 +59,8 @@ private static JsonSerializerOptions CreateDefaultOptions()
AddAIContentType(options, typeof(McpServerToolApprovalResponseContent), typeDiscriminatorId: "mcpServerToolApprovalResponse", checkBuiltIn: false);
AddAIContentType(options, typeof(CodeInterpreterToolCallContent), typeDiscriminatorId: "codeInterpreterToolCall", checkBuiltIn: false);
AddAIContentType(options, typeof(CodeInterpreterToolResultContent), typeDiscriminatorId: "codeInterpreterToolResult", checkBuiltIn: false);
+ AddAIContentType(options, typeof(AdditionalDetailsRequestContent), typeDiscriminatorId: "additionalDetailsRequestContent", checkBuiltIn: false);
+ AddAIContentType(options, typeof(AdditionalDetailsResponseContent), typeDiscriminatorId: "additionalDetailsResponseContent", checkBuiltIn: false);
if (JsonSerializer.IsReflectionEnabledByDefault)
{
@@ -133,6 +135,8 @@ private static JsonSerializerOptions CreateDefaultOptions()
[JsonSerializable(typeof(McpServerToolApprovalResponseContent))]
[JsonSerializable(typeof(CodeInterpreterToolCallContent))]
[JsonSerializable(typeof(CodeInterpreterToolResultContent))]
+ [JsonSerializable(typeof(AdditionalDetailsRequestContent))]
+ [JsonSerializable(typeof(AdditionalDetailsResponseContent))]
[JsonSerializable(typeof(ResponseContinuationToken))]
// IEmbeddingGenerator
diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AIContentTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AIContentTests.cs
index e5734ccd7cf..152eea7dcf1 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AIContentTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AIContentTests.cs
@@ -75,7 +75,9 @@ public void Serialization_DerivedTypes_Roundtrips()
new McpServerToolCallContent("call123", "myTool", "myServer"),
new McpServerToolResultContent("call123"),
new McpServerToolApprovalRequestContent("request123", new McpServerToolCallContent("call123", "myTool", "myServer")),
- new McpServerToolApprovalResponseContent("request123", approved: true)
+ new McpServerToolApprovalResponseContent("request123", approved: true),
+ new AdditionalDetailsRequestContent("request123", new TextContent("Please provide the image you mentioned but did not provide.")),
+ new AdditionalDetailsResponseContent("response123", new DataContent(new byte[]{ 1, 2, 3 }, "image/jpeg"))
]);
var serialized = JsonSerializer.Serialize(message, AIJsonUtilities.DefaultOptions);
diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AdditionalDetailsRequestContentTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AdditionalDetailsRequestContentTests.cs
new file mode 100644
index 00000000000..247137dd288
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AdditionalDetailsRequestContentTests.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Text.Json;
+using Xunit;
+
+namespace Microsoft.Extensions.AI.Contents;
+
+public class AdditionalDetailsRequestContentTests
+{
+ [Fact]
+ public void Constructor_InvalidArguments_Throws()
+ {
+ Assert.Throws("id", () => new AdditionalDetailsRequestContent(null!, new TextContent("request")));
+ Assert.Throws("id", () => new AdditionalDetailsRequestContent("", new TextContent("request")));
+ Assert.Throws("id", () => new AdditionalDetailsRequestContent("\r\t\n ", new TextContent("request")));
+
+ Assert.Throws("request", () => new AdditionalDetailsRequestContent("id", null!));
+ }
+
+ [Fact]
+ public void Constructor_Roundtrips()
+ {
+ string id = "abc";
+ TextContent request = new("What is your name?");
+ AdditionalDetailsRequestContent content = new(id, request);
+
+ Assert.Same(id, content.Id);
+ Assert.Same(request, content.Request);
+ }
+
+ [Fact]
+ public void CreateResponse_ReturnsExpectedResponse()
+ {
+ string id = "req-1";
+ string request = "What is your name?";
+ TextContent response = new TextContent("My name is John");
+
+ AdditionalDetailsRequestContent content = new(id, new TextContent(request));
+
+ var textResponse = content.CreateResponse(response);
+
+ Assert.NotNull(textResponse);
+ Assert.Same(id, textResponse.Id);
+ Assert.Same(response, textResponse.Response);
+ Assert.Throws("response", () => content.CreateResponse(null!));
+ }
+
+ [Fact]
+ public void Serialization_Roundtrips()
+ {
+ var content = new AdditionalDetailsRequestContent("request123", new TextContent("What is your name?"));
+
+ var json = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
+ var deserializedContent = JsonSerializer.Deserialize(json, AIJsonUtilities.DefaultOptions);
+
+ Assert.NotNull(deserializedContent);
+ Assert.Equal(content.Id, deserializedContent.Id);
+
+ TextContent originalRequest = Assert.IsType(content.Request);
+ TextContent deserializedRequest = Assert.IsType(deserializedContent.Request);
+
+ Assert.Equal(originalRequest.Text, deserializedRequest.Text);
+ }
+}
diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AdditionalDetailsResponseContentTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AdditionalDetailsResponseContentTests.cs
new file mode 100644
index 00000000000..34f847a3b51
--- /dev/null
+++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/AdditionalDetailsResponseContentTests.cs
@@ -0,0 +1,49 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Text.Json;
+using Xunit;
+
+namespace Microsoft.Extensions.AI.Contents;
+
+public class AdditionalDetailsResponseContentTests
+{
+ [Fact]
+ public void Constructor_InvalidArguments_Throws()
+ {
+ Assert.Throws("id", () => new AdditionalDetailsResponseContent(null!, new TextContent("response")));
+ Assert.Throws("id", () => new AdditionalDetailsResponseContent("", new TextContent("response")));
+ Assert.Throws("id", () => new AdditionalDetailsResponseContent("\r\t\n ", new TextContent("response")));
+
+ Assert.Throws("response", () => new AdditionalDetailsResponseContent("id", null!));
+ }
+
+ [Fact]
+ public void Constructor_Roundtrips()
+ {
+ string id = "test-id";
+ TextContent response = new("test-response");
+ AdditionalDetailsResponseContent content = new(id, response);
+
+ Assert.Same(id, content.Id);
+ Assert.Same(response, content.Response);
+ }
+
+ [Fact]
+ public void Serialization_Roundtrips()
+ {
+ var content = new AdditionalDetailsResponseContent("response123", new TextContent("This is my answer"));
+
+ var json = JsonSerializer.Serialize(content, AIJsonUtilities.DefaultOptions);
+ var deserializedContent = JsonSerializer.Deserialize(json, AIJsonUtilities.DefaultOptions);
+
+ Assert.NotNull(deserializedContent);
+ Assert.Equal(content.Id, deserializedContent.Id);
+
+ TextContent originalResponse = Assert.IsType(content.Response);
+ TextContent deserializedResponse = Assert.IsType(deserializedContent.Response);
+
+ Assert.Equal(originalResponse.Text, deserializedResponse.Text);
+ }
+}
diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/UserInputRequestContentTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/UserInputRequestContentTests.cs
index fc4dac9cabb..8d0b5e62a1b 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/UserInputRequestContentTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/UserInputRequestContentTests.cs
@@ -43,6 +43,7 @@ public void Serialization_DerivedTypes_Roundtrips()
[
new FunctionApprovalRequestContent("request123", new FunctionCallContent("call123", "functionName", new Dictionary { { "param1", 123 } })),
new McpServerToolApprovalRequestContent("request123", new McpServerToolCallContent("call123", "myTool", "myServer")),
+ new AdditionalDetailsRequestContent("request123", new TextContent("I need more details. Where would you like to fly from and to?")),
];
var serializedContents = JsonSerializer.Serialize(contents, TestJsonSerializerContext.Default.UserInputRequestContentArray);
diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/UserInputResponseContentTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/UserInputResponseContentTests.cs
index 2442e57272d..3916546e437 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/UserInputResponseContentTests.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/UserInputResponseContentTests.cs
@@ -41,6 +41,7 @@ public void Serialization_DerivedTypes_Roundtrips()
[
new FunctionApprovalResponseContent("request123", true, new FunctionCallContent("call123", "functionName")),
new McpServerToolApprovalResponseContent("request123", true),
+ new AdditionalDetailsResponseContent("response123", new TextContent("I would like to fly from New York to San Francisco."))
];
var serializedContents = JsonSerializer.Serialize(contents, TestJsonSerializerContext.Default.UserInputResponseContentArray);