From 95887493a582662b92809e603a3df0bd10a52737 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Mon, 23 Feb 2026 13:48:05 +0000 Subject: [PATCH] .NET: Fix FunctionInvocationDelegatingAgent to preserve all AgentRunOptions properties When converting base AgentRunOptions to ChatClientAgentRunOptions, the middleware now preserves AllowBackgroundResponses, ContinuationToken, and AdditionalProperties in addition to ResponseFormat. Added unit test verifying all properties are preserved during the conversion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../FunctionInvocationDelegatingAgent.cs | 8 ++- .../FunctionInvocationDelegatingAgentTests.cs | 54 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs b/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs index bf34dfdeed..13f15ff1e9 100644 --- a/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs @@ -32,7 +32,13 @@ protected override IAsyncEnumerable RunCoreStreamingAsync(I { if (options is null || options.GetType() == typeof(AgentRunOptions)) { - options = new ChatClientAgentRunOptions(); + options = new ChatClientAgentRunOptions() + { + ResponseFormat = options?.ResponseFormat, + AllowBackgroundResponses = options?.AllowBackgroundResponses, + ContinuationToken = options?.ContinuationToken, + AdditionalProperties = options?.AdditionalProperties, + }; } if (options is not ChatClientAgentRunOptions aco) diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs index d54a0644a4..68228155a7 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/FunctionInvocationDelegatingAgentTests.cs @@ -935,6 +935,60 @@ public async Task RunAsync_MiddlewareDoesNotCallNext_FunctionNotExecutedAsync() #endregion + #region Options Preservation Tests + + /// + /// Tests that FunctionInvocationDelegatingAgent preserves all original AgentRunOptions properties + /// when converting base AgentRunOptions to ChatClientAgentRunOptions. + /// + [Fact] + public async Task RunAsync_WithBaseAgentRunOptions_PreservesAllOriginalOptionsAsync() + { + // Arrange + AgentRunOptions? capturedOptions = null; + var responseFormat = ChatResponseFormat.Json; + var additionalProperties = new AdditionalPropertiesDictionary { ["key1"] = "value1" }; + + Mock mockChatClient = new(); + var chatClientAgent = new ChatClientAgent(mockChatClient.Object); + + // Wrap the inner agent in a spy that captures the converted options and returns a dummy response + var spyAgent = new AnonymousDelegatingAIAgent( + chatClientAgent, + runFunc: (messages, session, options, innerAgent, ct) => + { + capturedOptions = options; + return Task.FromResult(new AgentResponse(new ChatResponse(new ChatMessage(ChatRole.Assistant, "test")) { ResponseId = "test" })); + }, + runStreamingFunc: null); + + static ValueTask MiddlewareCallbackAsync(AIAgent agent, FunctionInvocationContext context, Func> next, CancellationToken cancellationToken) + => next(context, cancellationToken); + + var middleware = new FunctionInvocationDelegatingAgent(spyAgent, MiddlewareCallbackAsync); + + var originalOptions = new AgentRunOptions + { + ResponseFormat = responseFormat, + AllowBackgroundResponses = true, + ContinuationToken = ResponseContinuationToken.FromBytes(new byte[] { 1, 2, 3 }), + AdditionalProperties = additionalProperties, + }; + + // Act + await middleware.RunAsync([new(ChatRole.User, "Test")], null, originalOptions, CancellationToken.None); + + // Assert - All original properties were preserved on the converted options + Assert.NotNull(capturedOptions); + Assert.IsType(capturedOptions); + Assert.Same(responseFormat, capturedOptions.ResponseFormat); + Assert.True(capturedOptions.AllowBackgroundResponses); + Assert.Same(originalOptions.ContinuationToken, capturedOptions.ContinuationToken); + Assert.Same(additionalProperties, capturedOptions.AdditionalProperties); + } + + #endregion + /// /// Creates a mock IChatClient with predefined responses for testing. ///