Skip to content

.NET: Update to OpenAI 2.9.1 and Microsoft.Extensions.AI 10.4.0#4613

Open
Copilot wants to merge 6 commits intomainfrom
copilot/update-openai-and-microsoft-extensions-ai
Open

.NET: Update to OpenAI 2.9.1 and Microsoft.Extensions.AI 10.4.0#4613
Copilot wants to merge 6 commits intomainfrom
copilot/update-openai-and-microsoft-extensions-ai

Conversation

Copy link
Contributor

Copilot AI commented Mar 11, 2026

Motivation and Context

OpenAI 2.9.1 and Microsoft.Extensions.AI 10.4.0 introduced breaking API changes requiring updates across the codebase.

Description

Package bumps

  • OpenAI 2.8.0 → 2.9.1
  • Microsoft.Extensions.AI / Microsoft.Extensions.AI.Abstractions / Microsoft.Extensions.AI.OpenAI 10.3.0 → 10.4.0
  • Associated transitive runtime deps 10.0.3 → 10.0.4

OpenAI 2.9.1: model moved from GetResponsesClient to AsIChatClient/AsAIAgent

// Before
openAIClient.GetResponsesClient("gpt-4o").AsAIAgent(instructions: "...");

// After
openAIClient.GetResponsesClient().AsAIAgent("gpt-4o", instructions: "...");

Updated OpenAIResponseClientExtensions, all samples, integration test fixtures, and OpenAIResponsesAgentClient.

ME.AI 10.4.0: approval content type unification

FunctionApprovalRequestContent, FunctionApprovalResponseContent, UserInputRequestContent, UserInputResponseContent, and McpServerToolApprovalRequestContent/McpServerToolApprovalResponseContent are all replaced by ToolApprovalRequestContent / ToolApprovalResponseContent. Constructor param id:requestId:. Property .Id.RequestId.

Updated across ~30 files including sources, samples, and tests. Key behavioral changes:

  • WorkflowRunner: merged separate ApproveFunction + ApproveMCP handlers into a single ApproveToolCall dispatching on ToolCall type via switch expression
  • FunctionApprovalRequestEventGenerator: replaced hard cast (FunctionCallContent)approvalRequest.ToolCall with pattern match; silently skips non-function approval requests (e.g., MCP) rather than throwing
  • MCP samples: ToolApprovalRequestContent.ToolCall is now ToolCallContent (base); must cast to McpServerToolCallContent to access .Name (was .ToolName) and .ServerName
  • Client/Program.cs and ServerFunctionApprovalServerAgent.cs: replaced unsafe (FunctionCallContent)approvalRequest.ToolCall casts with safe when approvalRequest.ToolCall is FunctionCallContent fcc pattern matching, ensuring only function-based approvals are handled

ME.AI 10.4.0: McpServerToolResultContent.Output.Outputs

Updated in InvokeMcpToolExecutor, DefaultMcpToolHandler, and test mocks.

