diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 5e83e0d577..19bd70400e 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -33,18 +33,18 @@ - + - + - - - + + + @@ -63,12 +63,12 @@ - - - - + + + + - + @@ -76,11 +76,11 @@ - + - + @@ -110,7 +110,7 @@ - + diff --git a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs index fafe9ccf83..5d770ff3fd 100644 --- a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs +++ b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs @@ -59,14 +59,14 @@ { switch (content) { - case FunctionApprovalRequestContent approvalRequest: - DisplayApprovalRequest(approvalRequest); + case ToolApprovalRequestContent approvalRequest when approvalRequest.ToolCall is FunctionCallContent fcc: + DisplayApprovalRequest(approvalRequest, fcc); - Console.Write($"\nApprove '{approvalRequest.FunctionCall.Name}'? (yes/no): "); + Console.Write($"\nApprove '{fcc.Name}'? (yes/no): "); string? userInput = Console.ReadLine(); bool approved = userInput?.ToUpperInvariant() is "YES" or "Y"; - FunctionApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved); + ToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved); if (approvalRequest.AdditionalProperties != null) { @@ -128,19 +128,19 @@ } #pragma warning disable MEAI001 -static void DisplayApprovalRequest(FunctionApprovalRequestContent approvalRequest) +static void DisplayApprovalRequest(ToolApprovalRequestContent approvalRequest, FunctionCallContent fcc) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(); Console.WriteLine("============================================================"); Console.WriteLine("APPROVAL REQUIRED"); Console.WriteLine("============================================================"); - Console.WriteLine($"Function: {approvalRequest.FunctionCall.Name}"); + Console.WriteLine($"Function: {fcc.Name}"); - if (approvalRequest.FunctionCall.Arguments != null) + if (fcc.Arguments != null) { Console.WriteLine("Arguments:"); - foreach (var arg in approvalRequest.FunctionCall.Arguments) + foreach (var arg in fcc.Arguments) { Console.WriteLine($" {arg.Key} = {arg.Value}"); } diff --git a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs index ee0191fd98..866bbfad31 100644 --- a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs +++ b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs @@ -9,7 +9,7 @@ /// /// A delegating agent that handles server function approval requests and responses. -/// Transforms between FunctionApprovalRequestContent/FunctionApprovalResponseContent +/// Transforms between ToolApprovalRequestContent/ToolApprovalResponseContent /// and the server's request_approval tool call pattern. /// internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent @@ -50,14 +50,14 @@ protected override async IAsyncEnumerable RunCoreStreamingA } #pragma warning disable MEAI001 // Type is for evaluation purposes only - private static FunctionResultContent ConvertApprovalResponseToToolResult(FunctionApprovalResponseContent approvalResponse, JsonSerializerOptions jsonOptions) + private static FunctionResultContent ConvertApprovalResponseToToolResult(ToolApprovalResponseContent approvalResponse, JsonSerializerOptions jsonOptions) { return new FunctionResultContent( - callId: approvalResponse.Id, + callId: approvalResponse.RequestId, result: JsonSerializer.SerializeToElement( new ApprovalResponse { - ApprovalId = approvalResponse.Id, + ApprovalId = approvalResponse.RequestId, Approved = approvalResponse.Approved }, jsonOptions)); @@ -89,7 +89,7 @@ private static List ProcessOutgoingServerFunctionApprovals( { List? result = null; - Dictionary approvalRequests = []; + Dictionary approvalRequests = []; for (var messageIndex = 0; messageIndex < messages.Count; messageIndex++) { var message = messages[messageIndex]; @@ -102,21 +102,21 @@ private static List ProcessOutgoingServerFunctionApprovals( var content = message.Contents[contentIndex]; // Handle pending approval requests (transform to tool call) - if (content is FunctionApprovalRequestContent approvalRequest && + if (content is ToolApprovalRequestContent approvalRequest && approvalRequest.AdditionalProperties?.TryGetValue("original_function", out var originalFunction) == true && originalFunction is FunctionCallContent original) { - approvalRequests[approvalRequest.Id] = approvalRequest; + approvalRequests[approvalRequest.RequestId] = approvalRequest; transformedContents ??= CopyContentsUpToIndex(message.Contents, contentIndex); transformedContents.Add(original); } // Handle pending approval responses (transform to tool result) - else if (content is FunctionApprovalResponseContent approvalResponse && - approvalRequests.TryGetValue(approvalResponse.Id, out var correspondingRequest)) + else if (content is ToolApprovalResponseContent approvalResponse && + approvalRequests.TryGetValue(approvalResponse.RequestId, out var correspondingRequest)) { transformedContents ??= CopyContentsUpToIndex(message.Contents, contentIndex); transformedContents.Add(ConvertApprovalResponseToToolResult(approvalResponse, jsonSerializerOptions)); - approvalRequests.Remove(approvalResponse.Id); + approvalRequests.Remove(approvalResponse.RequestId); correspondingRequest.AdditionalProperties?.Remove("original_function"); } // Skip historical approval content @@ -198,8 +198,8 @@ private static AgentResponseUpdate ProcessIncomingServerApprovalRequests( var functionCallArgs = (Dictionary?)approvalRequest.FunctionArguments? .Deserialize(jsonSerializerOptions.GetTypeInfo(typeof(Dictionary))); - var approvalRequestContent = new FunctionApprovalRequestContent( - id: approvalRequest.ApprovalId, + var approvalRequestContent = new ToolApprovalRequestContent( + requestId: approvalRequest.ApprovalId, new FunctionCallContent( callId: approvalRequest.ApprovalId, name: approvalRequest.FunctionName, diff --git a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs index 62209792f6..ff3e6ffbb1 100644 --- a/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs +++ b/dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs @@ -9,7 +9,7 @@ /// /// A delegating agent that handles function approval requests on the server side. -/// Transforms between FunctionApprovalRequestContent/FunctionApprovalResponseContent +/// Transforms between ToolApprovalRequestContent/ToolApprovalResponseContent /// and the request_approval tool call pattern for client communication. /// internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent @@ -50,7 +50,7 @@ protected override async IAsyncEnumerable RunCoreStreamingA } #pragma warning disable MEAI001 // Type is for evaluation purposes only - private static FunctionApprovalRequestContent ConvertToolCallToApprovalRequest(FunctionCallContent toolCall, JsonSerializerOptions jsonSerializerOptions) + private static ToolApprovalRequestContent ConvertToolCallToApprovalRequest(FunctionCallContent toolCall, JsonSerializerOptions jsonSerializerOptions) { if (toolCall.Name != "request_approval" || toolCall.Arguments == null) { @@ -67,15 +67,15 @@ reqObj is JsonElement argsElement && throw new InvalidOperationException("Failed to deserialize approval request from tool call"); } - return new FunctionApprovalRequestContent( - id: request.ApprovalId, + return new ToolApprovalRequestContent( + requestId: request.ApprovalId, new FunctionCallContent( callId: request.ApprovalId, name: request.FunctionName, arguments: request.FunctionArguments)); } - private static FunctionApprovalResponseContent ConvertToolResultToApprovalResponse(FunctionResultContent result, FunctionApprovalRequestContent approval, JsonSerializerOptions jsonSerializerOptions) + private static ToolApprovalResponseContent ConvertToolResultToApprovalResponse(FunctionResultContent result, ToolApprovalRequestContent approval, JsonSerializerOptions jsonSerializerOptions) { var approvalResponse = result.Result is JsonElement je ? (ApprovalResponse?)je.Deserialize(jsonSerializerOptions.GetTypeInfo(typeof(ApprovalResponse))) : @@ -121,7 +121,7 @@ private static List ProcessIncomingFunctionApprovals( // Track approval ID to original call ID mapping _ = new Dictionary(); #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. - Dictionary trackedRequestApprovalToolCalls = new(); // Remote approvals + Dictionary trackedRequestApprovalToolCalls = new(); // Remote approvals for (int messageIndex = 0; messageIndex < messages.Count; messageIndex++) { var message = messages[messageIndex]; @@ -181,11 +181,10 @@ private static AgentResponseUpdate ProcessOutgoingApprovalRequests( { var content = update.Contents[i]; #pragma warning disable MEAI001 // Type is for evaluation purposes only - if (content is FunctionApprovalRequestContent request) + if (content is ToolApprovalRequestContent request && request.ToolCall is FunctionCallContent functionCall) { updatedContents ??= [.. update.Contents]; - var functionCall = request.FunctionCall; - var approvalId = request.Id; + var approvalId = request.RequestId; var approvalData = new ApprovalRequest { diff --git a/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Program.cs index 611f3f9a9a..baa6677a4f 100644 --- a/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Program.cs @@ -11,8 +11,8 @@ AIAgent agent = new OpenAIClient( apiKey) - .GetResponsesClient(model) - .AsAIAgent(instructions: "You are good at telling jokes.", name: "Joker"); + .GetResponsesClient() + .AsAIAgent(model: model, instructions: "You are good at telling jokes.", name: "Joker"); // Invoke the agent and output the text result. Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.")); diff --git a/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs index d13d0d5346..12e30dc203 100644 --- a/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs @@ -10,8 +10,8 @@ var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-5"; var client = new OpenAIClient(apiKey) - .GetResponsesClient(model) - .AsIChatClient().AsBuilder() + .GetResponsesClient() + .AsIChatClient(model).AsBuilder() .ConfigureOptions(o => { o.Reasoning = new() diff --git a/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs index 196bd64922..4deb134fd7 100644 --- a/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs @@ -20,19 +20,21 @@ public class OpenAIResponseClientAgent : DelegatingAIAgent /// Optional instructions for the agent. /// Optional name for the agent. /// Optional description for the agent. + /// Optional default model ID to use for requests. Required when using a plain (not via Azure OpenAI). /// Optional instance of public OpenAIResponseClientAgent( ResponsesClient client, string? instructions = null, string? name = null, string? description = null, + string? model = null, ILoggerFactory? loggerFactory = null) : this(client, new() { Name = name, Description = description, ChatOptions = new ChatOptions() { Instructions = instructions }, - }, loggerFactory) + }, model, loggerFactory) { } @@ -41,10 +43,11 @@ public OpenAIResponseClientAgent( /// /// Instance of /// Options to create the agent. + /// Optional default model ID to use for requests. Required when using a plain (not via Azure OpenAI). /// Optional instance of public OpenAIResponseClientAgent( - ResponsesClient client, ChatClientAgentOptions options, ILoggerFactory? loggerFactory = null) : - base(new ChatClientAgent((client ?? throw new ArgumentNullException(nameof(client))).AsIChatClient(), options, loggerFactory)) + ResponsesClient client, ChatClientAgentOptions options, string? model = null, ILoggerFactory? loggerFactory = null) : + base(new ChatClientAgent((client ?? throw new ArgumentNullException(nameof(client))).AsIChatClient(model), options, loggerFactory)) { } diff --git a/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs index 8004770c21..dbd11ce3c6 100644 --- a/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs @@ -10,10 +10,10 @@ var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt-4o-mini"; // Create a ResponsesClient directly from OpenAIClient -ResponsesClient responseClient = new OpenAIClient(apiKey).GetResponsesClient(model); +ResponsesClient responseClient = new OpenAIClient(apiKey).GetResponsesClient(); // Create an agent directly from the ResponsesClient using OpenAIResponseClientAgent -OpenAIResponseClientAgent agent = new(responseClient, instructions: "You are good at telling jokes.", name: "Joker"); +OpenAIResponseClientAgent agent = new(responseClient, instructions: "You are good at telling jokes.", name: "Joker", model: model); ResponseItem userMessage = ResponseItem.CreateUserMessageItem("Tell me a joke about a pirate."); diff --git a/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs index 921acbad0d..603f8b8e7b 100644 --- a/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs +++ b/dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs @@ -22,7 +22,7 @@ ConversationClient conversationClient = openAIClient.GetConversationClient(); // Create an agent directly from the ResponsesClient using OpenAIResponseClientAgent -ChatClientAgent agent = new(openAIClient.GetResponsesClient(model).AsIChatClient(), instructions: "You are a helpful assistant.", name: "ConversationAgent"); +ChatClientAgent agent = new(openAIClient.GetResponsesClient().AsIChatClient(model), instructions: "You are a helpful assistant.", name: "ConversationAgent"); ClientResult createConversationResult = await conversationClient.CreateConversationAsync(BinaryContent.Create(BinaryData.FromString("{}"))); diff --git a/dotnet/samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Program.cs index 5bdfc9421c..8ff4181a51 100644 --- a/dotnet/samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Program.cs +++ b/dotnet/samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Program.cs @@ -36,11 +36,11 @@ static string GetWeather([Description("The location to get the weather for.")] s // For simplicity, we are assuming here that only function approvals are pending. AgentSession session = await agent.CreateSessionAsync(); AgentResponse response = await agent.RunAsync("What is the weather like in Amsterdam?", session); -List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); +List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); // For streaming use: // var updates = await agent.RunStreamingAsync("What is the weather like in Amsterdam?", session).ToListAsync(); -// approvalRequests = updates.SelectMany(x => x.Contents).OfType().ToList(); +// approvalRequests = updates.SelectMany(x => x.Contents).OfType().ToList(); while (approvalRequests.Count > 0) { @@ -48,18 +48,18 @@ static string GetWeather([Description("The location to get the weather for.")] s List userInputResponses = approvalRequests .ConvertAll(functionApprovalRequest => { - Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {functionApprovalRequest.FunctionCall.Name}"); + Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {((FunctionCallContent)functionApprovalRequest.ToolCall).Name}"); return new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false)]); }); // Pass the user input responses back to the agent for further processing. response = await agent.RunAsync(userInputResponses, session); - approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); + approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); // For streaming use: // updates = await agent.RunStreamingAsync(userInputResponses, session).ToListAsync(); - // approvalRequests = updates.SelectMany(x => x.Contents).OfType().ToList(); + // approvalRequests = updates.SelectMany(x => x.Contents).OfType().ToList(); } Console.WriteLine($"\nAgent: {response}"); diff --git a/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs index 09cd540378..18969ed66e 100644 --- a/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs +++ b/dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs @@ -246,7 +246,7 @@ async Task ConsolePromptingApprovalMiddleware(IEnumerable approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); + List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); while (approvalRequests.Count > 0) { @@ -255,13 +255,13 @@ async Task ConsolePromptingApprovalMiddleware(IEnumerable { - Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {functionApprovalRequest.FunctionCall.Name}"); + Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {((FunctionCallContent)functionApprovalRequest.ToolCall).Name}"); return new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false)]); }); response = await innerAgent.RunAsync(response.Messages, session, options, cancellationToken); - approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); + approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); } return response; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs index f33fae35f4..08051a500e 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs @@ -40,7 +40,7 @@ static string GetWeather([Description("The location to get the weather for.")] s // Check if there are any approval requests. // For simplicity, we are assuming here that only function approvals are pending. -List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); +List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); while (approvalRequests.Count > 0) { @@ -48,7 +48,7 @@ static string GetWeather([Description("The location to get the weather for.")] s List userInputMessages = approvalRequests .ConvertAll(functionApprovalRequest => { - Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {functionApprovalRequest.FunctionCall.Name}"); + Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {((FunctionCallContent)functionApprovalRequest.ToolCall).Name}"); bool approved = Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false; return new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(approved)]); }); @@ -56,7 +56,7 @@ static string GetWeather([Description("The location to get the weather for.")] s // Pass the user input responses back to the agent for further processing. response = await agent.RunAsync(userInputMessages, session); - approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); + approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); } Console.WriteLine($"\nAgent: {response}"); diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs index 7ea6bc88a3..824e1507b3 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs @@ -197,7 +197,7 @@ async Task ConsolePromptingApprovalMiddleware(IEnumerable approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); + List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); while (approvalRequests.Count > 0) { @@ -206,14 +206,14 @@ async Task ConsolePromptingApprovalMiddleware(IEnumerable { - Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {functionApprovalRequest.FunctionCall.Name}"); + Console.WriteLine($"The agent would like to invoke the following function, please reply Y to approve: Name {((FunctionCallContent)functionApprovalRequest.ToolCall).Name}"); bool approved = Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false; return new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(approved)]); }); response = await innerAgent.RunAsync(response.Messages, session, options, cancellationToken); - approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); + approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); } return response; diff --git a/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs index 99d26c103d..e34c3d932e 100644 --- a/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs @@ -81,7 +81,7 @@ // For simplicity, we are assuming here that only mcp tool approvals are pending. AgentSession sessionWithRequiredApproval = await agentWithRequiredApproval.CreateSessionAsync(); AgentResponse response = await agentWithRequiredApproval.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", sessionWithRequiredApproval); -List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); +List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); while (approvalRequests.Count > 0) { @@ -89,11 +89,12 @@ List userInputResponses = approvalRequests .ConvertAll(approvalRequest => { + McpServerToolCallContent mcpToolCall = (McpServerToolCallContent)approvalRequest.ToolCall!; Console.WriteLine($""" The agent would like to invoke the following MCP Tool, please reply Y to approve. - ServerName: {approvalRequest.ToolCall.ServerName} - Name: {approvalRequest.ToolCall.ToolName} - Arguments: {string.Join(", ", approvalRequest.ToolCall.Arguments?.Select(x => $"{x.Key}: {x.Value}") ?? [])} + ServerName: {mcpToolCall.ServerName} + Name: {mcpToolCall.Name} + Arguments: {string.Join(", ", mcpToolCall.Arguments?.Select(x => $"{x.Key}: {x.Value}") ?? [])} """); return new ChatMessage(ChatRole.User, [approvalRequest.CreateResponse(Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false)]); }); @@ -101,7 +102,7 @@ // Pass the user input responses back to the agent for further processing. response = await agentWithRequiredApproval.RunAsync(userInputResponses, sessionWithRequiredApproval); - approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); + approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); } Console.WriteLine($"\nAgent: {response}"); diff --git a/dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs b/dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs index 194952e68a..66180b4cad 100644 --- a/dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs @@ -70,7 +70,7 @@ // For simplicity, we are assuming here that only mcp tool approvals are pending. AgentSession sessionWithRequiredApproval = await agentWithRequiredApproval.CreateSessionAsync(); AgentResponse response = await agentWithRequiredApproval.RunAsync("Please summarize the Azure AI Agent documentation related to MCP Tool calling?", sessionWithRequiredApproval); -List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); +List approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); while (approvalRequests.Count > 0) { @@ -78,11 +78,12 @@ List userInputResponses = approvalRequests .ConvertAll(approvalRequest => { + McpServerToolCallContent mcpToolCall = (McpServerToolCallContent)approvalRequest.ToolCall!; Console.WriteLine($""" The agent would like to invoke the following MCP Tool, please reply Y to approve. - ServerName: {approvalRequest.ToolCall.ServerName} - Name: {approvalRequest.ToolCall.ToolName} - Arguments: {string.Join(", ", approvalRequest.ToolCall.Arguments?.Select(x => $"{x.Key}: {x.Value}") ?? [])} + ServerName: {mcpToolCall.ServerName} + Name: {mcpToolCall.Name} + Arguments: {string.Join(", ", mcpToolCall.Arguments?.Select(x => $"{x.Key}: {x.Value}") ?? [])} """); return new ChatMessage(ChatRole.User, [approvalRequest.CreateResponse(Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false)]); }); @@ -90,7 +91,7 @@ // Pass the user input responses back to the agent for further processing. response = await agentWithRequiredApproval.RunAsync(userInputResponses, sessionWithRequiredApproval); - approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); + approvalRequests = response.Messages.SelectMany(m => m.Contents).OfType().ToList(); } Console.WriteLine($"\nAgent: {response}"); diff --git a/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/Program.cs b/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/Program.cs index 076e764ea8..a8d42b5342 100644 --- a/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/Program.cs +++ b/dotnet/samples/03-workflows/Agents/GroupChatToolApproval/Program.cs @@ -17,7 +17,7 @@ // // Demonstrate: // - Using custom GroupChatManager with agents that have approval-required tools. -// - Handling FunctionApprovalRequestContent in group chat scenarios. +// - Handling ToolApprovalRequestContent in group chat scenarios. // - Multi-round group chat with tool approval interruption and resumption. using System.ComponentModel; @@ -101,16 +101,16 @@ private static async Task Main() { case RequestInfoEvent e: { - if (e.Request.TryGetDataAs(out FunctionApprovalRequestContent? approvalRequestContent)) + if (e.Request.TryGetDataAs(out ToolApprovalRequestContent? approvalRequestContent)) { Console.WriteLine(); Console.WriteLine($"[APPROVAL REQUIRED] From agent: {e.Request.PortInfo.PortId}"); - Console.WriteLine($" Tool: {approvalRequestContent.FunctionCall.Name}"); - Console.WriteLine($" Arguments: {JsonSerializer.Serialize(approvalRequestContent.FunctionCall.Arguments)}"); + Console.WriteLine($" Tool: {((FunctionCallContent)approvalRequestContent.ToolCall).Name}"); + Console.WriteLine($" Arguments: {JsonSerializer.Serialize(((FunctionCallContent)approvalRequestContent.ToolCall).Arguments)}"); Console.WriteLine(); // Approve the tool call request - Console.WriteLine($"Tool: {approvalRequestContent.FunctionCall.Name} approved"); + Console.WriteLine($"Tool: {((FunctionCallContent)approvalRequestContent.ToolCall).Name} approved"); await run.SendResponseAsync(e.Request.CreateResponse(approvalRequestContent.CreateResponse(approved: true))); } diff --git a/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs index 0d2470762e..839c8e75a1 100644 --- a/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs +++ b/dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs @@ -27,7 +27,7 @@ public override async IAsyncEnumerable RunStreamingAsync( Transport = new HttpClientPipelineTransport(httpClient) }; - var openAiClient = new ResponsesClient(model: agentName, credential: new ApiKeyCredential("dummy-key"), options: options).AsIChatClient(); + var openAiClient = new ResponsesClient(credential: new ApiKeyCredential("dummy-key"), options: options).AsIChatClient(agentName); var chatOptions = new ChatOptions() { ConversationId = sessionId diff --git a/dotnet/samples/05-end-to-end/M365Agent/AFAgentApplication.cs b/dotnet/samples/05-end-to-end/M365Agent/AFAgentApplication.cs index 7e58819a65..502c57204e 100644 --- a/dotnet/samples/05-end-to-end/M365Agent/AFAgentApplication.cs +++ b/dotnet/samples/05-end-to-end/M365Agent/AFAgentApplication.cs @@ -122,7 +122,7 @@ private static ChatMessage HandleUserInput(ITurnContext turnContext) && valueElement.GetProperty("requestJson") is JsonElement requestJsonElement && requestJsonElement.ValueKind == JsonValueKind.String) { - var requestContent = JsonSerializer.Deserialize(requestJsonElement.GetString()!, JsonUtilities.DefaultOptions); + var requestContent = JsonSerializer.Deserialize(requestJsonElement.GetString()!, JsonUtilities.DefaultOptions); return new ChatMessage(ChatRole.User, [requestContent!.CreateResponse(approvedJsonElement.ValueKind == JsonValueKind.True)]); } @@ -138,7 +138,7 @@ private static ChatMessage HandleUserInput(ITurnContext turnContext) /// The list of to which the adaptive cards will be added. private static void HandleUserInputRequests(AgentResponse response, ref List? attachments) { - foreach (FunctionApprovalRequestContent functionApprovalRequest in response.Messages.SelectMany(m => m.Contents).OfType()) + foreach (ToolApprovalRequestContent functionApprovalRequest in response.Messages.SelectMany(m => m.Contents).OfType()) { var functionApprovalRequestJson = JsonSerializer.Serialize(functionApprovalRequest, JsonUtilities.DefaultOptions); @@ -152,7 +152,7 @@ private static void HandleUserInputRequests(AgentResponse response, ref List ToStreamingResponse TextReasoningContent => new TextReasoningContentEventGenerator(context.IdGenerator, seq, outputIndex), FunctionCallContent => new FunctionCallEventGenerator(context.IdGenerator, seq, outputIndex, context.JsonSerializerOptions), FunctionResultContent => new FunctionResultEventGenerator(context.IdGenerator, seq, outputIndex), - FunctionApprovalRequestContent => new FunctionApprovalRequestEventGenerator(context.IdGenerator, seq, outputIndex, context.JsonSerializerOptions), - FunctionApprovalResponseContent => new FunctionApprovalResponseEventGenerator(context.IdGenerator, seq, outputIndex), + ToolApprovalRequestContent => new ToolApprovalRequestEventGenerator(context.IdGenerator, seq, outputIndex, context.JsonSerializerOptions), + ToolApprovalResponseContent => new ToolApprovalResponseEventGenerator(context.IdGenerator, seq, outputIndex), ErrorContent => new ErrorContentEventGenerator(context.IdGenerator, seq, outputIndex), UriContent uriContent when uriContent.HasTopLevelMediaType("image") => new ImageContentEventGenerator(context.IdGenerator, seq, outputIndex), DataContent dataContent when dataContent.HasTopLevelMediaType("image") => new ImageContentEventGenerator(context.IdGenerator, seq, outputIndex), diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Streaming/FunctionApprovalRequestEventGenerator.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Streaming/FunctionApprovalRequestEventGenerator.cs index 4e565b0784..f68fa12e4d 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Streaming/FunctionApprovalRequestEventGenerator.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Streaming/FunctionApprovalRequestEventGenerator.cs @@ -12,33 +12,37 @@ namespace Microsoft.Agents.AI.Hosting.OpenAI.Responses.Streaming; /// A generator for streaming events from function approval request content. /// This is a non-standard DevUI extension for human-in-the-loop scenarios. /// -internal sealed class FunctionApprovalRequestEventGenerator( +internal sealed class ToolApprovalRequestEventGenerator( IdGenerator idGenerator, SequenceNumber seq, int outputIndex, JsonSerializerOptions jsonSerializerOptions) : StreamingEventGenerator { - public override bool IsSupported(AIContent content) => content is FunctionApprovalRequestContent; + public override bool IsSupported(AIContent content) => content is ToolApprovalRequestContent; public override IEnumerable ProcessContent(AIContent content) { - if (content is not FunctionApprovalRequestContent approvalRequest) + if (content is not ToolApprovalRequestContent approvalRequest) { - throw new InvalidOperationException("FunctionApprovalRequestEventGenerator only supports FunctionApprovalRequestContent."); + throw new InvalidOperationException("ToolApprovalRequestEventGenerator only supports ToolApprovalRequestContent."); } + if (approvalRequest.ToolCall is not FunctionCallContent functionCall) + { + yield break; + } yield return new StreamingFunctionApprovalRequested { SequenceNumber = seq.Increment(), OutputIndex = outputIndex, - RequestId = approvalRequest.Id, + RequestId = approvalRequest.RequestId, ItemId = idGenerator.GenerateMessageId(), FunctionCall = new FunctionCallInfo { - Id = approvalRequest.FunctionCall.CallId, - Name = approvalRequest.FunctionCall.Name, + Id = functionCall.CallId, + Name = functionCall.Name, Arguments = JsonSerializer.SerializeToElement( - approvalRequest.FunctionCall.Arguments, + functionCall.Arguments, jsonSerializerOptions.GetTypeInfo(typeof(IDictionary))) } }; diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Streaming/FunctionApprovalResponseEventGenerator.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Streaming/FunctionApprovalResponseEventGenerator.cs index ab4af8f408..df1379cfbb 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Streaming/FunctionApprovalResponseEventGenerator.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Streaming/FunctionApprovalResponseEventGenerator.cs @@ -11,25 +11,25 @@ namespace Microsoft.Agents.AI.Hosting.OpenAI.Responses.Streaming; /// A generator for streaming events from function approval response content. /// This is a non-standard DevUI extension for human-in-the-loop scenarios. /// -internal sealed class FunctionApprovalResponseEventGenerator( +internal sealed class ToolApprovalResponseEventGenerator( IdGenerator idGenerator, SequenceNumber seq, int outputIndex) : StreamingEventGenerator { - public override bool IsSupported(AIContent content) => content is FunctionApprovalResponseContent; + public override bool IsSupported(AIContent content) => content is ToolApprovalResponseContent; public override IEnumerable ProcessContent(AIContent content) { - if (content is not FunctionApprovalResponseContent approvalResponse) + if (content is not ToolApprovalResponseContent approvalResponse) { - throw new InvalidOperationException("FunctionApprovalResponseEventGenerator only supports FunctionApprovalResponseContent."); + throw new InvalidOperationException("ToolApprovalResponseEventGenerator only supports ToolApprovalResponseContent."); } yield return new StreamingFunctionApprovalResponded { SequenceNumber = seq.Increment(), OutputIndex = outputIndex, - RequestId = approvalResponse.Id, + RequestId = approvalResponse.RequestId, Approved = approvalResponse.Approved, ItemId = idGenerator.GenerateMessageId() }; diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/AsyncStreamingChatCompletionUpdateCollectionResult.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/AsyncStreamingChatCompletionUpdateCollectionResult.cs index db0c7a8673..34c07ea73f 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/AsyncStreamingChatCompletionUpdateCollectionResult.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/AsyncStreamingChatCompletionUpdateCollectionResult.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +#pragma warning disable OPENAI001 // Experimental OpenAI features + using System.ClientModel; using OpenAI.Chat; diff --git a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIResponseClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIResponseClientExtensions.cs index 98561704f2..da7826e785 100644 --- a/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIResponseClientExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIResponseClientExtensions.cs @@ -26,6 +26,7 @@ public static class OpenAIResponseClientExtensions /// Creates an AI agent from an using the OpenAI Response API. /// /// The to use for the agent. + /// Optional default model ID to use for requests. Required when using a plain (not via Azure OpenAI). /// Optional system instructions that define the agent's behavior and personality. /// Optional name for the agent for identification purposes. /// Optional description of the agent's capabilities and purpose. @@ -37,6 +38,7 @@ public static class OpenAIResponseClientExtensions /// Thrown when is . public static ChatClientAgent AsAIAgent( this ResponsesClient client, + string? model = null, string? instructions = null, string? name = null, string? description = null, @@ -58,6 +60,7 @@ public static ChatClientAgent AsAIAgent( Tools = tools, } }, + model, clientFactory, loggerFactory, services); @@ -68,6 +71,7 @@ public static ChatClientAgent AsAIAgent( /// /// The to use for the agent. /// Full set of options to configure the agent. + /// Optional default model ID to use for requests. Required when using a plain (not via Azure OpenAI). /// Provides a way to customize the creation of the underlying used by the agent. /// Optional logger factory for enabling logging within the agent. /// An optional to use for resolving services required by the instances being invoked. @@ -76,6 +80,7 @@ public static ChatClientAgent AsAIAgent( public static ChatClientAgent AsAIAgent( this ResponsesClient client, ChatClientAgentOptions options, + string? model = null, Func? clientFactory = null, ILoggerFactory? loggerFactory = null, IServiceProvider? services = null) @@ -83,7 +88,7 @@ public static ChatClientAgent AsAIAgent( Throw.IfNull(client); Throw.IfNull(options); - var chatClient = client.AsIChatClient(); + var chatClient = client.AsIChatClient(model); if (clientFactory is not null) { @@ -100,13 +105,14 @@ public static ChatClientAgent AsAIAgent( /// This corresponds to setting the "store" property in the JSON representation to false. /// /// The client. + /// Optional default model ID to use for requests. Required when using a plain (not via Azure OpenAI). /// An that can be used to converse via the that does not store responses for later retrieval. /// is . [Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)] - public static IChatClient AsIChatClientWithStoredOutputDisabled(this ResponsesClient responseClient) + public static IChatClient AsIChatClientWithStoredOutputDisabled(this ResponsesClient responseClient, string? model = null) { return Throw.IfNull(responseClient) - .AsIChatClient() + .AsIChatClient(model) .AsBuilder() .ConfigureOptions(x => x.RawRepresentationFactory = _ => new CreateResponseOptions() { StoredOutputEnabled = false }) .Build(); diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs index 751f518277..a2e2a6cc22 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs @@ -184,8 +184,8 @@ private static string ComputeHeadersHash(IDictionary? headers) private static void PopulateResultContent(McpServerToolResultContent resultContent, CallToolResult result) { - // Ensure Output list is initialized - resultContent.Output ??= []; + // Ensure Outputs list is initialized + resultContent.Outputs ??= []; if (result.IsError == true) { @@ -202,7 +202,7 @@ private static void PopulateResultContent(McpServerToolResultContent resultConte } } - resultContent.Output.Add(new TextContent($"Error: {errorText ?? "Unknown error from MCP Server call"}")); + resultContent.Outputs.Add(new TextContent($"Error: {errorText ?? "Unknown error from MCP Server call"}")); return; } @@ -217,7 +217,7 @@ private static void PopulateResultContent(McpServerToolResultContent resultConte AIContent content = ConvertContentBlock(block); if (content is not null) { - resultContent.Output.Add(content); + resultContent.Outputs.Add(content); } } } diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs index c8cde902fa..24653af0f2 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs @@ -150,7 +150,7 @@ [.. agentResponse.Messages foreach (ChatMessage responseMessage in agentResponse.Messages) { - if (responseMessage.Contents.Any(content => content is UserInputRequestContent)) + if (responseMessage.Contents.Any(content => content is ToolApprovalRequestContent)) { yield return responseMessage; continue; diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs index 0e95bebe63..baa6f9c6b8 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs @@ -68,7 +68,7 @@ public static class Steps // If approval is required, add user input request content if (requireApproval) { - requestMessage.Contents.Add(new FunctionApprovalRequestContent(this.Id, functionCall)); + requestMessage.Contents.Add(new ToolApprovalRequestContent(this.Id, functionCall)); } AgentResponse agentResponse = new([requestMessage]); diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeMcpToolExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeMcpToolExecutor.cs index 45929f20f7..b1d9a44269 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeMcpToolExecutor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeMcpToolExecutor.cs @@ -85,7 +85,7 @@ public static class Steps toolCall.AdditionalProperties.Add(headers); } - McpServerToolApprovalRequestContent approvalRequest = new(this.Id, toolCall); + ToolApprovalRequestContent approvalRequest = new(this.Id, toolCall); ChatMessage requestMessage = new(ChatRole.Assistant, [approvalRequest]); AgentResponse agentResponse = new([requestMessage]); @@ -127,11 +127,10 @@ public async ValueTask CaptureResponseAsync( ExternalInputResponse response, CancellationToken cancellationToken) { - // Check for approval response - McpServerToolApprovalResponseContent? approvalResponse = response.Messages + ToolApprovalResponseContent? approvalResponse = response.Messages .SelectMany(m => m.Contents) - .OfType() - .FirstOrDefault(r => r.Id == this.Id); + .OfType() + .FirstOrDefault(r => r.RequestId == this.Id); if (approvalResponse?.Approved != true) { @@ -174,7 +173,7 @@ private async ValueTask ProcessResultAsync(IWorkflowContext context, McpServerTo string? conversationId = this.GetConversationId(); await this.AssignResultAsync(context, resultContent).ConfigureAwait(false); - ChatMessage resultMessage = new(ChatRole.Tool, resultContent.Output); + ChatMessage resultMessage = new(ChatRole.Tool, resultContent.Outputs); // Store messages if output path is configured if (this.Model.Output?.Messages is not null) @@ -192,20 +191,20 @@ private async ValueTask ProcessResultAsync(IWorkflowContext context, McpServerTo // Add messages to conversation if conversationId is provided if (conversationId is not null) { - ChatMessage assistantMessage = new(ChatRole.Assistant, resultContent.Output); + ChatMessage assistantMessage = new(ChatRole.Assistant, resultContent.Outputs); await agentProvider.CreateMessageAsync(conversationId, assistantMessage, cancellationToken).ConfigureAwait(false); } } private async ValueTask AssignResultAsync(IWorkflowContext context, McpServerToolResultContent toolResult) { - if (this.Model.Output?.Result is null || toolResult.Output is null || toolResult.Output.Count == 0) + if (this.Model.Output?.Result is null || toolResult.Outputs is null || toolResult.Outputs.Count == 0) { return; } List parsedResults = []; - foreach (AIContent resultContent in toolResult.Output) + foreach (AIContent resultContent in toolResult.Outputs) { object? resultValue = resultContent switch { diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/AIAgentHostOptions.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/AIAgentHostOptions.cs index 623981e204..c981b5d801 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/AIAgentHostOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/AIAgentHostOptions.cs @@ -21,7 +21,7 @@ public sealed class AIAgentHostOptions public bool EmitAgentResponseEvents { get; set; } /// - /// Gets or sets a value indicating whether should be intercepted and sent + /// Gets or sets a value indicating whether should be intercepted and sent /// as a message to the workflow for handling, instead of being raised as a request. /// public bool InterceptUserInputRequests { get; set; } diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/AIAgentHostExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/AIAgentHostExecutor.cs index a38f49681a..6876ddbb99 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/AIAgentHostExecutor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/AIAgentHostExecutor.cs @@ -19,7 +19,7 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor private AgentSession? _session; private bool? _currentTurnEmitEvents; - private AIContentExternalHandler? _userInputHandler; + private AIContentExternalHandler? _userInputHandler; private AIContentExternalHandler? _functionCallHandler; private static readonly ChatProtocolExecutorOptions s_defaultChatProtocolOptions = new() @@ -38,7 +38,7 @@ public AIAgentHostExecutor(AIAgent agent, AIAgentHostOptions options) : base(id: private ProtocolBuilder ConfigureUserInputHandling(ProtocolBuilder protocolBuilder) { - this._userInputHandler = new AIContentExternalHandler( + this._userInputHandler = new AIContentExternalHandler( ref protocolBuilder, portId: $"{this.Id}_UserInput", intercepted: this._options.InterceptUserInputRequests, @@ -59,13 +59,13 @@ protected override ProtocolBuilder ConfigureProtocol(ProtocolBuilder protocolBui } private ValueTask HandleUserInputResponseAsync( - UserInputResponseContent response, + ToolApprovalResponseContent response, IWorkflowContext context, CancellationToken cancellationToken) { - if (!this._userInputHandler!.MarkRequestAsHandled(response.Id)) + if (!this._userInputHandler!.MarkRequestAsHandled(response.RequestId)) { - throw new InvalidOperationException($"No pending UserInputRequest found with id '{response.Id}'."); + throw new InvalidOperationException($"No pending UserInputRequest found with id '{response.RequestId}'."); } List implicitTurnMessages = [new ChatMessage(ChatRole.User, [response])]; @@ -164,7 +164,7 @@ protected override ValueTask TakeTurnAsync(List messages, IWorkflow private async ValueTask InvokeAgentAsync(IEnumerable messages, IWorkflowContext context, bool emitEvents, CancellationToken cancellationToken = default) { #pragma warning disable MEAI001 - Dictionary userInputRequests = new(); + Dictionary userInputRequests = new(); Dictionary functionCalls = new(); AgentResponse response; @@ -218,15 +218,15 @@ void ExtractUnservicedRequests(IEnumerable contents) { foreach (AIContent content in contents) { - if (content is UserInputRequestContent userInputRequest) + if (content is ToolApprovalRequestContent userInputRequest) { // It is an error to simultaneously have multiple outstanding user input requests with the same ID. - userInputRequests.Add(userInputRequest.Id, userInputRequest); + userInputRequests.Add(userInputRequest.RequestId, userInputRequest); } - else if (content is UserInputResponseContent userInputResponse) + else if (content is ToolApprovalResponseContent userInputResponse) { // If the set of messages somehow already has a corresponding user input response, remove it. - _ = userInputRequests.Remove(userInputResponse.Id); + _ = userInputRequests.Remove(userInputResponse.RequestId); } else if (content is FunctionCallContent functionCall) { diff --git a/dotnet/src/Shared/Workflows/Execution/WorkflowRunner.cs b/dotnet/src/Shared/Workflows/Execution/WorkflowRunner.cs index 0f4f0c9217..8135c25570 100644 --- a/dotnet/src/Shared/Workflows/Execution/WorkflowRunner.cs +++ b/dotnet/src/Shared/Workflows/Execution/WorkflowRunner.cs @@ -306,8 +306,7 @@ private async IAsyncEnumerable ProcessInputMessageAsync(ChatMessage requestItem switch { FunctionCallContent functionCall when !functionCall.InformationalOnly => await InvokeFunctionAsync(functionCall).ConfigureAwait(false), - FunctionApprovalRequestContent functionApprovalRequest => ApproveFunction(functionApprovalRequest), - McpServerToolApprovalRequestContent mcpApprovalRequest => ApproveMCP(mcpApprovalRequest), + ToolApprovalRequestContent approvalRequest => ApproveToolCall(approvalRequest), _ => HandleUnknown(requestItem), }; @@ -325,16 +324,16 @@ private async IAsyncEnumerable ProcessInputMessageAsync(ChatMessage return null; } - ChatMessage ApproveFunction(FunctionApprovalRequestContent functionApprovalRequest) + ChatMessage ApproveToolCall(ToolApprovalRequestContent approvalRequest) { - Notify($"INPUT - Approving Function: {functionApprovalRequest.FunctionCall.Name}"); - return new ChatMessage(ChatRole.User, [functionApprovalRequest.CreateResponse(approved: true)]); - } - - ChatMessage ApproveMCP(McpServerToolApprovalRequestContent mcpApprovalRequest) - { - Notify($"INPUT - Approving MCP: {mcpApprovalRequest.ToolCall.ToolName}"); - return new ChatMessage(ChatRole.User, [mcpApprovalRequest.CreateResponse(approved: true)]); + string toolName = approvalRequest.ToolCall switch + { + McpServerToolCallContent mcp => mcp.Name, + FunctionCallContent f => f.Name, + _ => approvalRequest.ToolCall!.CallId + }; + Notify($"INPUT - Approving: {toolName}"); + return new ChatMessage(ChatRole.User, [approvalRequest.CreateResponse(approved: true)]); } async Task InvokeFunctionAsync(FunctionCallContent functionCall) diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/FunctionApprovalTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/FunctionApprovalTests.cs index 296217f931..683c4c0cb4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/FunctionApprovalTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/FunctionApprovalTests.cs @@ -20,7 +20,7 @@ public sealed class FunctionApprovalTests : ConformanceTestBase // Streaming request JSON for OpenAI Responses API private const string StreamingRequestJson = @"{""model"":""gpt-4o-mini"",""input"":""test"",""stream"":true}"; - #region FunctionApprovalRequestContent Tests + #region ToolApprovalRequestContent Tests [Fact] public async Task FunctionApprovalRequest_GeneratesCorrectEvent_SuccessAsync() @@ -34,7 +34,7 @@ public async Task FunctionApprovalRequest_GeneratesCorrectEvent_SuccessAsync() #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates FunctionCallContent functionCall = new(FunctionId, FunctionName, arguments); - FunctionApprovalRequestContent approvalRequest = new(RequestId, functionCall); + ToolApprovalRequestContent approvalRequest = new(RequestId, functionCall); #pragma warning restore MEAI001 HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) => @@ -81,7 +81,7 @@ public async Task FunctionApprovalRequest_WithComplexArguments_GeneratesCorrectE #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates FunctionCallContent functionCall = new(FunctionId, FunctionName, arguments); - FunctionApprovalRequestContent approvalRequest = new(RequestId, functionCall); + ToolApprovalRequestContent approvalRequest = new(RequestId, functionCall); #pragma warning restore MEAI001 HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) => @@ -114,7 +114,7 @@ public async Task FunctionApprovalRequest_EmitsCorrectEventSequence_SuccessAsync #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates FunctionCallContent functionCall = new("call-1", "test_function", new Dictionary()); - FunctionApprovalRequestContent approvalRequest = new("req-1", functionCall); + ToolApprovalRequestContent approvalRequest = new("req-1", functionCall); #pragma warning restore MEAI001 HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) => @@ -150,7 +150,7 @@ public async Task FunctionApprovalRequest_SequenceNumbersAreCorrect_SuccessAsync #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates FunctionCallContent functionCall = new("call-1", "test", new Dictionary()); - FunctionApprovalRequestContent approvalRequest = new("req-1", functionCall); + ToolApprovalRequestContent approvalRequest = new("req-1", functionCall); #pragma warning restore MEAI001 HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) => @@ -173,7 +173,7 @@ public async Task FunctionApprovalRequest_SequenceNumbersAreCorrect_SuccessAsync #endregion - #region FunctionApprovalResponseContent Tests + #region ToolApprovalResponseContent Tests [Fact] public async Task FunctionApprovalResponse_Approved_GeneratesCorrectEvent_SuccessAsync() @@ -187,7 +187,7 @@ public async Task FunctionApprovalResponse_Approved_GeneratesCorrectEvent_Succes #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates FunctionCallContent functionCall = new(FunctionId, FunctionName, arguments); - FunctionApprovalResponseContent approvalResponse = new(RequestId, approved: true, functionCall); + ToolApprovalResponseContent approvalResponse = new(RequestId, approved: true, functionCall); #pragma warning restore MEAI001 HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) => @@ -221,7 +221,7 @@ public async Task FunctionApprovalResponse_Rejected_GeneratesCorrectEvent_Succes #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates FunctionCallContent functionCall = new(FunctionId, FunctionName, new Dictionary { ["path"] = "/important.txt" }); - FunctionApprovalResponseContent approvalResponse = new(RequestId, approved: false, functionCall); + ToolApprovalResponseContent approvalResponse = new(RequestId, approved: false, functionCall); #pragma warning restore MEAI001 HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) => @@ -249,7 +249,7 @@ public async Task FunctionApprovalResponse_EmitsCorrectEventSequence_SuccessAsyn #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates FunctionCallContent functionCall = new("call-1", "test_function", new Dictionary()); - FunctionApprovalResponseContent approvalResponse = new("req-1", approved: true, functionCall); + ToolApprovalResponseContent approvalResponse = new("req-1", approved: true, functionCall); #pragma warning restore MEAI001 HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) => @@ -279,7 +279,7 @@ public async Task MixedContent_ApprovalRequestAndText_GeneratesMultipleEvents_Su #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates FunctionCallContent functionCall = new("call-mixed-1", "test", new Dictionary()); - FunctionApprovalRequestContent approvalRequest = new("req-mixed-1", functionCall); + ToolApprovalRequestContent approvalRequest = new("req-mixed-1", functionCall); #pragma warning restore MEAI001 HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) => @@ -308,10 +308,10 @@ public async Task MixedContent_MultipleApprovalRequests_GeneratesMultipleEvents_ #pragma warning disable MEAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates FunctionCallContent functionCall1 = new("call-multi-1", "function1", new Dictionary()); - FunctionApprovalRequestContent approvalRequest1 = new("req-multi-1", functionCall1); + ToolApprovalRequestContent approvalRequest1 = new("req-multi-1", functionCall1); FunctionCallContent functionCall2 = new("call-multi-2", "function2", new Dictionary()); - FunctionApprovalRequestContent approvalRequest2 = new("req-multi-2", functionCall2); + ToolApprovalRequestContent approvalRequest2 = new("req-multi-2", functionCall2); #pragma warning restore MEAI001 HttpClient client = await this.CreateTestServerAsync(AgentName, "You are a test agent.", string.Empty, (msg) => diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/OpenAIResponsesIntegrationTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/OpenAIResponsesIntegrationTests.cs index 0b9441d633..4c3896ec1d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/OpenAIResponsesIntegrationTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/OpenAIResponsesIntegrationTests.cs @@ -52,7 +52,7 @@ public async Task CreateResponseStreaming_WithSimpleMessage_ReturnsStreamingUpda ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Count to 3"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Count to 3"); // Assert List updates = []; @@ -93,7 +93,7 @@ public async Task CreateResponse_WithSimpleMessage_ReturnsCompleteResponseAsync( ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("Hello"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "Hello"); // Assert Assert.NotNull(response); @@ -120,7 +120,7 @@ public async Task CreateResponseStreaming_WithMultipleChunks_StreamsAllContentAs ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert List updates = []; @@ -166,8 +166,8 @@ public async Task CreateResponse_WithMultipleAgents_EachAgentRespondsCorrectlyAs ResponsesClient responseClient2 = this.CreateResponseClient(Agent2Name); // Act - ResponseResult response1 = await responseClient1.CreateResponseAsync("Hello"); - ResponseResult response2 = await responseClient2.CreateResponseAsync("Hello"); + ResponseResult response1 = await responseClient1.CreateResponseAsync("test-model", "Hello"); + ResponseResult response2 = await responseClient2.CreateResponseAsync("test-model", "Hello"); // Assert string content1 = response1.GetOutputText(); @@ -193,10 +193,10 @@ public async Task CreateResponse_SameAgentStreamingAndNonStreaming_BothWorkCorre ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - Non-streaming - ResponseResult nonStreamingResponse = await responseClient.CreateResponseAsync("Test"); + ResponseResult nonStreamingResponse = await responseClient.CreateResponseAsync("test-model", "Test"); // Act - Streaming - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); StringBuilder streamingContent = new(); await foreach (StreamingResponseUpdate update in streamingResult) { @@ -227,7 +227,7 @@ public async Task CreateResponse_CompletedResponse_HasCorrectStatusAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("Test"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "Test"); // Assert Assert.Equal(ResponseStatus.Completed, response.Status); @@ -250,7 +250,7 @@ public async Task CreateResponseStreaming_VerifyEventSequence_ContainsExpectedEv ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert List updates = []; @@ -289,7 +289,7 @@ public async Task CreateResponseStreaming_EmptyResponse_HandlesGracefullyAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert List updates = []; @@ -319,7 +319,7 @@ public async Task CreateResponse_IncludesMetadata_HasRequiredFieldsAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("Test"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "Test"); // Assert Assert.NotNull(response.Id); @@ -343,7 +343,7 @@ public async Task CreateResponseStreaming_LongText_StreamsAllContentAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Generate long text"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Generate long text"); // Assert StringBuilder contentBuilder = new(); @@ -374,7 +374,7 @@ public async Task CreateResponseStreaming_OutputIndices_AreConsistentAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert List outputIndices = []; @@ -410,7 +410,7 @@ public async Task CreateResponseStreaming_SingleWord_StreamsCorrectlyAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert StringBuilder contentBuilder = new(); @@ -440,7 +440,7 @@ public async Task CreateResponseStreaming_SpecialCharacters_PreservesFormattingA ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert StringBuilder contentBuilder = new(); @@ -470,7 +470,7 @@ public async Task CreateResponse_SpecialCharacters_PreservesContentAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("Test"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "Test"); // Assert string content = response.GetOutputText(); @@ -492,7 +492,7 @@ public async Task CreateResponseStreaming_ItemIds_AreConsistentAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert List itemIds = []; @@ -530,7 +530,7 @@ public async Task CreateResponse_MultipleSequentialRequests_AllSucceedAsync() // Act & Assert - Make 5 sequential requests for (int i = 0; i < 5; i++) { - ResponseResult response = await responseClient.CreateResponseAsync($"Request {i}"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", $"Request {i}"); Assert.NotNull(response); Assert.Equal(ResponseStatus.Completed, response.Status); Assert.Equal(ExpectedResponse, response.GetOutputText()); @@ -554,7 +554,7 @@ public async Task CreateResponseStreaming_MultipleSequentialRequests_AllStreamCo // Act & Assert - Make 3 sequential streaming requests for (int i = 0; i < 3; i++) { - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync($"Request {i}"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", $"Request {i}"); StringBuilder contentBuilder = new(); await foreach (StreamingResponseUpdate update in streamingResult) @@ -587,7 +587,7 @@ public async Task CreateResponse_MultipleRequests_GenerateUniqueIdsAsync() List responseIds = []; for (int i = 0; i < 10; i++) { - ResponseResult response = await responseClient.CreateResponseAsync($"Request {i}"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", $"Request {i}"); responseIds.Add(response.Id); } @@ -611,7 +611,7 @@ public async Task CreateResponseStreaming_SequenceNumbers_AreMonotonicallyIncrea ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert List sequenceNumbers = []; @@ -644,7 +644,7 @@ public async Task CreateResponse_ModelInformation_IsCorrectAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("Test"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "Test"); // Assert Assert.NotNull(response.Model); @@ -666,7 +666,7 @@ public async Task CreateResponseStreaming_Punctuation_PreservesContentAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert StringBuilder contentBuilder = new(); @@ -696,7 +696,7 @@ public async Task CreateResponse_ShortInput_ReturnsValidResponseAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("Hi"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "Hi"); // Assert Assert.NotNull(response); @@ -719,7 +719,7 @@ public async Task CreateResponseStreaming_ContentIndices_AreConsistentAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert List contentIndices = []; @@ -751,7 +751,7 @@ public async Task CreateResponse_Newlines_PreservesFormattingAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("Test"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "Test"); // Assert string content = response.GetOutputText(); @@ -774,7 +774,7 @@ public async Task CreateResponseStreaming_Newlines_PreservesFormattingAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert StringBuilder contentBuilder = new(); @@ -810,7 +810,7 @@ public async Task CreateResponse_ImageContent_ReturnsCorrectlyAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("Show me an image"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "Show me an image"); // Assert Assert.NotNull(response); @@ -837,7 +837,7 @@ public async Task CreateResponseStreaming_ImageContent_StreamsCorrectlyAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Show me an image"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Show me an image"); // Assert List updates = []; @@ -871,7 +871,7 @@ public async Task CreateResponse_AudioContent_ReturnsCorrectlyAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("Generate audio"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "Generate audio"); // Assert Assert.NotNull(response); @@ -899,7 +899,7 @@ public async Task CreateResponseStreaming_AudioContent_StreamsCorrectlyAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Generate audio"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Generate audio"); // Assert List updates = []; @@ -933,7 +933,7 @@ public async Task CreateResponse_FunctionCall_ReturnsCorrectlyAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("What's the weather?"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "What's the weather?"); // Assert Assert.NotNull(response); @@ -960,7 +960,7 @@ public async Task CreateResponseStreaming_FunctionCall_StreamsCorrectlyAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Calculate 2+2"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Calculate 2+2"); // Assert List updates = []; @@ -991,7 +991,7 @@ public async Task CreateResponse_MixedContent_ReturnsCorrectlyAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - ResponseResult response = await responseClient.CreateResponseAsync("Show me various content"); + ResponseResult response = await responseClient.CreateResponseAsync("test-model", "Show me various content"); // Assert Assert.NotNull(response); @@ -1017,7 +1017,7 @@ public async Task CreateResponseStreaming_MixedContent_StreamsCorrectlyAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Show me various content"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Show me various content"); // Assert List updates = []; @@ -1050,7 +1050,7 @@ public async Task CreateResponseStreaming_TextDone_IncludesDoneEventAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert List updates = []; @@ -1078,7 +1078,7 @@ public async Task CreateResponseStreaming_ContentPartAdded_IncludesEventAsync() ResponsesClient responseClient = this.CreateResponseClient(AgentName); // Act - AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("Test"); + AsyncCollectionResult streamingResult = responseClient.CreateResponseStreamingAsync("test-model", "Test"); // Assert List updates = []; @@ -1273,7 +1273,6 @@ private async Task SendRawResponseAsync( private ResponsesClient CreateResponseClient(string agentName) { return new ResponsesClient( - model: "test-model", credential: new ApiKeyCredential("test-api-key"), options: new OpenAIClientOptions { diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeToolWorkflowTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeToolWorkflowTest.cs index 9d5efa6b6d..bd5b250eb8 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeToolWorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeToolWorkflowTest.cs @@ -123,9 +123,9 @@ private async Task> ProcessFunctionCallsAsync( foreach (ChatMessage message in toolRequest.AgentResponse.Messages) { // Handle approval requests if present - foreach (FunctionApprovalRequestContent approvalRequest in message.Contents.OfType()) + foreach (ToolApprovalRequestContent approvalRequest in message.Contents.OfType()) { - this.Output.WriteLine($"APPROVAL REQUEST: {approvalRequest.FunctionCall.Name}"); + this.Output.WriteLine($"APPROVAL REQUEST: {((FunctionCallContent)approvalRequest.ToolCall).Name}"); // Auto-approve for testing results.Add(approvalRequest.CreateResponse(approved: true)); } @@ -233,12 +233,12 @@ private List ProcessMcpToolRequests( foreach (ChatMessage message in toolRequest.AgentResponse.Messages) { // Handle MCP approval requests if present - foreach (McpServerToolApprovalRequestContent approvalRequest in message.Contents.OfType()) + foreach (ToolApprovalRequestContent approvalRequest in message.Contents.OfType()) { - this.Output.WriteLine($"MCP APPROVAL REQUEST: {approvalRequest.Id}"); + this.Output.WriteLine($"MCP APPROVAL REQUEST: {approvalRequest.RequestId}"); // Respond based on test configuration - McpServerToolApprovalResponseContent response = approvalRequest.CreateResponse(approved: approveRequest); + ToolApprovalResponseContent response = approvalRequest.CreateResponse(approved: approveRequest); results.Add(response); this.Output.WriteLine($"MCP APPROVAL RESPONSE: {(approveRequest ? "Approved" : "Rejected")}"); diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputRequestTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputRequestTest.cs index cebdc60cb9..d732c11099 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputRequestTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputRequestTest.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Collections.Generic; +using System.Linq; using Microsoft.Agents.AI.Workflows.Declarative.Events; using Microsoft.Extensions.AI; @@ -33,8 +35,8 @@ public void VerifySerializationWithRequests() new ChatMessage( ChatRole.Assistant, [ - new McpServerToolApprovalRequestContent("call1", new McpServerToolCallContent("call1", "testmcp", "server-name")), - new FunctionApprovalRequestContent("call2", new FunctionCallContent("call2", "result1")), + new ToolApprovalRequestContent("call1", new McpServerToolCallContent("call1", "testmcp", "server-name")), + new ToolApprovalRequestContent("call2", new FunctionCallContent("call2", "result1")), new FunctionCallContent("call3", "myfunc"), new TextContent("Heya"), ]))); @@ -46,11 +48,14 @@ public void VerifySerializationWithRequests() ChatMessage messageCopy = Assert.Single(source.AgentResponse.Messages); Assert.Equal(messageCopy.Contents.Count, copy.AgentResponse.Messages[0].Contents.Count); - McpServerToolApprovalRequestContent mcpRequest = AssertContent(messageCopy); - Assert.Equal("call1", mcpRequest.Id); + List approvalRequests = messageCopy.Contents.OfType().ToList(); + Assert.Equal(2, approvalRequests.Count); - FunctionApprovalRequestContent functionRequest = AssertContent(messageCopy); - Assert.Equal("call2", functionRequest.Id); + ToolApprovalRequestContent mcpRequest = approvalRequests[0]; + Assert.Equal("call1", mcpRequest.RequestId); + + ToolApprovalRequestContent functionRequest = approvalRequests[1]; + Assert.Equal("call2", functionRequest.RequestId); FunctionCallContent functionCall = AssertContent(messageCopy); Assert.Equal("call3", functionCall.CallId); diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputResponseTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputResponseTest.cs index 384664a68c..853851375e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputResponseTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputResponseTest.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Collections.Generic; +using System.Linq; using Microsoft.Agents.AI.Workflows.Declarative.Events; using Microsoft.Extensions.AI; @@ -32,8 +34,8 @@ public void VerifySerializationWithResponses() new(new ChatMessage( ChatRole.Assistant, [ - new McpServerToolApprovalRequestContent("call1", new McpServerToolCallContent("call1", "testmcp", "server-name")).CreateResponse(approved: true), - new FunctionApprovalRequestContent("call2", new FunctionCallContent("call2", "result1")).CreateResponse(approved: true), + new ToolApprovalRequestContent("call1", new McpServerToolCallContent("call1", "testmcp", "server-name")).CreateResponse(approved: true), + new ToolApprovalRequestContent("call2", new FunctionCallContent("call2", "result1")).CreateResponse(approved: true), new FunctionResultContent("call3", 33), new TextContent("Heya"), ])); @@ -45,11 +47,14 @@ public void VerifySerializationWithResponses() ChatMessage responseMessage = Assert.Single(source.Messages); Assert.Equal(responseMessage.Contents.Count, copy.Messages[0].Contents.Count); - McpServerToolApprovalResponseContent mcpApproval = AssertContent(responseMessage); - Assert.Equal("call1", mcpApproval.Id); + List approvalResponses = responseMessage.Contents.OfType().ToList(); + Assert.Equal(2, approvalResponses.Count); - FunctionApprovalResponseContent functionApproval = AssertContent(responseMessage); - Assert.Equal("call2", functionApproval.Id); + ToolApprovalResponseContent mcpApproval = approvalResponses[0]; + Assert.Equal("call1", mcpApproval.RequestId); + + ToolApprovalResponseContent functionApproval = approvalResponses[1]; + Assert.Equal("call2", functionApproval.RequestId); FunctionResultContent functionResult = AssertContent(responseMessage); Assert.Equal("call3", functionResult.CallId); diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeMcpToolExecutorTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeMcpToolExecutorTest.cs index 45b0b3c7b7..a1337b3e2d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeMcpToolExecutorTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeMcpToolExecutorTest.cs @@ -473,8 +473,8 @@ public async Task InvokeMcpToolCaptureResponseWithApprovalApprovedAsync() // Create approval request then response McpServerToolCallContent toolCall = new(action.Id, TestToolName, TestServerUrl); - McpServerToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); - McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); + ToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); + ToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); // Act @@ -501,8 +501,8 @@ public async Task InvokeMcpToolCaptureResponseWithApprovalRejectedAsync() // Create approval request then response (rejected) McpServerToolCallContent toolCall = new(action.Id, TestToolName, TestServerUrl); - McpServerToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); - McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: false); + ToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); + ToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: false); ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); // Act @@ -552,8 +552,8 @@ public async Task InvokeMcpToolCaptureResponseWithNonMatchingApprovalIdAsync() // Create approval with different ID McpServerToolCallContent toolCall = new("different_id", TestToolName, TestServerUrl); - McpServerToolApprovalRequestContent approvalRequest = new("different_id", toolCall); - McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); + ToolApprovalRequestContent approvalRequest = new("different_id", toolCall); + ToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); // Act @@ -582,8 +582,8 @@ public async Task InvokeMcpToolCaptureResponseWithApprovedAndArgumentsAsync() // Create approval request then response McpServerToolCallContent toolCall = new(action.Id, TestToolName, TestServerUrl); - McpServerToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); - McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); + ToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); + ToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); // Act @@ -613,8 +613,8 @@ public async Task InvokeMcpToolCaptureResponseWithApprovedAndHeadersAsync() // Create approval request then response McpServerToolCallContent toolCall = new(action.Id, TestToolName, TestServerLabel); - McpServerToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); - McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); + ToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); + ToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); // Act @@ -643,8 +643,8 @@ public async Task InvokeMcpToolCaptureResponseWithApprovedAndConversationIdAsync // Create approval request then response McpServerToolCallContent toolCall = new(action.Id, TestToolName, TestServerUrl); - McpServerToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); - McpServerToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); + ToolApprovalRequestContent approvalRequest = new(action.Id, toolCall); + ToolApprovalResponseContent approvalResponse = approvalRequest.CreateResponse(approved: true); ExternalInputResponse response = new(new ChatMessage(ChatRole.User, [approvalResponse])); // Act @@ -799,31 +799,31 @@ public MockMcpToolProvider( if (returnNullOutput) { - result.Output = null; + result.Outputs = null; } else if (returnEmptyOutput) { - result.Output = []; + result.Outputs = []; } else if (returnJsonObject) { - result.Output = [new TextContent("{\"key\": \"value\", \"number\": 42}")]; + result.Outputs = [new TextContent("{\"key\": \"value\", \"number\": 42}")]; } else if (returnJsonArray) { - result.Output = [new TextContent("[1, 2, 3, \"four\"]")]; + result.Outputs = [new TextContent("[1, 2, 3, \"four\"]")]; } else if (returnInvalidJson) { - result.Output = [new TextContent("this is not valid json {")]; + result.Outputs = [new TextContent("this is not valid json {")]; } else if (returnDataContent) { - result.Output = [new DataContent("data:image/png;base64,iVBORw0KGgo=", "image/png")]; + result.Outputs = [new DataContent("data:image/png;base64,iVBORw0KGgo=", "image/png")]; } else if (returnMultipleContent) { - result.Output = + result.Outputs = [ new TextContent("First text"), new TextContent("{\"nested\": true}"), @@ -832,7 +832,7 @@ public MockMcpToolProvider( } else { - result.Output = [new TextContent("Mock MCP tool result")]; + result.Outputs = [new TextContent("Mock MCP tool result")]; } return Task.FromResult(result); diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AIAgentHostExecutorTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AIAgentHostExecutorTests.cs index 2ea117856f..063bd77cda 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AIAgentHostExecutorTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AIAgentHostExecutorTests.cs @@ -229,7 +229,7 @@ public async Task Test_AgentHostExecutor_InterceptsRequestsIFFConfiguredAsync(bo responses = ExtractAndValidateRequestContents(); break; case TestAgentRequestType.UserInputRequest: - responses = ExtractAndValidateRequestContents(); + responses = ExtractAndValidateRequestContents(); break; default: throw new NotSupportedException(); diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestRequestAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestRequestAgent.cs index 4faeff29a1..5547b2a539 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestRequestAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestRequestAgent.cs @@ -33,7 +33,7 @@ protected override ValueTask CreateSessionCoreAsync(CancellationTo => new(requestType switch { TestAgentRequestType.FunctionCall => new TestRequestAgentSession(), - TestAgentRequestType.UserInputRequest => new TestRequestAgentSession(), + TestAgentRequestType.UserInputRequest => new TestRequestAgentSession(), _ => throw new NotSupportedException(), }); @@ -41,7 +41,7 @@ protected override ValueTask DeserializeSessionCoreAsync(JsonEleme => new(requestType switch { TestAgentRequestType.FunctionCall => new TestRequestAgentSession(), - TestAgentRequestType.UserInputRequest => new TestRequestAgentSession(), + TestAgentRequestType.UserInputRequest => new TestRequestAgentSession(), _ => throw new NotSupportedException(), }); @@ -179,58 +179,58 @@ public void ProcessResponse(FunctionResultContent response, TestRequestAgentSess } } - private sealed class FunctionApprovalStrategy : IRequestResponseStrategy + private sealed class FunctionApprovalStrategy : IRequestResponseStrategy { - public UserInputResponseContent CreatePairedResponse(UserInputRequestContent request) + public ToolApprovalResponseContent CreatePairedResponse(ToolApprovalRequestContent request) { - if (request is not FunctionApprovalRequestContent approvalRequest) + if (request is not ToolApprovalRequestContent approvalRequest) { - throw new InvalidOperationException($"Invalid request: Expecting {typeof(FunctionApprovalResponseContent)}, got {request.GetType()}"); + throw new InvalidOperationException($"Invalid request: Expecting {typeof(ToolApprovalResponseContent)}, got {request.GetType()}"); } - return new FunctionApprovalResponseContent(approvalRequest.Id, true, approvalRequest.FunctionCall); + return new ToolApprovalResponseContent(approvalRequest.RequestId, true, approvalRequest.ToolCall); } - public IEnumerable<(string, UserInputRequestContent)> CreateRequests(int count) + public IEnumerable<(string, ToolApprovalRequestContent)> CreateRequests(int count) { for (int i = 0; i < count; i++) { string id = Guid.NewGuid().ToString("N"); - UserInputRequestContent request = new FunctionApprovalRequestContent(id, new(id, "TestFunction")); + ToolApprovalRequestContent request = new ToolApprovalRequestContent(id, new FunctionCallContent(id, "TestFunction")); yield return (id, request); } } - public void ProcessResponse(UserInputResponseContent response, TestRequestAgentSession session) + public void ProcessResponse(ToolApprovalResponseContent response, TestRequestAgentSession session) { - if (session.UnservicedRequests.TryGetValue(response.Id, out UserInputRequestContent? request)) + if (session.UnservicedRequests.TryGetValue(response.RequestId, out ToolApprovalRequestContent? request)) { - if (request is not FunctionApprovalRequestContent approvalRequest) + if (request is not ToolApprovalRequestContent approvalRequest) { - throw new InvalidOperationException($"Invalid request: Expecting {typeof(FunctionApprovalResponseContent)}, got {request.GetType()}"); + throw new InvalidOperationException($"Invalid request: Expecting {typeof(ToolApprovalResponseContent)}, got {request.GetType()}"); } - if (response is not FunctionApprovalResponseContent approvalResponse) + if (response is not ToolApprovalResponseContent approvalResponse) { - throw new InvalidOperationException($"Invalid response: Expecting {typeof(FunctionApprovalResponseContent)}, got {response.GetType()}"); + throw new InvalidOperationException($"Invalid response: Expecting {typeof(ToolApprovalResponseContent)}, got {response.GetType()}"); } approvalResponse.Approved.Should().BeTrue(); - approvalResponse.FunctionCall.As().Should().Be(approvalRequest.FunctionCall); - session.ServicedRequests.Add(response.Id); - session.UnservicedRequests.Remove(response.Id); + ((FunctionCallContent)approvalResponse.ToolCall).Should().Be((FunctionCallContent)approvalRequest.ToolCall); + session.ServicedRequests.Add(response.RequestId); + session.UnservicedRequests.Remove(response.RequestId); } - else if (session.ServicedRequests.Contains(response.Id)) + else if (session.ServicedRequests.Contains(response.RequestId)) { - throw new InvalidOperationException($"Seeing duplicate response with id {response.Id}"); + throw new InvalidOperationException($"Seeing duplicate response with id {response.RequestId}"); } - else if (session.PairedRequests.Contains(response.Id)) + else if (session.PairedRequests.Contains(response.RequestId)) { - throw new InvalidOperationException($"Seeing explicit response to initially paired request with id {response.Id}"); + throw new InvalidOperationException($"Seeing explicit response to initially paired request with id {response.RequestId}"); } else { - throw new InvalidOperationException($"Seeing response to nonexistent request with id {response.Id}"); + throw new InvalidOperationException($"Seeing response to nonexistent request with id {response.RequestId}"); } } } @@ -261,7 +261,7 @@ private static string RetrieveId(TRequest request) return request switch { FunctionCallContent functionCall => functionCall.CallId, - UserInputRequestContent userInputRequest => userInputRequest.Id, + ToolApprovalRequestContent userInputRequest => userInputRequest.RequestId, _ => throw new NotSupportedException($"Unknown request type {typeof(TRequest)}"), }; } @@ -295,12 +295,12 @@ internal IEnumerable ValidateUnpairedRequests(IEnumerable)requests, new FunctionCallStrategy()); case TestAgentRequestType.UserInputRequest: - if (!typeof(UserInputRequestContent).IsAssignableFrom(typeof(TRequest))) + if (!typeof(ToolApprovalRequestContent).IsAssignableFrom(typeof(TRequest))) { - throw new ArgumentException($"Invalid request type: Expected {typeof(UserInputRequestContent)}, got {typeof(TRequest)}", nameof(requests)); + throw new ArgumentException($"Invalid request type: Expected {typeof(ToolApprovalRequestContent)}, got {typeof(TRequest)}", nameof(requests)); } - return this.ValidateUnpairedRequests((IEnumerable)requests, new FunctionApprovalStrategy()); + return this.ValidateUnpairedRequests((IEnumerable)requests, new FunctionApprovalStrategy()); default: throw new NotSupportedException($"Unknown AgentRequestType {requestType}"); } @@ -315,7 +315,7 @@ internal IEnumerable ValidateUnpairedRequests(List)).ToList(); break; case TestAgentRequestType.UserInputRequest: - responses = this.ValidateUnpairedRequests(requests.Select(AssertAndExtractRequestContent)).ToList(); + responses = this.ValidateUnpairedRequests(requests.Select(AssertAndExtractRequestContent)).ToList(); break; default: throw new NotSupportedException($"Unknown AgentRequestType {requestType}"); diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs index 74c7ef9041..20f79d0ad1 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs @@ -17,6 +17,7 @@ namespace ResponseResult.IntegrationTests; public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture { private ResponsesClient _openAIResponseClient = null!; + private string _modelName = null!; private ChatClientAgent _agent = null!; public AIAgent Agent => this._agent; @@ -74,7 +75,7 @@ public async Task CreateChatClientAgentAsync( string instructions = "You are a helpful assistant.", IList? aiTools = null) => new( - this._openAIResponseClient.AsIChatClient(), + this._openAIResponseClient.AsIChatClient(this._modelName), options: new() { Name = name, @@ -96,8 +97,9 @@ public Task DeleteSessionAsync(AgentSession session) => public async ValueTask InitializeAsync() { + this._modelName = TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName); this._openAIResponseClient = new OpenAIClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIApiKey)) - .GetResponsesClient(TestConfiguration.GetRequiredValue(TestSettings.OpenAIChatModelName)); + .GetResponsesClient(); this._agent = await this.CreateChatClientAgentAsync(); }