Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ public sealed class FunctionApprovalRequestContent : UserInputRequestContent
/// <summary>
/// Initializes a new instance of the <see cref="FunctionApprovalRequestContent"/> class.
/// </summary>
/// <param name="id">The ID that uniquely identifies the function approval request/response pair.</param>
/// <param name="requestId">The identifier of this request.</param>
/// <param name="functionCall">The function call that requires user approval.</param>
/// <exception cref="ArgumentNullException"><paramref name="id"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="id"/> is empty or composed entirely of whitespace.</exception>
/// <exception cref="ArgumentNullException"><paramref name="requestId"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="requestId"/> is empty or composed entirely of whitespace.</exception>
/// <exception cref="ArgumentNullException"><paramref name="functionCall"/> is <see langword="null"/>.</exception>
public FunctionApprovalRequestContent(string id, FunctionCallContent functionCall)
: base(id)
public FunctionApprovalRequestContent(string requestId, FunctionCallContent functionCall)
: base(requestId)
{
FunctionCall = Throw.IfNull(functionCall);
}
Expand All @@ -37,5 +37,5 @@ public FunctionApprovalRequestContent(string id, FunctionCallContent functionCal
/// </summary>
/// <param name="approved"><see langword="true"/> if the function call is approved; otherwise, <see langword="false"/>.</param>
/// <returns>The <see cref="FunctionApprovalResponseContent"/> representing the approval response.</returns>
public FunctionApprovalResponseContent CreateResponse(bool approved) => new(Id, approved, FunctionCall);
public FunctionApprovalResponseContent CreateResponse(bool approved) => new(RequestId, approved, FunctionCall);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ public sealed class FunctionApprovalResponseContent : UserInputResponseContent
/// <summary>
/// Initializes a new instance of the <see cref="FunctionApprovalResponseContent"/> class.
/// </summary>
/// <param name="id">The ID that uniquely identifies the function approval request/response pair.</param>
/// <param name="requestId">The identifier of the <see cref="FunctionApprovalRequestContent"/> associated with this response.</param>
/// <param name="approved"><see langword="true"/> if the function call is approved; otherwise, <see langword="false"/>.</param>
/// <param name="functionCall">The function call that requires user approval.</param>
/// <exception cref="ArgumentNullException"><paramref name="id"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="id"/> is empty or composed entirely of whitespace.</exception>
/// <exception cref="ArgumentNullException"><paramref name="requestId"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="requestId"/> is empty or composed entirely of whitespace.</exception>
/// <exception cref="ArgumentNullException"><paramref name="functionCall"/> is <see langword="null"/>.</exception>
public FunctionApprovalResponseContent(string id, bool approved, FunctionCallContent functionCall)
: base(id)
public FunctionApprovalResponseContent(string requestId, bool approved, FunctionCallContent functionCall)
: base(requestId)
{
Approved = approved;
FunctionCall = Throw.IfNull(functionCall);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ public sealed class McpServerToolApprovalRequestContent : UserInputRequestConten
/// <summary>
/// Initializes a new instance of the <see cref="McpServerToolApprovalRequestContent"/> class.
/// </summary>
/// <param name="id">The ID that uniquely identifies the MCP server tool approval request/response pair.</param>
/// <param name="requestId">The identifier of this request.</param>
/// <param name="toolCall">The tool call that requires user approval.</param>
/// <exception cref="ArgumentNullException"><paramref name="id"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="id"/> is empty or composed entirely of whitespace.</exception>
/// <exception cref="ArgumentNullException"><paramref name="requestId"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="requestId"/> is empty or composed entirely of whitespace.</exception>
/// <exception cref="ArgumentNullException"><paramref name="toolCall"/> is <see langword="null"/>.</exception>
public McpServerToolApprovalRequestContent(string id, McpServerToolCallContent toolCall)
: base(id)
public McpServerToolApprovalRequestContent(string requestId, McpServerToolCallContent toolCall)
: base(requestId)
{
ToolCall = Throw.IfNull(toolCall);
}
Expand All @@ -37,5 +37,5 @@ public McpServerToolApprovalRequestContent(string id, McpServerToolCallContent t
/// </summary>
/// <param name="approved"><see langword="true"/> if the function call is approved; otherwise, <see langword="false"/>.</param>
/// <returns>The <see cref="FunctionApprovalResponseContent"/> representing the approval response.</returns>
public McpServerToolApprovalResponseContent CreateResponse(bool approved) => new(Id, approved);
public McpServerToolApprovalResponseContent CreateResponse(bool approved) => new(RequestId, approved);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ public sealed class McpServerToolApprovalResponseContent : UserInputResponseCont
/// <summary>
/// Initializes a new instance of the <see cref="McpServerToolApprovalResponseContent"/> class.
/// </summary>
/// <param name="id">The ID that uniquely identifies the MCP server tool approval request/response pair.</param>
/// <param name="requestId">The identifier of the <see cref="McpServerToolApprovalRequestContent"/> associated with this response.</param>
/// <param name="approved"><see langword="true"/> if the MCP server tool call is approved; otherwise, <see langword="false"/>.</param>
/// <exception cref="ArgumentNullException"><paramref name="id"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="id"/> is empty or composed entirely of whitespace.</exception>
public McpServerToolApprovalResponseContent(string id, bool approved)
: base(id)
/// <exception cref="ArgumentNullException"><paramref name="requestId"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="requestId"/> is empty or composed entirely of whitespace.</exception>
public McpServerToolApprovalResponseContent(string requestId, bool approved)
: base(requestId)
{
Approved = approved;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ public class UserInputRequestContent : AIContent
/// <summary>
/// Initializes a new instance of the <see cref="UserInputRequestContent"/> class.
/// </summary>
/// <param name="id">The ID that uniquely identifies the user input request/response pair.</param>
/// <exception cref="ArgumentNullException"><paramref name="id"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="id"/> is empty or composed entirely of whitespace.</exception>
protected UserInputRequestContent(string id)
/// <param name="requestId">The identifier of this request.</param>
/// <exception cref="ArgumentNullException"><paramref name="requestId"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="requestId"/> is empty or composed entirely of whitespace.</exception>
protected UserInputRequestContent(string requestId)
{
Id = Throw.IfNullOrWhitespace(id);
RequestId = Throw.IfNullOrWhitespace(requestId);
}

/// <summary>
/// Gets the ID that uniquely identifies the user input request/response pair.
/// Gets the identifier of this request.
/// </summary>
public string Id { get; }
public string RequestId { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ public class UserInputResponseContent : AIContent
/// <summary>
/// Initializes a new instance of the <see cref="UserInputResponseContent"/> class.
/// </summary>
/// <param name="id">The ID that uniquely identifies the user input request/response pair.</param>
/// <exception cref="ArgumentNullException"><paramref name="id"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="id"/> is empty or composed entirely of whitespace.</exception>
protected UserInputResponseContent(string id)
/// <param name="requestId">The identifier of the <see cref="UserInputRequestContent"/> associated with this response.</param>
/// <exception cref="ArgumentNullException"><paramref name="requestId"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="requestId"/> is empty or composed entirely of whitespace.</exception>
protected UserInputResponseContent(string requestId)
{
Id = Throw.IfNullOrWhitespace(id);
RequestId = Throw.IfNullOrWhitespace(requestId);
}

/// <summary>
/// Gets the ID that uniquely identifies the user input request/response pair.
/// Gets the identifier of the <see cref="UserInputRequestContent"/> associated with this response.
/// </summary>
public string Id { get; }
public string RequestId { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,7 @@ internal static IEnumerable<ResponseItem> ToOpenAIResponseItems(IEnumerable<Chat
ResponseItem? directItem = item switch
{
{ RawRepresentation: ResponseItem rawRep } => rawRep,
McpServerToolApprovalResponseContent mcpResp => ResponseItem.CreateMcpApprovalResponseItem(mcpResp.Id, mcpResp.Approved),
McpServerToolApprovalResponseContent mcpResp => ResponseItem.CreateMcpApprovalResponseItem(mcpResp.RequestId, mcpResp.Approved),
_ => null
};

Expand Down Expand Up @@ -1053,7 +1053,7 @@ static FunctionCallOutputResponseItem SerializeAIContent(string callId, IEnumera
break;

case McpServerToolApprovalResponseContent mcpApprovalResponseContent:
yield return ResponseItem.CreateMcpApprovalResponseItem(mcpApprovalResponseContent.Id, mcpApprovalResponseContent.Approved);
yield return ResponseItem.CreateMcpApprovalResponseItem(mcpApprovalResponseContent.RequestId, mcpApprovalResponseContent.Approved);
break;
}
}
Expand Down Expand Up @@ -1092,7 +1092,7 @@ static FunctionCallOutputResponseItem SerializeAIContent(string callId, IEnumera

case McpServerToolApprovalRequestContent mcpApprovalRequestContent:
yield return ResponseItem.CreateMcpApprovalRequestItem(
mcpApprovalRequestContent.Id,
mcpApprovalRequestContent.RequestId,
mcpApprovalRequestContent.ToolCall.ServerName,
mcpApprovalRequestContent.ToolCall.ToolName,
BinaryData.FromBytes(JsonSerializer.SerializeToUtf8Bytes(mcpApprovalRequestContent.ToolCall.Arguments!, OpenAIJsonContext.Default.IReadOnlyDictionaryStringObject)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1329,7 +1329,7 @@ private static (List<ApprovalResultWithRequestMessage>? approvals, List<Approval
case FunctionApprovalRequestContent farc:
// Validation: Capture each call id for each approval request to ensure later we have a matching response.
_ = (approvalRequestCallIds ??= []).Add(farc.FunctionCall.CallId);
(allApprovalRequestsMessages ??= []).Add(farc.Id, message);
(allApprovalRequestsMessages ??= []).Add(farc.RequestId, message);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's be thoughtful of when we merge this change with regards to next week's release and any other changes to this same area. It'd be good for all of the breaking changes we plan to make to all of this [Experimental] surface area to land in the same release.

Copy link
Member Author

@jozkee jozkee Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably won't make it for next week, unless we land these other proposed changes:

break;

case FunctionApprovalResponseContent farc:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ public void Constructor_InvalidArguments_Throws()
{
FunctionCallContent functionCall = new("FCC1", "TestFunction");

Assert.Throws<ArgumentNullException>("id", () => new FunctionApprovalRequestContent(null!, functionCall));
Assert.Throws<ArgumentException>("id", () => new FunctionApprovalRequestContent("", functionCall));
Assert.Throws<ArgumentException>("id", () => new FunctionApprovalRequestContent("\r\t\n ", functionCall));
Assert.Throws<ArgumentNullException>("requestId", () => new FunctionApprovalRequestContent(null!, functionCall));
Assert.Throws<ArgumentException>("requestId", () => new FunctionApprovalRequestContent("", functionCall));
Assert.Throws<ArgumentException>("requestId", () => new FunctionApprovalRequestContent("\r\t\n ", functionCall));

Assert.Throws<ArgumentNullException>("functionCall", () => new FunctionApprovalRequestContent("id", null!));
Assert.Throws<ArgumentNullException>("functionCall", () => new FunctionApprovalRequestContent("requestId", null!));
}

[Theory]
[InlineData("abc")]
[InlineData("123")]
[InlineData("!@#")]
public void Constructor_Roundtrips(string id)
public void Constructor_Roundtrips(string requestId)
{
FunctionCallContent functionCall = new("FCC1", "TestFunction");

FunctionApprovalRequestContent content = new(id, functionCall);
FunctionApprovalRequestContent content = new(requestId, functionCall);

Assert.Same(id, content.Id);
Assert.Same(requestId, content.RequestId);
Assert.Same(functionCall, content.FunctionCall);
}

Expand All @@ -41,15 +41,15 @@ public void Constructor_Roundtrips(string id)
[InlineData(false)]
public void CreateResponse_ReturnsExpectedResponse(bool approved)
{
string id = "req-1";
string requestId = "req-1";
FunctionCallContent functionCall = new("FCC1", "TestFunction");

FunctionApprovalRequestContent content = new(id, functionCall);
FunctionApprovalRequestContent content = new(requestId, functionCall);

var response = content.CreateResponse(approved);

Assert.NotNull(response);
Assert.Same(id, response.Id);
Assert.Same(requestId, response.RequestId);
Assert.Equal(approved, response.Approved);
Assert.Same(functionCall, response.FunctionCall);
}
Expand All @@ -63,7 +63,7 @@ public void Serialization_Roundtrips()
var deserializedContent = JsonSerializer.Deserialize<FunctionApprovalRequestContent>(json, AIJsonUtilities.DefaultOptions);

Assert.NotNull(deserializedContent);
Assert.Equal(content.Id, deserializedContent.Id);
Assert.Equal(content.RequestId, deserializedContent.RequestId);
Assert.NotNull(deserializedContent.FunctionCall);
Assert.Equal(content.FunctionCall.CallId, deserializedContent.FunctionCall.CallId);
Assert.Equal(content.FunctionCall.Name, deserializedContent.FunctionCall.Name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ public void Constructor_InvalidArguments_Throws()
{
FunctionCallContent functionCall = new("FCC1", "TestFunction");

Assert.Throws<ArgumentNullException>("id", () => new FunctionApprovalResponseContent(null!, true, functionCall));
Assert.Throws<ArgumentException>("id", () => new FunctionApprovalResponseContent("", true, functionCall));
Assert.Throws<ArgumentException>("id", () => new FunctionApprovalResponseContent("\r\t\n ", true, functionCall));
Assert.Throws<ArgumentNullException>("requestId", () => new FunctionApprovalResponseContent(null!, true, functionCall));
Assert.Throws<ArgumentException>("requestId", () => new FunctionApprovalResponseContent("", true, functionCall));
Assert.Throws<ArgumentException>("requestId", () => new FunctionApprovalResponseContent("\r\t\n ", true, functionCall));

Assert.Throws<ArgumentNullException>("functionCall", () => new FunctionApprovalResponseContent("id", true, null!));
Assert.Throws<ArgumentNullException>("functionCall", () => new FunctionApprovalResponseContent("requestId", true, null!));
}

[Theory]
[InlineData("abc", true)]
[InlineData("123", false)]
[InlineData("!@#", true)]
public void Constructor_Roundtrips(string id, bool approved)
public void Constructor_Roundtrips(string requestId, bool approved)
{
FunctionCallContent functionCall = new("FCC1", "TestFunction");
FunctionApprovalResponseContent content = new(id, approved, functionCall);
FunctionApprovalResponseContent content = new(requestId, approved, functionCall);

Assert.Same(id, content.Id);
Assert.Same(requestId, content.RequestId);
Assert.Equal(approved, content.Approved);
Assert.Same(functionCall, content.FunctionCall);
}
Expand All @@ -44,7 +44,7 @@ public void Serialization_Roundtrips()
var deserializedContent = JsonSerializer.Deserialize<FunctionApprovalResponseContent>(json, AIJsonUtilities.DefaultOptions);

Assert.NotNull(deserializedContent);
Assert.Equal(content.Id, deserializedContent.Id);
Assert.Equal(content.RequestId, deserializedContent.RequestId);
Assert.Equal(content.Approved, deserializedContent.Approved);
Assert.NotNull(deserializedContent.FunctionCall);
Assert.Equal(content.FunctionCall.CallId, deserializedContent.FunctionCall.CallId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ public class UserInputRequestContentTests
[Fact]
public void Constructor_InvalidArguments_Throws()
{
Assert.Throws<ArgumentNullException>("id", () => new TestUserInputRequestContent(null!));
Assert.Throws<ArgumentException>("id", () => new TestUserInputRequestContent(""));
Assert.Throws<ArgumentException>("id", () => new TestUserInputRequestContent("\r\t\n "));
Assert.Throws<ArgumentNullException>("requestId", () => new TestUserInputRequestContent(null!));
Assert.Throws<ArgumentException>("requestId", () => new TestUserInputRequestContent(""));
Assert.Throws<ArgumentException>("requestId", () => new TestUserInputRequestContent("\r\t\n "));
}

[Theory]
[InlineData("abc")]
[InlineData("123")]
[InlineData("!@#")]
public void Constructor_Roundtrips(string id)
public void Constructor_Roundtrips(string requestId)
{
TestUserInputRequestContent content = new(id);
TestUserInputRequestContent content = new(requestId);

Assert.Equal(id, content.Id);
Assert.Equal(requestId, content.RequestId);
}

[Fact]
Expand Down Expand Up @@ -59,8 +59,8 @@ public void Serialization_DerivedTypes_Roundtrips()

private sealed class TestUserInputRequestContent : UserInputRequestContent
{
public TestUserInputRequestContent(string id)
: base(id)
public TestUserInputRequestContent(string requestId)
: base(requestId)
{
}
}
Expand Down
Loading
Loading