From b150511407c7262a6b81151abe2154e47e1c1c0f Mon Sep 17 00:00:00 2001 From: Jiaming Liang Date: Tue, 21 Oct 2025 11:11:54 +0800 Subject: [PATCH] Respect custom ToolChoice and ParallelToolCallsEnabled options in OpenAIResponseAgent --- .../ResponseCreationOptionsFactory.cs | 10 ++- .../ResponseCreationOptionsFactoryTests.cs | 61 +++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/dotnet/src/Agents/OpenAI/Internal/ResponseCreationOptionsFactory.cs b/dotnet/src/Agents/OpenAI/Internal/ResponseCreationOptionsFactory.cs index de288614ba9e..9dc9b8df6aab 100644 --- a/dotnet/src/Agents/OpenAI/Internal/ResponseCreationOptionsFactory.cs +++ b/dotnet/src/Agents/OpenAI/Internal/ResponseCreationOptionsFactory.cs @@ -60,8 +60,14 @@ internal static ResponseCreationOptions CreateOptions( if (responseTools is not null && responseTools.Any()) { creationOptions.Tools.AddRange(responseTools); - creationOptions.ToolChoice = ResponseToolChoice.CreateAutoChoice(); - creationOptions.ParallelToolCallsEnabled = true; + if (creationOptions.ToolChoice == null) + { + creationOptions.ToolChoice = ResponseToolChoice.CreateAutoChoice(); + } + if (!creationOptions.ParallelToolCallsEnabled.HasValue) + { + creationOptions.ParallelToolCallsEnabled = true; + } } return creationOptions; diff --git a/dotnet/src/Agents/UnitTests/OpenAI/Internal/ResponseCreationOptionsFactoryTests.cs b/dotnet/src/Agents/UnitTests/OpenAI/Internal/ResponseCreationOptionsFactoryTests.cs index e92a4f2fb82a..a32393f48c3b 100644 --- a/dotnet/src/Agents/UnitTests/OpenAI/Internal/ResponseCreationOptionsFactoryTests.cs +++ b/dotnet/src/Agents/UnitTests/OpenAI/Internal/ResponseCreationOptionsFactoryTests.cs @@ -261,6 +261,60 @@ public void CreateOptionsWithNullAgentNameTest() Assert.Equal("UnnamedAgent", options.EndUserId); // Null name should fallback to "UnnamedAgent" } + /// + /// Verify response options creation with kernel plugin and default response options. + /// + [Fact] + public void CreateOptionsWithKernelPluginAndDefaultOptionsTest() + { + // Arrange + var mockAgent = CreateMockAgent("Test Agent", "You are a helpful assistant.", storeEnabled: false); + var mockThread = CreateMockAgentThread(null); + mockAgent.Kernel.Plugins.AddFromObject(new TestPlugin()); + + // Act + var options = ResponseCreationOptionsFactory.CreateOptions(mockAgent, mockThread.Object, null); + + // Assert + Assert.NotNull(options); + Assert.Single(options.Tools); + Assert.NotNull(options.ToolChoice); + Assert.Equal(ResponseToolChoiceKind.Auto, options.ToolChoice.Kind); + Assert.True(options.ParallelToolCallsEnabled); + } + + /// + /// Verify response options creation with kernel plugin and custom response options. + /// + [Fact] + public void CreateOptionsWithKernelPluginAndCustomOptionsTest() + { + // Arrange + var mockAgent = CreateMockAgent("Test Agent", "You are a helpful assistant.", storeEnabled: false); + var mockThread = CreateMockAgentThread(null); + mockAgent.Kernel.Plugins.AddFromObject(new TestPlugin()); + + // Custom invoke options should be respected + var invokeOptions = new OpenAIResponseAgentInvokeOptions + { + ResponseCreationOptions = new ResponseCreationOptions + { + ToolChoice = ResponseToolChoice.CreateNoneChoice(), + ParallelToolCallsEnabled = false + } + }; + + // Act + var options = ResponseCreationOptionsFactory.CreateOptions(mockAgent, mockThread.Object, invokeOptions); + + // Assert + Assert.NotNull(options); + Assert.Single(options.Tools); + Assert.NotNull(options.ToolChoice); + Assert.Equal(ResponseToolChoiceKind.None, options.ToolChoice.Kind); + Assert.False(options.ParallelToolCallsEnabled); + } + private static OpenAIResponseAgent CreateMockAgent(string? name, string? instructions, bool storeEnabled) { var mockClient = new Mock(); @@ -281,4 +335,11 @@ private static Mock CreateMockAgentThread(string? threadId) mockThread.Setup(t => t.Id).Returns(threadId); return mockThread; } + + private sealed class TestPlugin + { + [KernelFunction] + public void TestFunction() + { } + } }