Other

  • Added #pragma warning disable OPENAI001 for AsOpenAIStreamingChatCompletionUpdatesAsync, newly marked experimental in 2.9.1
  • CreateResponseAsync/CreateResponseStreamingAsync signatures updated in integration tests

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • Is this a breaking change? If yes, add "[BREAKING]" prefix to the title of the PR.

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 4 commits March 11, 2026 00:03
…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>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
…put→.Outputs

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
@markwallace-microsoft markwallace-microsoft added .NET workflows Related to Workflows in agent-framework labels Mar 11, 2026
@github-actions github-actions bot changed the title Update to OpenAI 2.9.1 and Microsoft.Extensions.AI 10.4.0 .NET: Update to OpenAI 2.9.1 and Microsoft.Extensions.AI 10.4.0 Mar 11, 2026
switch (content)
{
case FunctionApprovalRequestContent approvalRequest:
case ToolApprovalRequestContent approvalRequest:
Copy link
Member

Choose a reason for hiding this comment

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

@copilot in this and similar cases, you can do:

case ToolApprovalRequestContent approvalRequest when approvalRequest.ToolCall is FunctionCallContent fcc:

and then use fcc rather than casting. This helps ensure that only FunctionCallContent-based approvals are handled, and it reduces the amount of unsafe casting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done in eac339d. Updated both Client/Program.cs and ServerFunctionApprovalServerAgent.cs to use when approvalRequest.ToolCall is FunctionCallContent fcc pattern matching, eliminating all unsafe casts.

@stephentoub stephentoub marked this pull request as ready for review March 11, 2026 02:45
Copilot AI review requested due to automatic review settings March 11, 2026 02:45
@stephentoub
Copy link
Member

This will be broken until Azure.AI.OpenAI and Azure.AI.Projects update to OpenAI 2.9.1.

…ionCallContent

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the .NET Agent Framework codebase to align with newer OpenAI Responses client patterns (model passed per request / per chat client) and consolidates “approval required” flows under unified tool-approval content types across workflows, hosting, tests, and samples.

Changes:

  • Switch OpenAI Responses usage to GetResponsesClient() without a baked-in model, and plumb model IDs via AsIChatClient(model) / per-request parameters.
  • Replace function/MCP-specific approval content with ToolApprovalRequestContent / ToolApprovalResponseContent across workflows, hosting streaming events, tests, and samples.
  • Update MCP tool result handling from Output to Outputs, and bump several .NET package versions.

Reviewed changes

Copilot reviewed 39 out of 39 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs Stores model name separately and passes it into AsIChatClient(model) after switching to model-less GetResponsesClient().
dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestRequestAgent.cs Updates unit-test agent session/request plumbing to use tool-approval request/response content types.
dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AIAgentHostExecutorTests.cs Adjusts request interception tests to expect ToolApprovalRequestContent.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/ObjectModel/InvokeMcpToolExecutorTest.cs Updates MCP approval tests to use tool-approval content + renames OutputOutputs in mocked results.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputResponseTest.cs Updates serialization assertions for unified tool-approval response content.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Events/ExternalInputRequestTest.cs Updates serialization assertions for unified tool-approval request content.
dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/InvokeToolWorkflowTest.cs Updates integration test handling/logging for unified tool approvals.
dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/OpenAIResponsesIntegrationTests.cs Updates tests for new ResponsesClient usage and per-call model passing.
dotnet/tests/Microsoft.Agents.AI.Hosting.OpenAI.UnitTests/FunctionApprovalTests.cs Renames regions and swaps approval content types to tool-approval equivalents.
dotnet/src/Shared/Workflows/Execution/WorkflowRunner.cs Consolidates approval auto-response handling to a single tool-approval path.
dotnet/src/Microsoft.Agents.AI.Workflows/Specialized/AIAgentHostExecutor.cs Switches “user input” interception path to tool-approval request/response content.
dotnet/src/Microsoft.Agents.AI.Workflows/AIAgentHostOptions.cs Updates XML docs to reference ToolApprovalRequestContent.
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeMcpToolExecutor.cs Uses tool-approval request/response and migrates MCP result payload to Outputs.
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeFunctionToolExecutor.cs Emits tool-approval requests for function tools that require approval.
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/ObjectModel/InvokeAzureAgentExecutor.cs Treats tool-approval requests as “blocking” content when filtering responses.
dotnet/src/Microsoft.Agents.AI.Workflows.Declarative.Mcp/DefaultMcpToolHandler.cs Renames MCP tool result list from Output to Outputs and updates population logic.
dotnet/src/Microsoft.Agents.AI.OpenAI/Extensions/OpenAIResponseClientExtensions.cs Adds optional model plumbing to agent/chat-client helpers for plain ResponsesClient usage.
dotnet/src/Microsoft.Agents.AI.OpenAI/ChatClient/AsyncStreamingChatCompletionUpdateCollectionResult.cs Suppresses OPENAI001 experimental warnings at file scope.
dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Streaming/FunctionApprovalResponseEventGenerator.cs Renames generator to tool-approval response generator and maps RequestId.
dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/Streaming/FunctionApprovalRequestEventGenerator.cs Renames generator to tool-approval request generator and handles non-function tool calls by skipping.
dotnet/src/Microsoft.Agents.AI.Hosting.OpenAI/Responses/AgentResponseUpdateExtensions.cs Switches streaming event generator selection to tool-approval content types.
dotnet/samples/05-end-to-end/M365Agent/AFAgentApplication.cs Updates sample approval card plumbing to deserialize/handle tool-approval content.
dotnet/samples/05-end-to-end/AgentWebChat/AgentWebChat.Web/OpenAIResponsesAgentClient.cs Uses model-less ResponsesClient and passes model via AsIChatClient(agentName).
dotnet/samples/03-workflows/Agents/GroupChatToolApproval/Program.cs Updates sample to read tool-approval requests and extract function call info via ToolCall.
dotnet/samples/02-agents/ModelContextProtocol/ResponseAgent_Hosted_MCP/Program.cs Updates MCP approval loop to use unified tool-approval request content.
dotnet/samples/02-agents/ModelContextProtocol/FoundryAgent_Hosted_MCP/Program.cs Updates MCP approval loop to use unified tool-approval request content.
dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step12_Middleware/Program.cs Updates middleware example to use tool-approval request content.
dotnet/samples/02-agents/FoundryAgents/FoundryAgents_Step04_UsingFunctionToolsWithApprovals/Program.cs Updates function-approval loop example to use tool-approval request content.
dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs Updates server-side approval translation to tool-approval content types.
dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs Updates client-side approval translation to tool-approval content types.
dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs Updates console client to display/answer tool-approval requests.
dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step05_Conversation/Program.cs Updates agent creation to pass model via AsIChatClient(model) with model-less GetResponsesClient().
dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/Program.cs Updates sample agent instantiation to supply model separately.
dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs Adds optional model parameter and passes it into AsIChatClient(model).
dotnet/samples/02-agents/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Program.cs Updates reasoning sample to pass model through AsIChatClient(model).
dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs Updates middleware example to use tool-approval request content.
dotnet/samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Program.cs Updates function-approval loop example to use tool-approval request content.
dotnet/samples/02-agents/AgentProviders/Agent_With_OpenAIResponses/Program.cs Updates provider example to pass model via .AsAIAgent(model: ...) with model-less GetResponsesClient().
dotnet/Directory.Packages.props Bumps multiple package versions (System., Microsoft.Extensions.AI, OpenAI).
Comments suppressed due to low confidence (2)

dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestRequestAgent.cs:216

  • The Invalid request exception message in ProcessResponse is misleading: it reports it expects ToolApprovalResponseContent when it is actually validating the request pulled from UnservicedRequests. This makes debugging test failures harder; update the message (or remove the redundant type-check since request is already ToolApprovalRequestContent).
    dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs:196
  • ToolApprovalRequestContent can wrap approvals for non-function tool calls (e.g., MCP). This code unconditionally casts request.ToolCall to FunctionCallContent, which will throw InvalidCastException or incorrectly transform non-function approvals. Please guard with if (request.ToolCall is FunctionCallContent functionCall) and skip/handle other tool-call types.
#pragma warning disable MEAI001 // Type is for evaluation purposes only
            if (content is ToolApprovalRequestContent request && request.ToolCall is FunctionCallContent functionCall)
            {
                updatedContents ??= [.. update.Contents];
                var approvalId = request.RequestId;

                var approvalData = new ApprovalRequest
                {
                    ApprovalId = approvalId,
                    FunctionName = functionCall.Name,
                    FunctionArguments = functionCall.Arguments,
                    Message = $"Approve execution of '{functionCall.Name}'?"
                };

Comment on lines +184 to +191
public ToolApprovalResponseContent CreatePairedResponse(ToolApprovalRequestContent request)
{
if (request is not FunctionApprovalRequestContent approvalRequest)
if (request is not ToolApprovalRequestContent approvalRequest)
{
throw new InvalidOperationException($"Invalid request: Expecting {typeof(FunctionApprovalResponseContent)}, got {request.GetType()}");
throw new InvalidOperationException($"Invalid request: Expecting {typeof(ToolApprovalResponseContent)}, got {request.GetType()}");
}

return new FunctionApprovalResponseContent(approvalRequest.Id, true, approvalRequest.FunctionCall);
return new ToolApprovalResponseContent(approvalRequest.RequestId, true, approvalRequest.ToolCall);
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

In CreatePairedResponse, the type-check is redundant (the parameter is already ToolApprovalRequestContent) and the exception message is incorrect: it says it's expecting ToolApprovalResponseContent even though this is validating a request. Please either remove the check or update it to validate the underlying ToolCall type (and fix the message accordingly).

This issue also appears on line 206 of the same file.

Copilot uses AI. Check for mistakes.
if (!this._userInputHandler!.MarkRequestAsHandled(response.RequestId))
{
throw new InvalidOperationException($"No pending UserInputRequest found with id '{response.Id}'.");
throw new InvalidOperationException($"No pending UserInputRequest found with id '{response.RequestId}'.");
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

The thrown error still refers to UserInputRequest even though this handler now processes ToolApprovalResponseContent. Consider updating the message to reference tool approval requests/responses so failures are actionable (e.g., "No pending ToolApprovalRequest found...").

Suggested change
throw new InvalidOperationException($"No pending UserInputRequest found with id '{response.RequestId}'.");
throw new InvalidOperationException($"No pending ToolApprovalRequest found with id '{response.RequestId}'.");

Copilot uses AI. Check for mistakes.
@rogerbarreto rogerbarreto added the blocked external This label indicates whenever an issue or a PR cannot proceed due to an external dependency label Mar 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blocked external This label indicates whenever an issue or a PR cannot proceed due to an external dependency .NET workflows Related to Workflows in agent-framework

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants