diff --git a/dotnet/src/Agents/Core/ChatCompletionAgent.cs b/dotnet/src/Agents/Core/ChatCompletionAgent.cs index 56c8712ab50f..64d82edb596a 100644 --- a/dotnet/src/Agents/Core/ChatCompletionAgent.cs +++ b/dotnet/src/Agents/Core/ChatCompletionAgent.cs @@ -73,7 +73,7 @@ public override async IAsyncEnumerable> In () => new ChatHistoryAgentThread(), cancellationToken).ConfigureAwait(false); - Kernel kernel = (options?.Kernel ?? this.Kernel).Clone(); + Kernel kernel = options?.Kernel ?? this.Kernel; // Get the context contributions from the AIContextProviders. #pragma warning disable SKEXP0110, SKEXP0130 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. @@ -168,7 +168,7 @@ public override async IAsyncEnumerable new ChatHistoryAgentThread(), cancellationToken).ConfigureAwait(false); - Kernel kernel = (options?.Kernel ?? this.Kernel).Clone(); + Kernel kernel = options?.Kernel ?? this.Kernel; // Get the context contributions from the AIContextProviders. #pragma warning disable SKEXP0110, SKEXP0130 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. diff --git a/dotnet/src/Agents/UnitTests/Core/ChatCompletionAgentTests.cs b/dotnet/src/Agents/UnitTests/Core/ChatCompletionAgentTests.cs index 1f7d2d1e0fb2..7986d530d8ad 100644 --- a/dotnet/src/Agents/UnitTests/Core/ChatCompletionAgentTests.cs +++ b/dotnet/src/Agents/UnitTests/Core/ChatCompletionAgentTests.cs @@ -176,6 +176,46 @@ public async Task VerifyChatCompletionAgentInvocationAsync() Times.Once); } + /// + /// Verify the invocation and response of . + /// + [Fact] + public async Task VerifyChatCompletionAgentInvocationsCanMutateProvidedKernelAsync() + { + // Arrange + Mock mockService = new(); + mockService.Setup( + s => s.GetChatMessageContentsAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())).ReturnsAsync([new(AuthorRole.Assistant, "what?")]); + + var kernel = CreateKernel(mockService.Object); + ChatCompletionAgent agent = + new() + { + Instructions = "test instructions", + Kernel = kernel, + Arguments = [], + }; + + // Act + AgentResponseItem[] result = await agent.InvokeAsync(Array.Empty() as ICollection).ToArrayAsync(); + + // Assert + Assert.Single(result); + + mockService.Verify( + x => + x.GetChatMessageContentsAsync( + It.IsAny(), + It.IsAny(), + kernel, // Use the same kernel instance + It.IsAny()), + Times.Once); + } + /// /// Verify the invocation and response of using . /// @@ -195,7 +235,7 @@ public async Task VerifyChatClientAgentInvocationAsync() { Instructions = "test instructions", Kernel = CreateKernel(mockService.Object), - Arguments = [], + Arguments = new(new PromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }), }; // Act @@ -208,7 +248,7 @@ public async Task VerifyChatClientAgentInvocationAsync() x => x.GetResponseAsync( It.IsAny>(), - It.IsAny(), + It.Is(o => GetKernelFromChatOptions(o) == agent.Kernel), It.IsAny()), Times.Once); } @@ -258,6 +298,52 @@ public async Task VerifyChatCompletionAgentStreamingAsync() Times.Once); } + /// + /// Verify the streaming invocation and response of . + /// + [Fact] + public async Task VerifyChatCompletionAgentStreamingCanMutateProvidedKernelAsync() + { + // Arrange + StreamingChatMessageContent[] returnContent = + [ + new(AuthorRole.Assistant, "wh"), + new(AuthorRole.Assistant, "at?"), + ]; + + Mock mockService = new(); + mockService.Setup( + s => s.GetStreamingChatMessageContentsAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())).Returns(returnContent.ToAsyncEnumerable()); + + var kernel = CreateKernel(mockService.Object); + ChatCompletionAgent agent = + new() + { + Instructions = "test instructions", + Kernel = kernel, + Arguments = [], + }; + + // Act + AgentResponseItem[] result = await agent.InvokeStreamingAsync(Array.Empty() as ICollection).ToArrayAsync(); + + // Assert + Assert.Equal(2, result.Length); + + mockService.Verify( + x => + x.GetStreamingChatMessageContentsAsync( + It.IsAny(), + It.IsAny(), + kernel, // Use the same kernel instance + It.IsAny()), + Times.Once); + } + /// /// Verify the streaming invocation and response of using . /// @@ -283,7 +369,7 @@ public async Task VerifyChatClientAgentStreamingAsync() { Instructions = "test instructions", Kernel = CreateKernel(mockService.Object), - Arguments = [], + Arguments = new(new PromptExecutionSettings() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }), }; // Act @@ -296,7 +382,7 @@ public async Task VerifyChatClientAgentStreamingAsync() x => x.GetStreamingResponseAsync( It.IsAny>(), - It.IsAny(), + It.Is(o => GetKernelFromChatOptions(o) == agent.Kernel), It.IsAny()), Times.Once); } @@ -386,4 +472,25 @@ private static Kernel CreateKernel(IChatClient chatClient) builder.Services.AddSingleton(chatClient); return builder.Build(); } + + /// + /// Gets the Kernel property from ChatOptions using reflection. + /// + /// The ChatOptions instance to extract Kernel from. + /// The Kernel instance if found; otherwise, null. + private static Kernel? GetKernelFromChatOptions(ChatOptions options) + { + // Use reflection to try to get the Kernel property + var kernelProperty = options.GetType().GetProperty("Kernel", + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.NonPublic | + System.Reflection.BindingFlags.Instance); + + if (kernelProperty != null) + { + return kernelProperty.GetValue(options) as Kernel; + } + + return null; + } }