From 88ea9d08c7575ce9165c4a2e4ee0086bc77567a8 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 14:29:29 +0000 Subject: [PATCH] .NET: Update to OpenAI 2.9.1, Azure.AI.OpenAI 2.9.0-beta.1, Microsoft.Extensions.AI 10.4.0, and Azure.AI.Projects 2.0.0-beta.2 (#4613) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial plan * Update code for Microsoft.Extensions.AI.Abstractions 10.4.0 breaking changes - Rename FunctionApprovalRequestContent → ToolApprovalRequestContent - Rename FunctionApprovalResponseContent → ToolApprovalResponseContent - Rename UserInputRequestContent → ToolApprovalRequestContent - Rename UserInputResponseContent → ToolApprovalResponseContent - Update .FunctionCall property → .ToolCall with FunctionCallContent casts where needed - Update .Id property → .RequestId on the renamed types - Rename FunctionApprovalRequestEventGenerator → ToolApprovalRequestEventGenerator - Rename FunctionApprovalResponseEventGenerator → ToolApprovalResponseEventGenerator Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Update OpenAI 2.9.1, ME.AI 10.4.0, fix breaking API changes Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Fix remaining ME.AI 10.4.0 breaking changes: MCP approval types, .Output→.Outputs Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Use pattern matching with `when` for ToolApprovalRequestContent/FunctionCallContent Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Update Azure.AI.OpenAI to 2.9.0-beta.1 Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Fix remaining GetResponsesClient(model) build failures for Azure.AI.OpenAI 2.9.0-beta.1 Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Address review feedback: remove redundant type checks in TestRequestAgent.cs and fix error message in AIAgentHostExecutor.cs Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> * Update Azure.AI.Projects to 2.0.0-beta.2 with namespace migration - Azure.AI.Projects 2.0.0-beta.1 → 2.0.0-beta.2 - Azure.AI.Projects.OpenAI → Azure.AI.Extensions.OpenAI (transitive) - Agent types moved to Azure.AI.Projects.Agents namespace - AgentRecord.Versions.Latest → AgentRecord.GetLatestVersion() - OpenAPIFunctionDefinition → OpenApiFunctionDefinition - BingCustomSearchToolParameters → BingCustomSearchToolOptions - MemorySearchPreviewTool.UpdateDelay → UpdateDelayInSecs - Azure.Identity 1.17.1 → 1.19.0 - Microsoft.Identity.Client.Extensions.Msal 4.78.0 → 4.83.1 Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Fix remaining type renames for Azure.AI.Projects 2.0.0-beta.2 - BrowserAutomationToolParameters → BrowserAutomationToolOptions - MemoryUpdateOptions.UpdateDelay stays as UpdateDelay (not renamed) - WaitForMemoriesUpdateAsync parameter order: pollingInterval before options - AIProjectAgentsOperations → AgentsClient Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Fix format errors and OpenTelemetry test for ME.AI 10.4.0 - Remove unused 'using Azure.AI.Extensions.OpenAI' and fix import ordering in Agent_With_AzureAIProject/Program.cs - Update OpenTelemetryAgentTests: gen_ai.tool.definitions is now always emitted regardless of EnableSensitiveData per ME.AI 10.4.0 change (dotnet/extensions#7346). Tool definitions are not considered sensitive. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix GetRepoFolder() to work in git worktrees Use 'workflow-samples' directory as repo root marker instead of '.git', which fails in worktrees (.git is a file) and also matches too early when a '.github' folder exists in subdirectories. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix formatting: remove unused usings and fix import ordering dotnet format applied across 59 impacted projects. Primarily removes unnecessary 'using Azure.AI.Projects' where Azure.AI.Projects.Agents provides all needed types, and fixes import ordering per editorconfig. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Disable AzureAIAgentsPersistent integration tests (#4769) Azure.AI.Agents.Persistent 1.2.0-beta.9 references McpServerToolApprovalResponseContent which was removed in ME.AI 10.4.0 (renamed to ToolApprovalResponseContent), causing TypeLoadException at runtime. Mark all 6 test classes with IntegrationDisabled trait until Persistent ships a version targeting ME.AI 10.4.0+. Upstream fix: https://github.com/Azure/azure-sdk-for-net/pull/56929 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add README with compatibility note for AzureAI.Persistent (#4769) Documents that Azure.AI.Agents.Persistent 1.2.0-beta.9 is only compatible with ME.AI ≤10.3.0 and OpenAI ≤2.8.0 due to type renames in ME.AI 10.4.0. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix file encoding: restore UTF-8 BOM on Persistent test files Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Mark AzureAI.Persistent as IsPackable=false (#4769) Prevent shipping until Azure.AI.Agents.Persistent targets ME.AI 10.4.0+. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Moving IsPackable after import --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com> Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> --- dotnet/Directory.Packages.props | 35 +++++---- .../AGUI/Step04_HumanInLoop/Client/Program.cs | 16 ++-- .../ServerFunctionApprovalClientAgent.cs | 24 +++--- .../ServerFunctionApprovalServerAgent.cs | 17 ++--- .../Agent_With_AzureAIProject/Program.cs | 2 +- .../Program.cs | 8 +- .../Agent_With_OpenAIResponses/Program.cs | 4 +- .../Agent_Step01_BasicSkills/Program.cs | 5 +- .../Agent_OpenAI_Step02_Reasoning/Program.cs | 4 +- .../OpenAIResponseClientAgent.cs | 9 ++- .../Program.cs | 4 +- .../Program.cs | 2 +- .../Program.cs | 10 +-- .../Program.cs | 3 +- .../Agents/Agent_Step11_Middleware/Program.cs | 6 +- .../Program.cs | 4 +- .../FoundryAgents_Step01.1_Basics/Program.cs | 2 +- .../FoundryAgents_Step01.2_Running/Program.cs | 2 +- .../Program.cs | 3 +- .../Program.cs | 6 +- .../Program.cs | 6 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../Program.cs | 4 +- .../Program.cs | 4 +- .../Program.cs | 2 +- .../Program.cs | 2 +- .../FoundryAgents_Step21_WebSearch/Program.cs | 2 +- .../FoundryAgents_Step22_MemorySearch.csproj | 1 - .../Program.cs | 9 ++- .../FoundryAgent_Hosted_MCP/Program.cs | 11 +-- .../ResponseAgent_Hosted_MCP/Program.cs | 17 +++-- .../Agents/GroupChatToolApproval/Program.cs | 10 +-- .../Declarative/CustomerSupport/Program.cs | 2 +- .../Declarative/DeepResearch/Program.cs | 4 +- .../Declarative/FunctionTools/Program.cs | 2 +- .../Declarative/HostedWorkflow/Program.cs | 3 +- .../Declarative/InputArguments/Program.cs | 2 +- .../Declarative/InvokeFunctionTool/Program.cs | 2 +- .../Declarative/InvokeMcpTool/Program.cs | 2 +- .../Declarative/Marketing/Program.cs | 2 +- .../Declarative/StudentTeacher/Program.cs | 2 +- .../Declarative/ToolApproval/Program.cs | 2 +- .../OpenAIResponsesAgentClient.cs | 2 +- .../05-end-to-end/AgentWithPurview/Program.cs | 4 +- .../AgentThreadAndHITL.csproj | 2 +- .../AgentWithHostedMCP.csproj | 3 +- .../AgentWithHostedMCP/Program.cs | 3 +- .../AgentWithLocalTools.csproj | 2 +- .../AgentWithTextSearchRag.csproj | 2 +- .../AgentWithTools/AgentWithTools.csproj | 2 +- .../AgentsInWorkflows.csproj | 2 +- .../M365Agent/AFAgentApplication.cs | 6 +- ...rosoft.Agents.AI.AzureAI.Persistent.csproj | 2 + .../README.md | 19 +++++ .../AzureAIProjectChatClient.cs | 5 +- .../AzureAIProjectChatClientExtensions.cs | 13 ++-- .../Microsoft.Agents.AI.AzureAI.csproj | 1 - .../AgentResponseUpdateExtensions.cs | 4 +- .../FunctionApprovalRequestEventGenerator.cs | 20 +++-- .../FunctionApprovalResponseEventGenerator.cs | 10 +-- ...ingChatCompletionUpdateCollectionResult.cs | 2 + .../OpenAIResponseClientExtensions.cs | 12 ++- .../AzureAgentProvider.cs | 5 +- .../DefaultMcpToolHandler.cs | 8 +- .../ObjectModel/InvokeAzureAgentExecutor.cs | 2 +- .../ObjectModel/InvokeFunctionToolExecutor.cs | 2 +- .../ObjectModel/InvokeMcpToolExecutor.cs | 17 ++--- .../AIAgentHostOptions.cs | 2 +- .../Specialized/AIAgentHostExecutor.cs | 20 ++--- .../src/Shared/Foundry/Agents/AgentFactory.cs | 3 +- .../Workflows/Execution/WorkflowRunner.cs | 21 +++--- .../AIProjectClientCreateTests.cs | 6 +- .../AIProjectClientFixture.cs | 2 +- ...IAgentsChatClientAgentRunStreamingTests.cs | 4 + .../AzureAIAgentsChatClientAgentRunTests.cs | 4 + .../AzureAIAgentsPersistentCreateTests.cs | 4 + ...zureAIAgentsPersistentRunStreamingTests.cs | 4 + .../AzureAIAgentsPersistentRunTests.cs | 4 + ...gentsPersistentStructuredOutputRunTests.cs | 4 + ...AzureAIProjectChatClientExtensionsTests.cs | 19 ++--- .../TestDataUtil.cs | 2 +- .../FunctionApprovalTests.cs | 24 +++--- .../OpenAIResponsesIntegrationTests.cs | 75 +++++++++---------- .../OpenTelemetryAgentTests.cs | 42 ++++++++++- .../Agents/AgentProvider.cs | 2 +- .../Agents/FunctionToolAgentProvider.cs | 2 +- .../Agents/MarketingAgentProvider.cs | 2 +- .../Agents/MathChatAgentProvider.cs | 2 +- .../Agents/PoemAgentProvider.cs | 2 +- .../Agents/TestAgentProvider.cs | 2 +- .../Agents/VisionAgentProvider.cs | 2 +- .../Framework/WorkflowTest.cs | 2 +- .../InvokeToolWorkflowTest.cs | 10 +-- .../Events/ExternalInputRequestTest.cs | 17 +++-- .../Events/ExternalInputResponseTest.cs | 17 +++-- .../ObjectModel/InvokeMcpToolExecutorTest.cs | 40 +++++----- .../AIAgentHostExecutorTests.cs | 2 +- .../TestRequestAgent.cs | 61 ++++++--------- .../OpenAIResponseFixture.cs | 6 +- 101 files changed, 464 insertions(+), 358 deletions(-) create mode 100644 dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/README.md diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index dc840265d6..c5bd2191c4 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -19,11 +19,10 @@ - - - - - + + + + @@ -40,12 +39,12 @@ - + - - - + + + @@ -64,12 +63,12 @@ - - - - + + + + - + @@ -77,11 +76,11 @@ - + - + @@ -111,9 +110,9 @@ - + - + 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 @@ while ((input = Console.ReadLine()) != null && !input.Equals("exit", StringCompa { 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 @@ while ((input = Console.ReadLine()) != null && !input.Equals("exit", StringCompa } #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 @@ using ServerFunctionApproval; /// /// 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 @@ internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent } #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 @@ internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent { List? result = null; - Dictionary approvalRequests = []; + Dictionary approvalRequests = []; for (var messageIndex = 0; messageIndex < messages.Count; messageIndex++) { var message = messages[messageIndex]; @@ -102,21 +102,21 @@ internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent 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 @@ internal sealed class ServerFunctionApprovalClientAgent : DelegatingAIAgent 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 @@ using ServerFunctionApproval; /// /// 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 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent } #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 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent 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 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent // 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 @@ internal sealed class ServerFunctionApprovalAgent : DelegatingAIAgent { 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_AzureAIProject/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs index acdd0829ab..aab95d5b38 100644 --- a/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureAIProject/Program.cs @@ -3,7 +3,7 @@ // This sample shows how to create and use a AI agents with Azure Foundry Agents as the backend. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; diff --git a/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIResponses/Program.cs b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIResponses/Program.cs index 6aca7f24b8..f29b850700 100644 --- a/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIResponses/Program.cs +++ b/dotnet/samples/02-agents/AgentProviders/Agent_With_AzureOpenAIResponses/Program.cs @@ -17,8 +17,8 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT AIAgent agent = new AzureOpenAIClient( new Uri(endpoint), new DefaultAzureCredential()) - .GetResponsesClient(deploymentName) - .AsAIAgent(instructions: "You are good at telling jokes.", name: "Joker"); + .GetResponsesClient() + .AsAIAgent(model: deploymentName, 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.")); @@ -29,8 +29,8 @@ Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.")); AIAgent agentStoreFalse = new AzureOpenAIClient( new Uri(endpoint), new DefaultAzureCredential()) - .GetResponsesClient(deploymentName) - .AsIChatClientWithStoredOutputDisabled() + .GetResponsesClient() + .AsIChatClientWithStoredOutputDisabled(model: deploymentName) .AsAIAgent(instructions: "You are good at telling jokes.", name: "Joker"); // Invoke the agent and output the text result. 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 @@ var model = Environment.GetEnvironmentVariable("OPENAI_CHAT_MODEL_NAME") ?? "gpt 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/AgentSkills/Agent_Step01_BasicSkills/Program.cs b/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Program.cs index 290c3f9b6b..9b0a4b4f99 100644 --- a/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Program.cs +++ b/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Program.cs @@ -23,7 +23,7 @@ var skillsProvider = new FileAgentSkillsProvider(skillPath: Path.Combine(AppCont // --- Agent Setup --- AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential()) - .GetResponsesClient(deploymentName) + .GetResponsesClient() .AsAIAgent(new ChatClientAgentOptions { Name = "SkillsAgent", @@ -32,7 +32,8 @@ AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredent Instructions = "You are a helpful assistant.", }, AIContextProviders = [skillsProvider], - }); + }, + model: deploymentName); // --- Example 1: Expense policy question (loads FAQ resource) --- Console.WriteLine("Example 1: Checking expense policy FAQ"); 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 apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new I 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 class OpenAIResponseClientAgent : DelegatingAIAgent /// /// 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 apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY") ?? throw new I 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 @@ OpenAIClient openAIClient = new(apiKey); 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 @@ AIAgent agent = new AzureOpenAIClient( // 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 @@ while (approvalRequests.Count > 0) 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_Step10_BackgroundResponsesWithToolsAndPersistence/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence/Program.cs index 5d9c70a5fd..b568ef5867 100644 --- a/dotnet/samples/02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence/Program.cs +++ b/dotnet/samples/02-agents/Agents/Agent_Step10_BackgroundResponsesWithToolsAndPersistence/Program.cs @@ -25,8 +25,9 @@ var stateStore = new Dictionary(); AIAgent agent = new AzureOpenAIClient( new Uri(endpoint), new DefaultAzureCredential()) - .GetResponsesClient(deploymentName) + .GetResponsesClient() .AsAIAgent( + model: deploymentName, name: "SpaceNovelWriter", instructions: "You are a space novel writer. Always research relevant facts and generate character profiles for the main characters before writing novels." + "Write complete chapters without asking for approval or feedback. Do not ask the user about tone, style, pace, or format preferences - just write the novel based on the request.", 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/Agents/Agent_Step14_BackgroundResponses/Program.cs b/dotnet/samples/02-agents/Agents/Agent_Step14_BackgroundResponses/Program.cs index 62db550556..f474b938a6 100644 --- a/dotnet/samples/02-agents/Agents/Agent_Step14_BackgroundResponses/Program.cs +++ b/dotnet/samples/02-agents/Agents/Agent_Step14_BackgroundResponses/Program.cs @@ -16,8 +16,8 @@ var deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT AIAgent agent = new AzureOpenAIClient( new Uri(endpoint), new DefaultAzureCredential()) - .GetResponsesClient(deploymentName) - .AsAIAgent(); + .GetResponsesClient() + .AsAIAgent(model: deploymentName); // Enable background responses (only supported by OpenAI Responses at this time). AgentRunOptions options = new() { AllowBackgroundResponses = true }; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs index 72676bed45..f4521d8898 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.1_Basics/Program.cs @@ -3,7 +3,7 @@ // This sample shows how to create and use AI agents with Azure Foundry Agents as the backend. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs index dd5db03b15..0bc17aff0a 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step01.2_Running/Program.cs @@ -3,7 +3,7 @@ // This sample shows how to create and use a simple AI agent with Azure Foundry Agents as the backend. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs index 1ac51c30ad..7bf12094fc 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step02_MultiturnConversation/Program.cs @@ -2,8 +2,9 @@ // This sample shows how to create and use a simple AI agent with a multi-turn conversation. +using Azure.AI.Extensions.OpenAI; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; 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 @@ AgentResponse response = await agent.RunAsync("What is the weather like in Amste // 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 @@ while (approvalRequests.Count > 0) 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 @@ while (approvalRequests.Count > 0) // 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/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs index 854d317495..5a27daed12 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step14_CodeInterpreter/Program.cs @@ -4,7 +4,7 @@ using System.Text; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs index 1c5510218a..7f6382d085 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step15_ComputerUse/Program.cs @@ -3,7 +3,7 @@ // This sample shows how to use Computer Use Tool with AI Agents. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/Program.cs index 36f28c2387..5371903a9f 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step16_FileSearch/Program.cs @@ -3,7 +3,7 @@ // This sample shows how to use File Search Tool with AI Agents. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/Program.cs index 2ee5a94458..ebf66e6c2c 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step17_OpenAPITools/Program.cs @@ -3,7 +3,7 @@ // This sample shows how to use OpenAPI Tools with AI Agents. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; using OpenAI.Responses; @@ -72,7 +72,7 @@ const string CountriesOpenApiSpec = """ AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential()); // Create the OpenAPI function definition -var openApiFunction = new OpenAPIFunctionDefinition( +var openApiFunction = new OpenApiFunctionDefinition( "get_countries", BinaryData.FromString(CountriesOpenApiSpec), new OpenAPIAnonymousAuthenticationDetails()) diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/Program.cs index 365bf6ed08..98ea576226 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step18_BingCustomSearch/Program.cs @@ -3,7 +3,7 @@ // This sample shows how to use Bing Custom Search Tool with AI Agents. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; using OpenAI.Responses; @@ -25,7 +25,7 @@ const string AgentInstructions = """ AIProjectClient aiProjectClient = new(new Uri(endpoint), new DefaultAzureCredential()); // Bing Custom Search tool parameters shared by both options -BingCustomSearchToolParameters bingCustomSearchToolParameters = new([ +BingCustomSearchToolOptions bingCustomSearchToolParameters = new([ new BingCustomSearchConfiguration(connectionId, instanceName) ]); diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs index 6d1daf85df..ad6a08abaa 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step19_SharePoint/Program.cs @@ -3,7 +3,7 @@ // This sample shows how to use SharePoint Grounding Tool with AI Agents. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; using OpenAI.Responses; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs index 2f13c2c30c..e5ab205f68 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step20_MicrosoftFabric/Program.cs @@ -3,7 +3,7 @@ // This sample shows how to use Microsoft Fabric Tool with AI Agents. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; using OpenAI.Responses; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/Program.cs index 1ac312ddae..c116a975e1 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step21_WebSearch/Program.cs @@ -3,7 +3,7 @@ // This sample shows how to use the Responses API Web Search Tool with AI Agents. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj index a1ccdfcd3a..d83a9d9202 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/FoundryAgents_Step22_MemorySearch.csproj @@ -13,7 +13,6 @@ - diff --git a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs index 1f6b0f2ddc..60452b7d19 100644 --- a/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs +++ b/dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step22_MemorySearch/Program.cs @@ -4,8 +4,9 @@ // The Memory Search Tool enables agents to recall information from previous conversations, // supporting user profile persistence and chat summaries across sessions. +using Azure.AI.Extensions.OpenAI; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; using OpenAI.Responses; @@ -36,7 +37,7 @@ AIProjectClient aiProjectClient = new(new Uri(endpoint), credential); await EnsureMemoryStoreAsync(); // Create the Memory Search tool configuration -MemorySearchPreviewTool memorySearchTool = new(memoryStoreName, userScope) { UpdateDelay = 0 }; +MemorySearchPreviewTool memorySearchTool = new(memoryStoreName, userScope) { UpdateDelayInSecs = 0 }; // Create agent using Option 1 (MEAI) or Option 2 (Native SDK) AIAgent agent = await CreateAgentWithMEAI(); @@ -128,8 +129,8 @@ async Task EnsureMemoryStoreAsync() MemoryUpdateResult updateResult = await aiProjectClient.MemoryStores.WaitForMemoriesUpdateAsync( memoryStoreName: memoryStoreName, - options: memoryOptions, - pollingInterval: 500); + pollingInterval: 500, + options: memoryOptions); if (updateResult.Status == MemoryStoreUpdateStatus.Failed) { 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 @@ AIAgent agentWithRequiredApproval = await persistentAgentsClient.CreateAIAgentAs // 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 @@ while (approvalRequests.Count > 0) 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 @@ while (approvalRequests.Count > 0) // 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..f8715e4543 100644 --- a/dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs +++ b/dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs @@ -33,8 +33,9 @@ var mcpTool = new HostedMcpServerTool( AIAgent agent = new AzureOpenAIClient( new Uri(endpoint), new DefaultAzureCredential()) - .GetResponsesClient(deploymentName) + .GetResponsesClient() .AsAIAgent( + model: deploymentName, instructions: "You answer questions by searching the Microsoft Learn content only.", name: "MicrosoftLearnAgent", tools: [mcpTool]); @@ -60,8 +61,9 @@ var mcpToolWithApproval = new HostedMcpServerTool( AIAgent agentWithRequiredApproval = new AzureOpenAIClient( new Uri(endpoint), new DefaultAzureCredential()) - .GetResponsesClient(deploymentName) + .GetResponsesClient() .AsAIAgent( + model: deploymentName, instructions: "You answer questions by searching the Microsoft Learn content only.", name: "MicrosoftLearnAgentWithApproval", tools: [mcpToolWithApproval]); @@ -70,7 +72,7 @@ AIAgent agentWithRequiredApproval = new AzureOpenAIClient( // 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 +80,12 @@ while (approvalRequests.Count > 0) 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 +93,7 @@ while (approvalRequests.Count > 0) // 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 @@ public static class Program { 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/03-workflows/Declarative/CustomerSupport/Program.cs b/dotnet/samples/03-workflows/Declarative/CustomerSupport/Program.cs index b5df45a399..5b0458f23d 100644 --- a/dotnet/samples/03-workflows/Declarative/CustomerSupport/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/CustomerSupport/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Extensions.AI; using Microsoft.Extensions.Configuration; diff --git a/dotnet/samples/03-workflows/Declarative/DeepResearch/Program.cs b/dotnet/samples/03-workflows/Declarative/DeepResearch/Program.cs index 98d75d250b..e415c7aad0 100644 --- a/dotnet/samples/03-workflows/Declarative/DeepResearch/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/DeepResearch/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Extensions.Configuration; using OpenAI.Responses; @@ -275,7 +275,7 @@ internal sealed class Program Tools = { AgentTool.CreateOpenApiTool( - new OpenAPIFunctionDefinition( + new OpenApiFunctionDefinition( "weather-forecast", BinaryData.FromString(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "wttr.json"))), new OpenAPIAnonymousAuthenticationDetails())) diff --git a/dotnet/samples/03-workflows/Declarative/FunctionTools/Program.cs b/dotnet/samples/03-workflows/Declarative/FunctionTools/Program.cs index 8218e7c057..a1bd9de8f9 100644 --- a/dotnet/samples/03-workflows/Declarative/FunctionTools/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/FunctionTools/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Extensions.AI; using Microsoft.Extensions.Configuration; diff --git a/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs index 81e2abbafe..5936aaf82f 100644 --- a/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/HostedWorkflow/Program.cs @@ -3,8 +3,9 @@ // Uncomment this to enable JSON checkpointing to the local file system. //#define CHECKPOINT_JSON +using Azure.AI.Extensions.OpenAI; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; diff --git a/dotnet/samples/03-workflows/Declarative/InputArguments/Program.cs b/dotnet/samples/03-workflows/Declarative/InputArguments/Program.cs index 65a365b143..0a6f99f920 100644 --- a/dotnet/samples/03-workflows/Declarative/InputArguments/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/InputArguments/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Extensions.Configuration; using OpenAI.Responses; diff --git a/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/Program.cs b/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/Program.cs index fb20764977..8875d204f2 100644 --- a/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/InvokeFunctionTool/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Extensions.AI; using Microsoft.Extensions.Configuration; diff --git a/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/Program.cs b/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/Program.cs index 926afcfc3c..61ce1afc70 100644 --- a/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/InvokeMcpTool/Program.cs @@ -5,7 +5,7 @@ // invoked to perform specific tasks, like searching documentation or executing operations. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Core; using Azure.Identity; using Microsoft.Agents.AI.Workflows.Declarative.Mcp; diff --git a/dotnet/samples/03-workflows/Declarative/Marketing/Program.cs b/dotnet/samples/03-workflows/Declarative/Marketing/Program.cs index 308303c162..5d73edd26d 100644 --- a/dotnet/samples/03-workflows/Declarative/Marketing/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/Marketing/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Extensions.Configuration; using Shared.Foundry; diff --git a/dotnet/samples/03-workflows/Declarative/StudentTeacher/Program.cs b/dotnet/samples/03-workflows/Declarative/StudentTeacher/Program.cs index 28523c031e..4f1d31a2ea 100644 --- a/dotnet/samples/03-workflows/Declarative/StudentTeacher/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/StudentTeacher/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Extensions.Configuration; using Shared.Foundry; diff --git a/dotnet/samples/03-workflows/Declarative/ToolApproval/Program.cs b/dotnet/samples/03-workflows/Declarative/ToolApproval/Program.cs index 544974e096..9e9bd65b6b 100644 --- a/dotnet/samples/03-workflows/Declarative/ToolApproval/Program.cs +++ b/dotnet/samples/03-workflows/Declarative/ToolApproval/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Identity; using Microsoft.Extensions.Configuration; using OpenAI.Responses; 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 @@ internal sealed class OpenAIResponsesAgentClient(HttpClient httpClient) : AgentC 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/AgentWithPurview/Program.cs b/dotnet/samples/05-end-to-end/AgentWithPurview/Program.cs index fc0974c5bd..33e7001a51 100644 --- a/dotnet/samples/05-end-to-end/AgentWithPurview/Program.cs +++ b/dotnet/samples/05-end-to-end/AgentWithPurview/Program.cs @@ -30,8 +30,8 @@ TokenCredential browserCredential = new InteractiveBrowserCredential( using IChatClient client = new AzureOpenAIClient( new Uri(endpoint), new DefaultAzureCredential()) - .GetResponsesClient(deploymentName) - .AsIChatClient() + .GetResponsesClient() + .AsIChatClient(deploymentName) .AsBuilder() .WithPurview(browserCredential, new PurviewSettings("Agent Framework Test App")) .Build(); diff --git a/dotnet/samples/05-end-to-end/HostedAgents/AgentThreadAndHITL/AgentThreadAndHITL.csproj b/dotnet/samples/05-end-to-end/HostedAgents/AgentThreadAndHITL/AgentThreadAndHITL.csproj index 1398a60228..1afc7a7cec 100644 --- a/dotnet/samples/05-end-to-end/HostedAgents/AgentThreadAndHITL/AgentThreadAndHITL.csproj +++ b/dotnet/samples/05-end-to-end/HostedAgents/AgentThreadAndHITL/AgentThreadAndHITL.csproj @@ -37,7 +37,7 @@ - + diff --git a/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/AgentWithHostedMCP.csproj b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/AgentWithHostedMCP.csproj index e854cfcd40..c0e14e74b8 100644 --- a/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/AgentWithHostedMCP.csproj +++ b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/AgentWithHostedMCP.csproj @@ -36,9 +36,10 @@ - + + diff --git a/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/Program.cs b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/Program.cs index b7b610b663..8559269dff 100644 --- a/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/Program.cs +++ b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithHostedMCP/Program.cs @@ -31,7 +31,8 @@ AITool mcpTool = new HostedMcpServerTool(serverName: "microsoft_learn", serverAd AIAgent agent = new AzureOpenAIClient( new Uri(endpoint), new DefaultAzureCredential()) - .GetResponsesClient(deploymentName) + .GetResponsesClient() + .AsIChatClient(deploymentName) .AsAIAgent( instructions: "You answer questions by searching the Microsoft Learn content only.", name: "MicrosoftLearnAgent", diff --git a/dotnet/samples/05-end-to-end/HostedAgents/AgentWithLocalTools/AgentWithLocalTools.csproj b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithLocalTools/AgentWithLocalTools.csproj index 975333e584..ccda3156e5 100644 --- a/dotnet/samples/05-end-to-end/HostedAgents/AgentWithLocalTools/AgentWithLocalTools.csproj +++ b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithLocalTools/AgentWithLocalTools.csproj @@ -38,7 +38,7 @@ - + diff --git a/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj index 32e00f832b..19e5015912 100644 --- a/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj +++ b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTextSearchRag/AgentWithTextSearchRag.csproj @@ -36,7 +36,7 @@ - + diff --git a/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTools/AgentWithTools.csproj b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTools/AgentWithTools.csproj index 959cca1db5..19e5015912 100644 --- a/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTools/AgentWithTools.csproj +++ b/dotnet/samples/05-end-to-end/HostedAgents/AgentWithTools/AgentWithTools.csproj @@ -36,7 +36,7 @@ - + diff --git a/dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/AgentsInWorkflows.csproj b/dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/AgentsInWorkflows.csproj index 56a55a428d..3b3af40664 100644 --- a/dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/AgentsInWorkflows.csproj +++ b/dotnet/samples/05-end-to-end/HostedAgents/AgentsInWorkflows/AgentsInWorkflows.csproj @@ -36,7 +36,7 @@ - + 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 @@ internal sealed class AFAgentApplication : AgentApplication && 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 @@ internal sealed class AFAgentApplication : AgentApplication /// 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 @@ internal sealed class AFAgentApplication : AgentApplication }); card.Body.Add(new AdaptiveTextBlock { - Text = $"Function: {functionApprovalRequest.FunctionCall.Name}" + Text = $"Function: {((FunctionCallContent)functionApprovalRequest.ToolCall).Name}" }); card.Body.Add(new AdaptiveActionSet() { diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/Microsoft.Agents.AI.AzureAI.Persistent.csproj b/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/Microsoft.Agents.AI.AzureAI.Persistent.csproj index 31785a8fa9..bb02f3065a 100644 --- a/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/Microsoft.Agents.AI.AzureAI.Persistent.csproj +++ b/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/Microsoft.Agents.AI.AzureAI.Persistent.csproj @@ -20,6 +20,8 @@ Microsoft Agent Framework AzureAI Persistent Agents Provides Microsoft Agent Framework support for Azure AI Persistent Agents. + + false diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/README.md b/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/README.md new file mode 100644 index 0000000000..a01debc5ca --- /dev/null +++ b/dotnet/src/Microsoft.Agents.AI.AzureAI.Persistent/README.md @@ -0,0 +1,19 @@ +# Microsoft.Agents.AI.AzureAI.Persistent + +Provides integration between the Microsoft Agent Framework and Azure AI Agents Persistent (`Azure.AI.Agents.Persistent`). + +## ⚠️ Known Compatibility Limitation + +The underlying `Azure.AI.Agents.Persistent` package (currently 1.2.0-beta.9) targets `Microsoft.Extensions.AI.Abstractions` 10.1.x and references types that were renamed in 10.4.0 (e.g., `McpServerToolApprovalResponseContent` → `ToolApprovalResponseContent`). This causes `TypeLoadException` at runtime when used with ME.AI 10.4.0+. + +**Compatible versions:** + +| Package | Compatible Version | +|---|---| +| `Azure.AI.Agents.Persistent` | 1.2.0-beta.9 (targets ME.AI 10.1.x) | +| `Microsoft.Extensions.AI.Abstractions` | ≤ 10.3.0 | +| `OpenAI` | ≤ 2.8.0 | + +**Resolution:** An updated version of `Azure.AI.Agents.Persistent` targeting ME.AI 10.4.0+ is expected in 1.2.0-beta.10. The upstream fix is tracked in [Azure/azure-sdk-for-net#56929](https://github.com/Azure/azure-sdk-for-net/pull/56929). + +**Tracking issue:** [microsoft/agent-framework#4769](https://github.com/microsoft/agent-framework/issues/4769) diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs index 51ddf0054c..32bb08674b 100644 --- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs +++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClient.cs @@ -2,8 +2,9 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using Azure.AI.Extensions.OpenAI; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Extensions.AI; using Microsoft.Shared.DiagnosticIds; using Microsoft.Shared.Diagnostics; @@ -57,7 +58,7 @@ internal sealed class AzureAIProjectChatClient : DelegatingChatClient /// The provided should be decorated with a for proper functionality. /// internal AzureAIProjectChatClient(AIProjectClient aiProjectClient, AgentRecord agentRecord, ChatOptions? chatOptions) - : this(aiProjectClient, Throw.IfNull(agentRecord).Versions.Latest, chatOptions) + : this(aiProjectClient, Throw.IfNull(agentRecord).GetLatestVersion(), chatOptions) { this._agentRecord = agentRecord; } diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs index 5d2c67695f..b129f4b1f2 100644 --- a/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/AzureAIProjectChatClientExtensions.cs @@ -9,7 +9,8 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; using System.Text.RegularExpressions; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Extensions.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Agents.AI; using Microsoft.Agents.AI.AzureAI; using Microsoft.Extensions.AI; @@ -189,7 +190,7 @@ public static partial class AzureAIProjectChatClientExtensions ThrowIfInvalidAgentName(options.Name); AgentRecord agentRecord = await GetAgentRecordByNameAsync(aiProjectClient, options.Name, cancellationToken).ConfigureAwait(false); - var agentVersion = agentRecord.Versions.Latest; + var agentVersion = agentRecord.GetLatestVersion(); var agentOptions = CreateChatClientAgentOptions(agentVersion, options, requireInvocableTools: !options.UseProvidedChatClientAsIs); @@ -361,7 +362,7 @@ public static partial class AzureAIProjectChatClientExtensions { ClientResult protocolResponse = await aiProjectClient.Agents.GetAgentAsync(agentName, cancellationToken.ToRequestOptions(false)).ConfigureAwait(false); var rawResponse = protocolResponse.GetRawResponse(); - AgentRecord? result = ModelReaderWriter.Read(rawResponse.Content, s_modelWriterOptionsWire, AzureAIProjectsOpenAIContext.Default); + AgentRecord? result = ModelReaderWriter.Read(rawResponse.Content, s_modelWriterOptionsWire, AzureAIProjectsAgentsContext.Default); return result ?? throw new InvalidOperationException($"Agent with name '{agentName}' not found."); } @@ -370,11 +371,11 @@ public static partial class AzureAIProjectChatClientExtensions /// private static async Task CreateAgentVersionWithProtocolAsync(AIProjectClient aiProjectClient, string agentName, AgentVersionCreationOptions creationOptions, CancellationToken cancellationToken) { - BinaryData serializedOptions = ModelReaderWriter.Write(creationOptions, s_modelWriterOptionsWire, AzureAIProjectsContext.Default); + BinaryData serializedOptions = ModelReaderWriter.Write(creationOptions, s_modelWriterOptionsWire, AzureAIProjectsAgentsContext.Default); BinaryContent content = BinaryContent.Create(serializedOptions); ClientResult protocolResponse = await aiProjectClient.Agents.CreateAgentVersionAsync(agentName, content, foundryFeatures: null, cancellationToken.ToRequestOptions(false)).ConfigureAwait(false); var rawResponse = protocolResponse.GetRawResponse(); - AgentVersion? result = ModelReaderWriter.Read(rawResponse.Content, s_modelWriterOptionsWire, AzureAIProjectsOpenAIContext.Default); + AgentVersion? result = ModelReaderWriter.Read(rawResponse.Content, s_modelWriterOptionsWire, AzureAIProjectsAgentsContext.Default); return result ?? throw new InvalidOperationException($"Failed to create agent version for agent '{agentName}'."); } @@ -485,7 +486,7 @@ public static partial class AzureAIProjectChatClientExtensions => AsChatClientAgent( AIProjectClient, agentRecord, - CreateChatClientAgentOptions(agentRecord.Versions.Latest, new ChatOptions() { Tools = tools }, requireInvocableTools), + CreateChatClientAgentOptions(agentRecord.GetLatestVersion(), new ChatOptions() { Tools = tools }, requireInvocableTools), clientFactory, services); diff --git a/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj b/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj index 2fde79e32b..0cd8690126 100644 --- a/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj +++ b/dotnet/src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj @@ -15,7 +15,6 @@ - diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/AgentResponseUpdateExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/AgentResponseUpdateExtensions.cs index f4c1e3c7a0..ac63237d56 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/AgentResponseUpdateExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/AgentResponseUpdateExtensions.cs @@ -196,8 +196,8 @@ internal static class AgentResponseUpdateExtensions 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 09046e1822..5aee8eb046 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 class OpenAIResponseClientExtensions Tools = tools, } }, + model, clientFactory, loggerFactory, services); @@ -68,6 +71,7 @@ public static class OpenAIResponseClientExtensions /// /// 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 class OpenAIResponseClientExtensions 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 class OpenAIResponseClientExtensions Throw.IfNull(client); Throw.IfNull(options); - var chatClient = client.AsIChatClient(); + var chatClient = client.AsIChatClient(model); if (clientFactory is not null) { @@ -100,6 +105,7 @@ public static class OpenAIResponseClientExtensions /// 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). /// /// Includes an encrypted version of reasoning tokens in reasoning item outputs. /// This enables reasoning items to be used in multi-turn conversations when using the Responses API statelessly @@ -109,10 +115,10 @@ public static class OpenAIResponseClientExtensions /// 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, bool includeReasoningEncryptedContent = true) + public static IChatClient AsIChatClientWithStoredOutputDisabled(this ResponsesClient responseClient, string? model = null, bool includeReasoningEncryptedContent = true) { return Throw.IfNull(responseClient) - .AsIChatClient() + .AsIChatClient(model) .AsBuilder() .ConfigureOptions(x => x.RawRepresentationFactory = _ => includeReasoningEncryptedContent ? new CreateResponseOptions() { StoredOutputEnabled = false, IncludedProperties = { IncludedResponseProperty.ReasoningEncryptedContent } } diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/AzureAgentProvider.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/AzureAgentProvider.cs index 9f909ad84e..bfc7bd36ff 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/AzureAgentProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.AzureAI/AzureAgentProvider.cs @@ -10,8 +10,9 @@ using System.Runtime.CompilerServices; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; +using Azure.AI.Extensions.OpenAI; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Azure.Core; using Microsoft.Extensions.AI; using OpenAI.Responses; @@ -149,7 +150,7 @@ public sealed class AzureAgentProvider(Uri projectEndpoint, TokenCredential proj agentName, cancellationToken).ConfigureAwait(false); - targetAgent = agentRecord.Versions.Latest; + targetAgent = agentRecord.GetLatestVersion(); } else { 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 107f4f0260..681cd5dc85 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs @@ -185,8 +185,8 @@ public sealed class DefaultMcpToolHandler : IMcpToolHandler, IAsyncDisposable 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) { @@ -203,7 +203,7 @@ public sealed class DefaultMcpToolHandler : IMcpToolHandler, IAsyncDisposable } } - 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; } @@ -218,7 +218,7 @@ public sealed class DefaultMcpToolHandler : IMcpToolHandler, IAsyncDisposable 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 @@ internal sealed class InvokeAzureAgentExecutor(InvokeAzureAgent model, ResponseA 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 @@ internal sealed class InvokeFunctionToolExecutor( // 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 @@ internal sealed class InvokeMcpToolExecutor( 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 @@ internal sealed class InvokeMcpToolExecutor( 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 @@ internal sealed class InvokeMcpToolExecutor( 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 @@ internal sealed class InvokeMcpToolExecutor( // 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..9cc72d7310 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 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor 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 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor } 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 ToolApprovalRequest found with id '{response.RequestId}'."); } List implicitTurnMessages = [new ChatMessage(ChatRole.User, [response])]; @@ -164,7 +164,7 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor 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 @@ internal sealed class AIAgentHostExecutor : ChatProtocolExecutor { 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/Foundry/Agents/AgentFactory.cs b/dotnet/src/Shared/Foundry/Agents/AgentFactory.cs index e179058e69..c2a2770226 100644 --- a/dotnet/src/Shared/Foundry/Agents/AgentFactory.cs +++ b/dotnet/src/Shared/Foundry/Agents/AgentFactory.cs @@ -4,8 +4,9 @@ using System; using System.Threading.Tasks; +using Azure.AI.Extensions.OpenAI; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; namespace Shared.Foundry; 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 @@ internal sealed class WorkflowRunner 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 @@ internal sealed class WorkflowRunner 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/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs index a6691a41bd..e1cef1f1aa 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientCreateTests.cs @@ -5,7 +5,7 @@ using System.IO; using System.Threading.Tasks; using AgentConformance.IntegrationTests.Support; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using OpenAI.Files; @@ -56,8 +56,8 @@ public class AIProjectClientCreateTests var agentRecord = await this._client.Agents.GetAgentAsync(agent.Name); Assert.NotNull(agentRecord); Assert.Equal(AgentName, agentRecord.Value.Name); - var definition = Assert.IsType(agentRecord.Value.Versions.Latest.Definition); - Assert.Equal(AgentDescription, agentRecord.Value.Versions.Latest.Description); + var definition = Assert.IsType(agentRecord.Value.GetLatestVersion().Definition); + Assert.Equal(AgentDescription, agentRecord.Value.GetLatestVersion().Description); Assert.Equal(AgentInstructions, definition.Instructions); } finally diff --git a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs index 6356bb6e01..42892b99b3 100644 --- a/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs +++ b/dotnet/tests/AzureAI.IntegrationTests/AIProjectClientFixture.cs @@ -6,8 +6,8 @@ using System.Linq; using System.Threading.Tasks; using AgentConformance.IntegrationTests; using AgentConformance.IntegrationTests.Support; +using Azure.AI.Extensions.OpenAI; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; using Microsoft.Agents.AI; using Microsoft.Extensions.AI; using OpenAI.Responses; diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunStreamingTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunStreamingTests.cs index 84e3d5d9f9..b4a581d81e 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunStreamingTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunStreamingTests.cs @@ -4,6 +4,10 @@ using AgentConformance.IntegrationTests; namespace AzureAIAgentsPersistent.IntegrationTests; +// Disabled: Azure.AI.Agents.Persistent 1.2.0-beta.9 references McpServerToolApprovalResponseContent +// which was removed in ME.AI 10.4.0. Re-enable once Persistent targets ME.AI 10.4.0+ (expected in 1.2.0-beta.10). +// Tracking: https://github.com/microsoft/agent-framework/issues/4769 +[Trait("Category", "IntegrationDisabled")] public class AzureAIAgentsChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new()) { } diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunTests.cs index b2f75c536e..8529dac6b4 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunTests.cs @@ -4,6 +4,10 @@ using AgentConformance.IntegrationTests; namespace AzureAIAgentsPersistent.IntegrationTests; +// Disabled: Azure.AI.Agents.Persistent 1.2.0-beta.9 references McpServerToolApprovalResponseContent +// which was removed in ME.AI 10.4.0. Re-enable once Persistent targets ME.AI 10.4.0+ (expected in 1.2.0-beta.10). +// Tracking: https://github.com/microsoft/agent-framework/issues/4769 +[Trait("Category", "IntegrationDisabled")] public class AzureAIAgentsChatClientAgentRunTests() : ChatClientAgentRunTests(() => new()) { } diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs index 20f6a4cda4..b5098ddd74 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentCreateTests.cs @@ -12,6 +12,10 @@ using Shared.IntegrationTests; namespace AzureAIAgentsPersistent.IntegrationTests; +// Disabled: Azure.AI.Agents.Persistent 1.2.0-beta.9 references McpServerToolApprovalResponseContent +// which was removed in ME.AI 10.4.0. Re-enable once Persistent targets ME.AI 10.4.0+ (expected in 1.2.0-beta.10). +// Tracking: https://github.com/microsoft/agent-framework/issues/4769 +[Trait("Category", "IntegrationDisabled")] public class AzureAIAgentsPersistentCreateTests { private const string SkipCodeInterpreterReason = "Azure AI Code Interpreter intermittently fails to execute uploaded files in CI"; diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunStreamingTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunStreamingTests.cs index e18812aff4..87f1cfbb70 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunStreamingTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunStreamingTests.cs @@ -4,6 +4,10 @@ using AgentConformance.IntegrationTests; namespace AzureAIAgentsPersistent.IntegrationTests; +// Disabled: Azure.AI.Agents.Persistent 1.2.0-beta.9 references McpServerToolApprovalResponseContent +// which was removed in ME.AI 10.4.0. Re-enable once Persistent targets ME.AI 10.4.0+ (expected in 1.2.0-beta.10). +// Tracking: https://github.com/microsoft/agent-framework/issues/4769 +[Trait("Category", "IntegrationDisabled")] public class AzureAIAgentsPersistentRunStreamingTests() : RunStreamingTests(() => new()) { } diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunTests.cs index 3e6032401d..fd4b92e4b9 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunTests.cs @@ -4,6 +4,10 @@ using AgentConformance.IntegrationTests; namespace AzureAIAgentsPersistent.IntegrationTests; +// Disabled: Azure.AI.Agents.Persistent 1.2.0-beta.9 references McpServerToolApprovalResponseContent +// which was removed in ME.AI 10.4.0. Re-enable once Persistent targets ME.AI 10.4.0+ (expected in 1.2.0-beta.10). +// Tracking: https://github.com/microsoft/agent-framework/issues/4769 +[Trait("Category", "IntegrationDisabled")] public class AzureAIAgentsPersistentRunTests() : RunTests(() => new()) { } diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs index 0fa20f18ac..34663c29e4 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentStructuredOutputRunTests.cs @@ -5,6 +5,10 @@ using AgentConformance.IntegrationTests; namespace AzureAIAgentsPersistent.IntegrationTests; +// Disabled: Azure.AI.Agents.Persistent 1.2.0-beta.9 references McpServerToolApprovalResponseContent +// which was removed in ME.AI 10.4.0. Re-enable once Persistent targets ME.AI 10.4.0+ (expected in 1.2.0-beta.10). +// Tracking: https://github.com/microsoft/agent-framework/issues/4769 +[Trait("Category", "IntegrationDisabled")] public class AzureAIAgentsPersistentStructuredOutputRunTests() : StructuredOutputRunTests(() => new()) { private const string SkipReason = "Fails intermittently on the build agent/CI"; diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs index 65726bb2aa..261faaded8 100644 --- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AzureAIProjectChatClientExtensionsTests.cs @@ -12,8 +12,9 @@ using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Azure.AI.Extensions.OpenAI; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Extensions.AI; using Moq; using OpenAI.Responses; @@ -369,7 +370,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests public async Task GetAIAgentAsync_ByName_WithNonExistentAgent_ThrowsInvalidOperationExceptionAsync() { // Arrange - var mockAgentOperations = new Mock(); + var mockAgentOperations = new Mock(); mockAgentOperations .Setup(c => c.GetAgentAsync(It.IsAny(), It.IsAny())) .ReturnsAsync(ClientResult.FromOptionalValue((AgentRecord)null!, new MockPipelineResponse(200, BinaryData.FromString("null")))); @@ -889,12 +890,12 @@ public sealed class AzureAIProjectChatClientExtensionsTests // Add tools to the definition definition.Tools.Add(ResponseTool.CreateFunctionTool("create_tool", BinaryData.FromString("{}"), strictModeEnabled: false)); - definition.Tools.Add((ResponseTool)AgentTool.CreateBingCustomSearchTool(new BingCustomSearchToolParameters([new BingCustomSearchConfiguration("connection-id", "instance-name")]))); - definition.Tools.Add((ResponseTool)AgentTool.CreateBrowserAutomationTool(new BrowserAutomationToolParameters(new BrowserAutomationToolConnectionParameters("id")))); + definition.Tools.Add((ResponseTool)AgentTool.CreateBingCustomSearchTool(new BingCustomSearchToolOptions([new BingCustomSearchConfiguration("connection-id", "instance-name")]))); + definition.Tools.Add((ResponseTool)AgentTool.CreateBrowserAutomationTool(new BrowserAutomationToolOptions(new BrowserAutomationToolConnectionParameters("id")))); definition.Tools.Add(AgentTool.CreateA2ATool(new Uri("https://test-uri.microsoft.com"))); definition.Tools.Add((ResponseTool)AgentTool.CreateBingGroundingTool(new BingGroundingSearchToolOptions([new BingGroundingSearchConfiguration("connection-id")]))); definition.Tools.Add((ResponseTool)AgentTool.CreateMicrosoftFabricTool(fabricToolOptions)); - definition.Tools.Add((ResponseTool)AgentTool.CreateOpenApiTool(new OpenAPIFunctionDefinition("name", BinaryData.FromString(OpenAPISpec), new OpenAPIAnonymousAuthenticationDetails()))); + definition.Tools.Add((ResponseTool)AgentTool.CreateOpenApiTool(new OpenApiFunctionDefinition("name", BinaryData.FromString(OpenAPISpec), new OpenAPIAnonymousAuthenticationDetails()))); definition.Tools.Add((ResponseTool)AgentTool.CreateSharepointTool(sharepointOptions)); definition.Tools.Add((ResponseTool)AgentTool.CreateStructuredOutputsTool(structuredOutputs)); definition.Tools.Add((ResponseTool)AgentTool.CreateAzureAISearchTool(new AzureAISearchToolOptions([new AzureAISearchToolIndex() { IndexName = "name" }]))); @@ -3020,7 +3021,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests { // Handle backward compatibility with bool parameter var effectiveVersionMode = useEmptyVersion ? VersionMode.Empty : versionMode; - this.Agents = new FakeAIProjectAgentsOperations(agentName, instructions, description, agentDefinitionResponse, effectiveVersionMode); + this.Agents = new FakeAgentsClient(agentName, instructions, description, agentDefinitionResponse, effectiveVersionMode); } public override ClientConnection GetConnection(string connectionId) @@ -3028,9 +3029,9 @@ public sealed class AzureAIProjectChatClientExtensionsTests return new ClientConnection("fake-connection-id", "http://localhost", ClientPipeline.Create(), CredentialKind.None); } - public override AIProjectAgentsOperations Agents { get; } + public override AgentsClient Agents { get; } - private sealed class FakeAIProjectAgentsOperations : AIProjectAgentsOperations + private sealed class FakeAgentsClient : AgentsClient { private readonly string? _agentName; private readonly string? _instructions; @@ -3038,7 +3039,7 @@ public sealed class AzureAIProjectChatClientExtensionsTests private readonly AgentDefinition? _agentDefinition; private readonly VersionMode _versionMode; - public FakeAIProjectAgentsOperations(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null, VersionMode versionMode = VersionMode.Normal) + public FakeAgentsClient(string? agentName = null, string? instructions = null, string? description = null, AgentDefinition? agentDefinitionResponse = null, VersionMode versionMode = VersionMode.Normal) { this._agentName = agentName; this._instructions = instructions; diff --git a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/TestDataUtil.cs b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/TestDataUtil.cs index 8471ddbcf1..0a33c03ccd 100644 --- a/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/TestDataUtil.cs +++ b/dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/TestDataUtil.cs @@ -2,7 +2,7 @@ using System.ClientModel.Primitives; using System.IO; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; namespace Microsoft.Agents.AI.AzureAI.UnitTests; 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 sealed class FunctionApprovalTests : ConformanceTestBase #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 sealed class FunctionApprovalTests : ConformanceTestBase #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 sealed class FunctionApprovalTests : ConformanceTestBase #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 sealed class FunctionApprovalTests : ConformanceTestBase #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 sealed class FunctionApprovalTests : ConformanceTestBase #endregion - #region FunctionApprovalResponseContent Tests + #region ToolApprovalResponseContent Tests [Fact] public async Task FunctionApprovalResponse_Approved_GeneratesCorrectEvent_SuccessAsync() @@ -187,7 +187,7 @@ public sealed class FunctionApprovalTests : ConformanceTestBase #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 sealed class FunctionApprovalTests : ConformanceTestBase #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 sealed class FunctionApprovalTests : ConformanceTestBase #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 sealed class FunctionApprovalTests : ConformanceTestBase #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 sealed class FunctionApprovalTests : ConformanceTestBase #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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable // 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable // 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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 @@ public sealed class OpenAIResponsesIntegrationTests : IAsyncDisposable 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.UnitTests/OpenTelemetryAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs index 4cda58875e..48bdca287e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAgentTests.cs @@ -603,7 +603,47 @@ public class OpenTelemetryAgentTests Assert.False(tags.ContainsKey("gen_ai.input.messages")); Assert.False(tags.ContainsKey("gen_ai.output.messages")); Assert.False(tags.ContainsKey("gen_ai.system_instructions")); - Assert.False(tags.ContainsKey("gen_ai.tool.definitions")); + + // gen_ai.tool.definitions is always emitted regardless of EnableSensitiveData (ME.AI 10.4.0+) + Assert.Equal(ReplaceWhitespace(""" + [ + { + "type": "function", + "name": "GetPersonAge", + "description": "Gets the age of a person by name.", + "parameters": { + "type": "object", + "properties": { + "personName": { + "type": "string" + } + }, + "required": [ + "personName" + ] + } + }, + { + "type": "web_search" + }, + { + "type": "function", + "name": "GetCurrentWeather", + "description": "Gets the current weather for a location.", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string" + } + }, + "required": [ + "location" + ] + } + } + ] + """), ReplaceWhitespace(tags["gen_ai.tool.definitions"])); } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/AgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/AgentProvider.cs index a4198d3a4c..58e6b96d10 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/AgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/AgentProvider.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Extensions.Configuration; using Shared.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/FunctionToolAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/FunctionToolAgentProvider.cs index 98243dc4d3..05ad68fa3d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/FunctionToolAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/FunctionToolAgentProvider.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Extensions.AI; using Microsoft.Extensions.Configuration; using OpenAI.Responses; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MarketingAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MarketingAgentProvider.cs index 693d99b638..b25e921abe 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MarketingAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MarketingAgentProvider.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Extensions.Configuration; using Shared.Foundry; using Shared.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MathChatAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MathChatAgentProvider.cs index 91d63404bd..c65cc3ce57 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MathChatAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/MathChatAgentProvider.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Extensions.Configuration; using Shared.Foundry; using Shared.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/PoemAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/PoemAgentProvider.cs index 1b79e4e25e..6e3912a276 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/PoemAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/PoemAgentProvider.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Extensions.Configuration; using Shared.Foundry; using Shared.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/TestAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/TestAgentProvider.cs index dcb09a4798..e1278e6fb7 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/TestAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/TestAgentProvider.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Extensions.Configuration; using Shared.Foundry; using Shared.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/VisionAgentProvider.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/VisionAgentProvider.cs index 0d95342264..027a67e254 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/VisionAgentProvider.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Agents/VisionAgentProvider.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using Azure.AI.Projects; -using Azure.AI.Projects.OpenAI; +using Azure.AI.Projects.Agents; using Microsoft.Extensions.Configuration; using Shared.Foundry; using Shared.IntegrationTests; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/WorkflowTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/WorkflowTest.cs index 0333bf4d1c..43e64bbb35 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/WorkflowTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/Framework/WorkflowTest.cs @@ -94,7 +94,7 @@ public abstract class WorkflowTest(ITestOutputHelper output) : IntegrationTest(o while (current is not null) { - if (Directory.Exists(Path.Combine(current.FullName, ".git"))) + if (Directory.Exists(Path.Combine(current.FullName, "workflow-samples"))) { return current.FullName; } 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 @@ public sealed class InvokeToolWorkflowTest(ITestOutputHelper output) : Integrati 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 @@ public sealed class InvokeToolWorkflowTest(ITestOutputHelper output) : Integrati 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 sealed class ExternalInputRequestTest(ITestOutputHelper output) : EventTe 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 sealed class ExternalInputRequestTest(ITestOutputHelper output) : EventTe 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 sealed class ExternalInputResponseTest(ITestOutputHelper output) : EventT 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 sealed class ExternalInputResponseTest(ITestOutputHelper output) : EventT 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 sealed class InvokeMcpToolExecutorTest(ITestOutputHelper output) : Workfl // 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 sealed class InvokeMcpToolExecutorTest(ITestOutputHelper output) : Workfl // 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 sealed class InvokeMcpToolExecutorTest(ITestOutputHelper output) : Workfl // 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 sealed class InvokeMcpToolExecutorTest(ITestOutputHelper output) : Workfl // 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 sealed class InvokeMcpToolExecutorTest(ITestOutputHelper output) : Workfl // 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 sealed class InvokeMcpToolExecutorTest(ITestOutputHelper output) : Workfl // 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 sealed class InvokeMcpToolExecutorTest(ITestOutputHelper output) : Workfl 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 sealed class InvokeMcpToolExecutorTest(ITestOutputHelper output) : Workfl } 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 class AIAgentHostExecutorTests 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..32b082ad6d 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 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp => new(requestType switch { TestAgentRequestType.FunctionCall => new TestRequestAgentSession(), - TestAgentRequestType.UserInputRequest => new TestRequestAgentSession(), + TestAgentRequestType.UserInputRequest => new TestRequestAgentSession(), _ => throw new NotSupportedException(), }); @@ -41,7 +41,7 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp => new(requestType switch { TestAgentRequestType.FunctionCall => new TestRequestAgentSession(), - TestAgentRequestType.UserInputRequest => new TestRequestAgentSession(), + TestAgentRequestType.UserInputRequest => new TestRequestAgentSession(), _ => throw new NotSupportedException(), }); @@ -179,58 +179,43 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp } } - 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) - { - throw new InvalidOperationException($"Invalid request: Expecting {typeof(FunctionApprovalResponseContent)}, got {request.GetType()}"); - } - - return new FunctionApprovalResponseContent(approvalRequest.Id, true, approvalRequest.FunctionCall); + return new ToolApprovalResponseContent(request.RequestId, true, request.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(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) - { - throw new InvalidOperationException($"Invalid request: Expecting {typeof(FunctionApprovalResponseContent)}, got {request.GetType()}"); - } - - if (response is not FunctionApprovalResponseContent approvalResponse) - { - throw new InvalidOperationException($"Invalid response: Expecting {typeof(FunctionApprovalResponseContent)}, got {response.GetType()}"); - } - - approvalResponse.Approved.Should().BeTrue(); - approvalResponse.FunctionCall.As().Should().Be(approvalRequest.FunctionCall); - session.ServicedRequests.Add(response.Id); - session.UnservicedRequests.Remove(response.Id); + response.Approved.Should().BeTrue(); + ((FunctionCallContent)response.ToolCall).Should().Be((FunctionCallContent)request.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 +246,7 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp 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 +280,12 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp return this.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 +300,7 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp responses = this.ValidateUnpairedRequests(requests.Select(AssertAndExtractRequestContent)).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 class OpenAIResponseFixture(bool store) : IChatClientAgentFixture 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 class OpenAIResponseFixture(bool store) : IChatClientAgentFixture 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(); }