From 8b23f712eeb65baae46d8d88810fa92a53e19bdc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 12:49:47 +0000 Subject: [PATCH 01/15] Initial plan From 322052e0628a5669a6b8c16c15bb0187c6347359 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:05:14 +0000 Subject: [PATCH 02/15] Refactor AIAgent: Make RunAsync and RunStreamingAsync non-abstract, add RunCoreAsync and RunCoreStreamingAsync Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --- .../Program.cs | 4 +- .../src/Microsoft.Agents.AI.A2A/A2AAgent.cs | 4 +- .../AIAgent.cs | 61 ++++++++++++++++++- .../DelegatingAIAgent.cs | 4 +- .../CopilotStudioAgent.cs | 4 +- .../DurableAIAgent.cs | 4 +- .../DurableAIAgentProxy.cs | 4 +- .../EntityAgentWrapper.cs | 4 +- .../PurviewAgent.cs | 4 +- .../WorkflowHostAgent.cs | 8 +-- .../ChatClient/ChatClientAgent.cs | 4 +- .../AIAgentTests.cs | 4 +- .../AggregatorPromptAgentFactoryTests.cs | 4 +- .../BasicStreamingTests.cs | 8 +-- .../ForwardedPropertiesTests.cs | 4 +- .../SharedStateTests.cs | 4 +- ...AGUIEndpointRouteBuilderExtensionsTests.cs | 8 +-- .../TestAgent.cs | 4 +- .../AgentExtensionsTests.cs | 4 +- .../TestAIAgent.cs | 4 +- .../AgentWorkflowBuilderTests.cs | 4 +- .../InProcessExecutionTests.cs | 4 +- .../RepresentationTests.cs | 4 +- .../Sample/06_GroupChat_Workflow.cs | 4 +- .../SpecializedExecutorSmokeTests.cs | 4 +- .../TestEchoAgent.cs | 4 +- 26 files changed, 115 insertions(+), 58 deletions(-) diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs index 8f1039251d..33b62aa582 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs @@ -34,7 +34,7 @@ public override AgentThread GetNewThread() public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) => new CustomAgentThread(serializedThread, jsonSerializerOptions); - public override async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override async Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { // Create a thread if the user didn't supply one. thread ??= this.GetNewThread(); @@ -58,7 +58,7 @@ public override async Task RunAsync(IEnumerable m }; } - public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { // Create a thread if the user didn't supply one. thread ??= this.GetNewThread(); diff --git a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs index bdb1f72928..69feaf70a5 100644 --- a/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs @@ -71,7 +71,7 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json => new A2AAgentThread(serializedThread, jsonSerializerOptions); /// - public override async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override async Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { _ = Throw.IfNull(messages); @@ -134,7 +134,7 @@ public override async Task RunAsync(IEnumerable m } /// - public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { _ = Throw.IfNull(messages); diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs index eba6f84687..f8a77608ba 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs @@ -221,6 +221,35 @@ public Task RunAsync( /// A task that represents the asynchronous operation. The task result contains an with the agent's output. /// /// + /// This method delegates to to perform the actual agent invocation. It handles collections of messages, + /// allowing for complex conversational scenarios including multi-turn interactions, function calls, and + /// context-rich conversations. + /// + /// + /// The messages are processed in the order provided and become part of the conversation history. + /// The agent's response will also be added to if one is provided. + /// + /// + public virtual Task RunAsync( + IEnumerable messages, + AgentThread? thread = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) => + this.RunCoreAsync(messages, thread, options, cancellationToken); + + /// + /// Core implementation of the agent invocation logic with a collection of chat messages. + /// + /// The collection of messages to send to the agent for processing. + /// + /// The conversation thread to use for this invocation. If , a new thread will be created. + /// The thread will be updated with the input messages and any response messages generated during invocation. + /// + /// Optional configuration parameters for controlling the agent's invocation behavior. + /// The to monitor for cancellation requests. The default is . + /// A task that represents the asynchronous operation. The task result contains an with the agent's output. + /// + /// /// This is the primary invocation method that implementations must override. It handles collections of messages, /// allowing for complex conversational scenarios including multi-turn interactions, function calls, and /// context-rich conversations. @@ -230,7 +259,7 @@ public Task RunAsync( /// The agent's response will also be added to if one is provided. /// /// - public abstract Task RunAsync( + protected abstract Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -315,6 +344,34 @@ public IAsyncEnumerable RunStreamingAsync( /// An asynchronous enumerable of instances representing the streaming response. /// /// + /// This method delegates to to perform the actual streaming invocation. It provides real-time + /// updates as the agent processes the input and generates its response, enabling more responsive user experiences. + /// + /// + /// Each represents a portion of the complete response, allowing consumers + /// to display partial results, implement progressive loading, or provide immediate feedback to users. + /// + /// + public virtual IAsyncEnumerable RunStreamingAsync( + IEnumerable messages, + AgentThread? thread = null, + AgentRunOptions? options = null, + CancellationToken cancellationToken = default) => + this.RunCoreStreamingAsync(messages, thread, options, cancellationToken); + + /// + /// Core implementation of the agent streaming invocation logic with a collection of chat messages. + /// + /// The collection of messages to send to the agent for processing. + /// + /// The conversation thread to use for this invocation. If , a new thread will be created. + /// The thread will be updated with the input messages and any response updates generated during invocation. + /// + /// Optional configuration parameters for controlling the agent's invocation behavior. + /// The to monitor for cancellation requests. The default is . + /// An asynchronous enumerable of instances representing the streaming response. + /// + /// /// This is the primary streaming invocation method that implementations must override. It provides real-time /// updates as the agent processes the input and generates its response, enabling more responsive user experiences. /// @@ -323,7 +380,7 @@ public IAsyncEnumerable RunStreamingAsync( /// to display partial results, implement progressive loading, or provide immediate feedback to users. /// /// - public abstract IAsyncEnumerable RunStreamingAsync( + protected abstract IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs index 353c82c996..f0412b7a95 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs @@ -81,7 +81,7 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json => this.InnerAgent.DeserializeThread(serializedThread, jsonSerializerOptions); /// - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -89,7 +89,7 @@ public override Task RunAsync( => this.InnerAgent.RunAsync(messages, thread, options, cancellationToken); /// - public override IAsyncEnumerable RunStreamingAsync( + protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgent.cs b/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgent.cs index 6ca2f38d3d..203bab21ed 100644 --- a/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.CopilotStudio/CopilotStudioAgent.cs @@ -58,7 +58,7 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json => new CopilotStudioAgentThread(serializedThread, jsonSerializerOptions); /// - public override async Task RunAsync( + protected override async Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -96,7 +96,7 @@ public override async Task RunAsync( } /// - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs index 021c8f22c7..2fbb976a48 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs @@ -63,7 +63,7 @@ public override AgentThread DeserializeThread( /// Thrown when the agent has not been registered. /// Thrown when the provided thread is not valid for a durable agent. /// Thrown when cancellation is requested (cancellation is not supported for durable agents). - public override async Task RunAsync( + protected override async Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -128,7 +128,7 @@ public override async Task RunAsync( /// Optional run options. /// The cancellation token. /// A streaming response enumerable. - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgentProxy.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgentProxy.cs index 58f9598a7e..ecff2d5c90 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgentProxy.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgentProxy.cs @@ -23,7 +23,7 @@ public override AgentThread GetNewThread() return new DurableAgentThread(AgentSessionId.WithRandomKey(this.Name!)); } - public override async Task RunAsync( + protected override async Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -70,7 +70,7 @@ public override async Task RunAsync( return await agentRunHandle.ReadAgentResponseAsync(cancellationToken); } - public override IAsyncEnumerable RunStreamingAsync( + protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs index 34c9208967..07da951c50 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs @@ -21,7 +21,7 @@ internal sealed class EntityAgentWrapper( // The ID of the agent is always the entity ID. public override string Id => this._entityContext.Id.ToString(); - public override async Task RunAsync( + protected override async Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -37,7 +37,7 @@ public override async Task RunAsync( return response; } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAgent.cs b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAgent.cs index fd2a1950e9..6907fe8889 100644 --- a/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Purview/PurviewAgent.cs @@ -42,13 +42,13 @@ public override AgentThread GetNewThread() } /// - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { return this._purviewWrapper.ProcessAgentContentAsync(messages, thread, options, this._innerAgent, cancellationToken); } /// - public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { var response = await this._purviewWrapper.ProcessAgentContentAsync(messages, thread, options, this._innerAgent, cancellationToken).ConfigureAwait(false); foreach (var update in response.ToAgentRunResponseUpdates()) diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostAgent.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostAgent.cs index 98dc5903bf..68d438e036 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostAgent.cs @@ -79,8 +79,8 @@ private async ValueTask UpdateThreadAsync(IEnumerable RunAsync( + protected override async + Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -101,8 +101,8 @@ Task RunAsync( return merger.ComputeMerged(workflowThread.LastResponseId!, this.Id, this.Name); } - public override async - IAsyncEnumerable RunStreamingAsync( + protected override async + IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs index df7477241c..16fbf791b9 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs @@ -149,7 +149,7 @@ public ChatClientAgent(IChatClient chatClient, ChatClientAgentOptions? options, internal ChatOptions? ChatOptions => this._agentOptions?.ChatOptions; /// - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -193,7 +193,7 @@ private static IChatClient ApplyRunOptionsTransformations(AgentRunOptions? optio } /// - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs index 5111a97ad1..d7e7a1845b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs @@ -350,14 +350,14 @@ public override AgentThread GetNewThread() public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) => throw new NotImplementedException(); - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public override IAsyncEnumerable RunStreamingAsync( + protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AggregatorPromptAgentFactoryTests.cs b/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AggregatorPromptAgentFactoryTests.cs index d20bd9be00..09ee72504a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AggregatorPromptAgentFactoryTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Declarative.UnitTests/AggregatorPromptAgentFactoryTests.cs @@ -76,12 +76,12 @@ public override AgentThread GetNewThread() throw new NotImplementedException(); } - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/BasicStreamingTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/BasicStreamingTests.cs index 5bc4e8afad..af9c78fb93 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/BasicStreamingTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/BasicStreamingTests.cs @@ -296,7 +296,7 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json return new FakeInMemoryAgentThread(serializedThread, jsonSerializerOptions); } - public override async Task RunAsync( + protected override async Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -311,7 +311,7 @@ public override async Task RunAsync( return updates.ToAgentRunResponse(); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -370,7 +370,7 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json return new FakeInMemoryAgentThread(serializedThread, jsonSerializerOptions); } - public override async Task RunAsync( + protected override async Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -385,7 +385,7 @@ public override async Task RunAsync( return updates.ToAgentRunResponse(); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs index df8caea214..335a2b3638 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs @@ -303,12 +303,12 @@ public FakeForwardedPropsAgent() public JsonElement ReceivedForwardedProperties { get; private set; } - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { return this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs index c96f2d92d0..c642704a72 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs @@ -342,12 +342,12 @@ internal sealed class FakeStateAgent : AIAgent { public override string? Description => "Agent for state testing"; - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { return this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs index 78a3048747..9b89ba0b2b 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/AGUIEndpointRouteBuilderExtensionsTests.cs @@ -430,12 +430,12 @@ private sealed class MultiResponseAgent : AIAgent public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) => new TestInMemoryAgentThread(serializedThread, jsonSerializerOptions); - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -519,12 +519,12 @@ private sealed class TestAgent : AIAgent public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) => new TestInMemoryAgentThread(serializedThread, jsonSerializerOptions); - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/TestAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/TestAgent.cs index b0ad7ec0fe..e6824a2dd4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/TestAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/TestAgent.cs @@ -17,13 +17,13 @@ public override AgentThread DeserializeThread( JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) => new DummyAgentThread(); - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => Task.FromResult(new AgentRunResponse([.. messages])); - public override IAsyncEnumerable RunStreamingAsync( + protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs index f2b2bcfd6a..d039c95652 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs @@ -337,7 +337,7 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json public CancellationToken LastCancellationToken { get; private set; } public int RunAsyncCallCount { get; private set; } - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -355,7 +355,7 @@ public override Task RunAsync( return Task.FromResult(this._responseToReturn!); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/TestAIAgent.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/TestAIAgent.cs index fb00973c78..3d2cdff868 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/TestAIAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/TestAIAgent.cs @@ -30,10 +30,10 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json public override AgentThread GetNewThread() => this.GetNewThreadFunc(); - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => this.RunAsyncFunc(messages, thread, options, cancellationToken); - public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => this.RunStreamingAsyncFunc(messages, thread, options, cancellationToken); public override object? GetService(Type serviceType, object? serviceKey = null) => diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs index 0437fc7695..6198303270 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs @@ -141,11 +141,11 @@ public override AgentThread GetNewThread() public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) => new DoubleEchoAgentThread(); - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { await Task.Yield(); diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/InProcessExecutionTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/InProcessExecutionTests.cs index e134f10aa7..b3e53da6f8 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/InProcessExecutionTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/InProcessExecutionTests.cs @@ -149,7 +149,7 @@ public SimpleTestAgent(string name) public override AgentThread DeserializeThread(System.Text.Json.JsonElement serializedThread, System.Text.Json.JsonSerializerOptions? jsonSerializerOptions = null) => new SimpleTestAgentThread(); - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -160,7 +160,7 @@ public override Task RunAsync( return Task.FromResult(new AgentRunResponse(responseMessage)); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RepresentationTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RepresentationTests.cs index 9cf460e658..6ec2e9ecdb 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RepresentationTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/RepresentationTests.cs @@ -30,10 +30,10 @@ public override AgentThread GetNewThread() public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) => throw new NotImplementedException(); - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotImplementedException(); } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs index a0e57006ed..db527fdfce 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs @@ -66,7 +66,7 @@ public override AgentThread GetNewThread() public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) => new HelloAgentThread(); - public override async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override async Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { IEnumerable update = [ await this.RunStreamingAsync(messages, thread, options, cancellationToken) @@ -76,7 +76,7 @@ await this.RunStreamingAsync(messages, thread, options, cancellationToken) return update.ToAgentRunResponse(); } - public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { yield return new(ChatRole.Assistant, "Hello World!") { diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs index b93d7862d5..5838da1051 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs @@ -62,14 +62,14 @@ public static TestAIAgent FromStrings(params string[] messages) => public List Messages { get; } = Validate(messages) ?? []; - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => Task.FromResult(new AgentRunResponse(this.Messages) { AgentId = this.Id, ResponseId = Guid.NewGuid().ToString("N") }); - public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { string responseId = Guid.NewGuid().ToString("N"); foreach (ChatMessage message in this.Messages) diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs index 4966585211..a0b4c9cd13 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/TestEchoAgent.cs @@ -60,7 +60,7 @@ protected virtual IEnumerable GetEpilogueMessages(AgentRunOptions? return []; } - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { AgentRunResponse result = new(this.EchoMessages(messages, thread, options).ToList()) @@ -73,7 +73,7 @@ public override Task RunAsync(IEnumerable message return Task.FromResult(result); } - public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + protected override async IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { string responseId = Guid.NewGuid().ToString("N"); From dc987edabc98251fa4afa1617066414f9e08cf4c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:10:52 +0000 Subject: [PATCH 03/15] Fix infinite recursion in test implementations Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --- .../ForwardedPropertiesTests.cs | 2 +- .../SharedStateTests.cs | 2 +- .../Sample/06_GroupChat_Workflow.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs index 335a2b3638..1777ff456a 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/ForwardedPropertiesTests.cs @@ -305,7 +305,7 @@ public FakeForwardedPropsAgent() protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); + return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); } protected override async IAsyncEnumerable RunCoreStreamingAsync( diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs index c642704a72..df51d1cbc4 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/SharedStateTests.cs @@ -344,7 +344,7 @@ internal sealed class FakeStateAgent : AIAgent protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); + return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); } protected override async IAsyncEnumerable RunCoreStreamingAsync( diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs index db527fdfce..1827c550e8 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs @@ -69,7 +69,7 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json protected override async Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { IEnumerable update = [ - await this.RunStreamingAsync(messages, thread, options, cancellationToken) + await this.RunCoreStreamingAsync(messages, thread, options, cancellationToken) .SingleAsync(cancellationToken) .ConfigureAwait(false)]; From cef4fa85dbfcefe06bb420f490d5f6869258cd77 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:30:08 +0000 Subject: [PATCH 04/15] Make RunAsync and RunStreamingAsync non-virtual as requested Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --- .../AIAgent.cs | 4 +- .../AIAgentTests.cs | 113 ++++++++++-------- .../DelegatingAIAgentTests.cs | 37 ++++-- 3 files changed, 88 insertions(+), 66 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs index f8a77608ba..6b5467181f 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs @@ -230,7 +230,7 @@ public Task RunAsync( /// The agent's response will also be added to if one is provided. /// /// - public virtual Task RunAsync( + public Task RunAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -352,7 +352,7 @@ public IAsyncEnumerable RunStreamingAsync( /// to display partial results, implement progressive loading, or provide immediate feedback to users. /// /// - public virtual IAsyncEnumerable RunStreamingAsync( + public IAsyncEnumerable RunStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs index d7e7a1845b..f14b30a67f 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AIAgentTests.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.AI; using Moq; +using Moq.Protected; namespace Microsoft.Agents.AI.Abstractions.UnitTests; @@ -33,18 +34,20 @@ public AIAgentTests() this._agentMock = new Mock { CallBase = true }; this._agentMock - .Setup(x => x.RunAsync( - It.IsAny>(), - this._agentThreadMock.Object, - It.IsAny(), - It.IsAny())) + .Protected() + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.IsAny(), + ItExpr.IsAny()) .ReturnsAsync(this._invokeResponse); this._agentMock - .Setup(x => x.RunStreamingAsync( - It.IsAny>(), - this._agentThreadMock.Object, - It.IsAny(), - It.IsAny())) + .Protected() + .Setup>("RunCoreStreamingAsync", + ItExpr.IsAny>(), + ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.IsAny(), + ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(this._invokeStreamingResponses)); } @@ -64,13 +67,14 @@ public async Task InvokeWithoutMessageCallsMockedInvokeWithEmptyArrayAsync() Assert.Equal(this._invokeResponse, response); // Verify that the mocked method was called with the expected parameters - this._agentMock.Verify( - x => x.RunAsync( - It.Is>(messages => messages.Count == 0), - this._agentThreadMock.Object, - options, - cancellationToken), - Times.Once); + this._agentMock + .Protected() + .Verify>("RunCoreAsync", + Times.Once(), + ItExpr.Is>(messages => !messages.Any()), + ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(o => o == options), + ItExpr.Is(ct => ct == cancellationToken)); } /// @@ -90,13 +94,14 @@ public async Task InvokeWithStringMessageCallsMockedInvokeWithMessageInCollectio Assert.Equal(this._invokeResponse, response); // Verify that the mocked method was called with the expected parameters - this._agentMock.Verify( - x => x.RunAsync( - It.Is>(messages => messages.Count == 1 && messages.First().Text == Message), - this._agentThreadMock.Object, - options, - cancellationToken), - Times.Once); + this._agentMock + .Protected() + .Verify>("RunCoreAsync", + Times.Once(), + ItExpr.Is>(messages => messages.Count() == 1 && messages.First().Text == Message), + ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(o => o == options), + ItExpr.Is(ct => ct == cancellationToken)); } /// @@ -116,13 +121,14 @@ public async Task InvokeWithSingleMessageCallsMockedInvokeWithMessageInCollectio Assert.Equal(this._invokeResponse, response); // Verify that the mocked method was called with the expected parameters - this._agentMock.Verify( - x => x.RunAsync( - It.Is>(messages => messages.Count == 1 && messages.First() == message), - this._agentThreadMock.Object, - options, - cancellationToken), - Times.Once); + this._agentMock + .Protected() + .Verify>("RunCoreAsync", + Times.Once(), + ItExpr.Is>(messages => messages.Count() == 1 && messages.First() == message), + ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(o => o == options), + ItExpr.Is(ct => ct == cancellationToken)); } /// @@ -144,13 +150,14 @@ public async Task InvokeStreamingWithoutMessageCallsMockedInvokeWithEmptyArrayAs } // Verify that the mocked method was called with the expected parameters - this._agentMock.Verify( - x => x.RunStreamingAsync( - It.Is>(messages => messages.Count == 0), - this._agentThreadMock.Object, - options, - cancellationToken), - Times.Once); + this._agentMock + .Protected() + .Verify>("RunCoreStreamingAsync", + Times.Once(), + ItExpr.Is>(messages => !messages.Any()), + ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(o => o == options), + ItExpr.Is(ct => ct == cancellationToken)); } /// @@ -173,13 +180,14 @@ public async Task InvokeStreamingWithStringMessageCallsMockedInvokeWithMessageIn } // Verify that the mocked method was called with the expected parameters - this._agentMock.Verify( - x => x.RunStreamingAsync( - It.Is>(messages => messages.Count == 1 && messages.First().Text == Message), - this._agentThreadMock.Object, - options, - cancellationToken), - Times.Once); + this._agentMock + .Protected() + .Verify>("RunCoreStreamingAsync", + Times.Once(), + ItExpr.Is>(messages => messages.Count() == 1 && messages.First().Text == Message), + ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(o => o == options), + ItExpr.Is(ct => ct == cancellationToken)); } /// @@ -202,13 +210,14 @@ public async Task InvokeStreamingWithSingleMessageCallsMockedInvokeWithMessageIn } // Verify that the mocked method was called with the expected parameters - this._agentMock.Verify( - x => x.RunStreamingAsync( - It.Is>(messages => messages.Count == 1 && messages.First() == message), - this._agentThreadMock.Object, - options, - cancellationToken), - Times.Once); + this._agentMock + .Protected() + .Verify>("RunCoreStreamingAsync", + Times.Once(), + ItExpr.Is>(messages => messages.Count() == 1 && messages.First() == message), + ItExpr.Is(t => t == this._agentThreadMock.Object), + ItExpr.Is(o => o == options), + ItExpr.Is(ct => ct == cancellationToken)); } [Fact] diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs index 4dca99a77c..6274daa443 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/DelegatingAIAgentTests.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.AI; using Moq; +using Moq.Protected; namespace Microsoft.Agents.AI.Abstractions.UnitTests; @@ -37,19 +38,21 @@ public DelegatingAIAgentTests() this._innerAgentMock.Setup(x => x.GetNewThread()).Returns(this._testThread); this._innerAgentMock - .Setup(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + .Protected() + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .ReturnsAsync(this._testResponse); this._innerAgentMock - .Setup(x => x.RunStreamingAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + .Protected() + .Setup>("RunCoreStreamingAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(this._testStreamingResponses)); this._delegatingAgent = new TestDelegatingAIAgent(this._innerAgentMock.Object); @@ -158,7 +161,12 @@ public async Task RunAsyncDefaultsToInnerAgentAsync() var innerAgentMock = new Mock(); innerAgentMock - .Setup(x => x.RunAsync(expectedMessages, expectedThread, expectedOptions, expectedCancellationToken)) + .Protected() + .Setup>("RunCoreAsync", + ItExpr.Is>(m => m == expectedMessages), + ItExpr.Is(t => t == expectedThread), + ItExpr.Is(o => o == expectedOptions), + ItExpr.Is(ct => ct == expectedCancellationToken)) .Returns(expectedResult.Task); var delegatingAgent = new TestDelegatingAIAgent(innerAgentMock.Object); @@ -192,7 +200,12 @@ public async Task RunStreamingAsyncDefaultsToInnerAgentAsync() var innerAgentMock = new Mock(); innerAgentMock - .Setup(x => x.RunStreamingAsync(expectedMessages, expectedThread, expectedOptions, expectedCancellationToken)) + .Protected() + .Setup>("RunCoreStreamingAsync", + ItExpr.Is>(m => m == expectedMessages), + ItExpr.Is(t => t == expectedThread), + ItExpr.Is(o => o == expectedOptions), + ItExpr.Is(ct => ct == expectedCancellationToken)) .Returns(ToAsyncEnumerableAsync(expectedResults)); var delegatingAgent = new TestDelegatingAIAgent(innerAgentMock.Object); From 6f8fb49163e4dc74701dc50e94c19bb1ca128d41 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:39:27 +0000 Subject: [PATCH 05/15] Fix DelegatingAIAgent subclasses to use RunCoreAsync/RunCoreStreamingAsync Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --- dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs | 4 ++-- .../Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs | 4 ++-- dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs | 4 ++-- dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs index 21fbfda639..8e24d7f1b5 100644 --- a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs @@ -85,7 +85,7 @@ public AnonymousDelegatingAIAgent( } /// - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, @@ -132,7 +132,7 @@ await this._sharedFunc( } /// - public override IAsyncEnumerable RunStreamingAsync( + protected override IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs b/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs index 7eefcebc55..2463b266c7 100644 --- a/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/FunctionInvocationDelegatingAgent.cs @@ -21,10 +21,10 @@ internal FunctionInvocationDelegatingAgent(AIAgent innerAgent, Func RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => this.InnerAgent.RunAsync(messages, thread, this.AgentRunOptionsWithFunctionMiddleware(options), cancellationToken); - public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => this.InnerAgent.RunStreamingAsync(messages, thread, this.AgentRunOptionsWithFunctionMiddleware(options), cancellationToken); // Decorate options to add the middleware function diff --git a/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs b/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs index b986e58bae..7f43ea09df 100644 --- a/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs @@ -55,7 +55,7 @@ public JsonSerializerOptions JsonSerializerOptions } /// - public override async Task RunAsync( + protected override async Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { if (this._logger.IsEnabled(LogLevel.Debug)) @@ -101,7 +101,7 @@ public override async Task RunAsync( } /// - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (this._logger.IsEnabled(LogLevel.Debug)) diff --git a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs index 7cd3c27b70..e0741bfed3 100644 --- a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs @@ -78,7 +78,7 @@ public bool EnableSensitiveData } /// - public override async Task RunAsync( + protected override async Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { ChatOptions co = new ForwardedOptions(options, thread, Activity.Current); @@ -89,7 +89,7 @@ public override async Task RunAsync( } /// - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { ChatOptions co = new ForwardedOptions(options, thread, Activity.Current); From 95bfe14e3bb459487b52a655f3113e50c5634970 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:43:06 +0000 Subject: [PATCH 06/15] Fix XML documentation references in AnonymousDelegatingAIAgent Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --- .../AnonymousDelegatingAIAgent.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs index 8e24d7f1b5..c30266cbaf 100644 --- a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs @@ -17,18 +17,18 @@ namespace Microsoft.Agents.AI; /// internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent { - /// The delegate to use as the implementation of . + /// The delegate to use as the implementation of RunAsync. private readonly Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, Task>? _runFunc; - /// The delegate to use as the implementation of . + /// The delegate to use as the implementation of RunStreamingAsync. /// - /// When non-, this delegate is used as the implementation of and + /// When non-, this delegate is used as the implementation of RunStreamingAsync and /// will be invoked with the same arguments as the method itself. - /// When , will delegate directly to the inner agent. + /// When , RunStreamingAsync will delegate directly to the inner agent. /// private readonly Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, IAsyncEnumerable>? _runStreamingFunc; - /// The delegate to use as the implementation of both and . + /// The delegate to use as the implementation of both RunAsync and RunStreamingAsync. private readonly Func, AgentThread?, AgentRunOptions?, Func, AgentThread?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task>? _sharedFunc; /// @@ -36,7 +36,7 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent /// /// The inner agent. /// - /// A delegate that provides the implementation for both and . + /// A delegate that provides the implementation for both RunAsync and RunStreamingAsync. /// In addition to the arguments for the operation, it's provided with a delegate to the inner agent that should be /// used to perform the operation on the inner agent. It will handle both the non-streaming and streaming cases. /// @@ -61,13 +61,13 @@ public AnonymousDelegatingAIAgent( /// /// The inner agent. /// - /// A delegate that provides the implementation for . When , - /// must be non-null, and the implementation of + /// A delegate that provides the implementation for RunAsync. When , + /// must be non-null, and the implementation of RunAsync /// will use for the implementation. /// /// - /// A delegate that provides the implementation for . When , - /// must be non-null, and the implementation of + /// A delegate that provides the implementation for RunStreamingAsync. When , + /// must be non-null, and the implementation of RunStreamingAsync /// will use for the implementation. /// /// is . From 04fdb4ca0248501fde220fa951e39c8974f0d12e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 14:05:55 +0000 Subject: [PATCH 07/15] Restore tags with proper qualified signatures in AnonymousDelegatingAIAgent Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --- .../AnonymousDelegatingAIAgent.cs | 20 ++--- .../AnonymousDelegatingAIAgentTests.cs | 75 +++++++++++-------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs index c30266cbaf..57735d978e 100644 --- a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs @@ -17,18 +17,18 @@ namespace Microsoft.Agents.AI; /// internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent { - /// The delegate to use as the implementation of RunAsync. + /// The delegate to use as the implementation of . private readonly Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, Task>? _runFunc; - /// The delegate to use as the implementation of RunStreamingAsync. + /// The delegate to use as the implementation of . /// - /// When non-, this delegate is used as the implementation of RunStreamingAsync and + /// When non-, this delegate is used as the implementation of and /// will be invoked with the same arguments as the method itself. - /// When , RunStreamingAsync will delegate directly to the inner agent. + /// When , will delegate directly to the inner agent. /// private readonly Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, IAsyncEnumerable>? _runStreamingFunc; - /// The delegate to use as the implementation of both RunAsync and RunStreamingAsync. + /// The delegate to use as the implementation of both and . private readonly Func, AgentThread?, AgentRunOptions?, Func, AgentThread?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task>? _sharedFunc; /// @@ -36,7 +36,7 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent /// /// The inner agent. /// - /// A delegate that provides the implementation for both RunAsync and RunStreamingAsync. + /// A delegate that provides the implementation for both and . /// In addition to the arguments for the operation, it's provided with a delegate to the inner agent that should be /// used to perform the operation on the inner agent. It will handle both the non-streaming and streaming cases. /// @@ -61,13 +61,13 @@ public AnonymousDelegatingAIAgent( /// /// The inner agent. /// - /// A delegate that provides the implementation for RunAsync. When , - /// must be non-null, and the implementation of RunAsync + /// A delegate that provides the implementation for . When , + /// must be non-null, and the implementation of /// will use for the implementation. /// /// - /// A delegate that provides the implementation for RunStreamingAsync. When , - /// must be non-null, and the implementation of RunStreamingAsync + /// A delegate that provides the implementation for . When , + /// must be non-null, and the implementation of /// will use for the implementation. /// /// is . diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AnonymousDelegatingAIAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AnonymousDelegatingAIAgentTests.cs index 369ab1ad4f..4e91fc1430 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AnonymousDelegatingAIAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AnonymousDelegatingAIAgentTests.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.AI; using Moq; +using Moq.Protected; namespace Microsoft.Agents.AI.UnitTests; @@ -35,18 +36,22 @@ public AnonymousDelegatingAIAgentTests() new AgentRunResponseUpdate(ChatRole.Assistant, "Response 2") ]; - this._innerAgentMock.Setup(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + this._innerAgentMock + .Protected() + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .ReturnsAsync(this._testResponse); - this._innerAgentMock.Setup(x => x.RunStreamingAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + this._innerAgentMock + .Protected() + .Setup>("RunCoreStreamingAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(this._testStreamingResponses)); } @@ -184,11 +189,14 @@ public async Task RunAsync_WithSharedFunc_ContextPropagatedAsync() Assert.Same(this._testOptions, capturedOptions); Assert.Equal(expectedCancellationToken, capturedCancellationToken); - this._innerAgentMock.Verify(x => x.RunAsync( - this._testMessages, - this._testThread, - this._testOptions, - expectedCancellationToken), Times.Once); + this._innerAgentMock + .Protected() + .Verify>("RunCoreAsync", + Times.Once(), + ItExpr.Is>(m => m == this._testMessages), + ItExpr.Is(t => t == this._testThread), + ItExpr.Is(o => o == this._testOptions), + ItExpr.Is(ct => ct == expectedCancellationToken)); } /// @@ -458,11 +466,13 @@ public async Task AsyncLocalContext_MaintainedAcrossDelegatesAsync() capturedValue = asyncLocal.Value; }); - this._innerAgentMock.Setup(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + this._innerAgentMock + .Protected() + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .Returns(() => { // Verify AsyncLocal value is available in inner agent call @@ -926,11 +936,13 @@ public async Task AIAgentBuilder_Use_CancellationTokenPropagation_WorksCorrectly var capturedTokens = new List(); // Setup mock to throw OperationCanceledException when cancelled token is used - this._innerAgentMock.Setup(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.Is(ct => ct.IsCancellationRequested))) + this._innerAgentMock + .Protected() + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.Is(ct => ct.IsCancellationRequested)) .ThrowsAsync(new OperationCanceledException()); var agent = new AIAgentBuilder(this._innerAgentMock.Object) @@ -993,11 +1005,14 @@ public async Task AIAgentBuilder_Use_MiddlewareShortCircuits_InnerAgentNotCalled Assert.Equal(expectedOrder, executionOrder); // Verify inner agent was never called - this._innerAgentMock.Verify(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny()), Times.Never); + this._innerAgentMock + .Protected() + .Verify>("RunCoreAsync", + Times.Never(), + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()); } #endregion From c1ed679a54acdf1ce3f2b3b66e5e812e537b0080 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 14:43:20 +0000 Subject: [PATCH 08/15] Rollback unnecessary XML documentation changes in AnonymousDelegatingAIAgent Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --- .../AnonymousDelegatingAIAgent.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs index 57735d978e..4456cbc74b 100644 --- a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved + using System; using System.Collections.Generic; using System.Diagnostics; @@ -17,18 +19,18 @@ namespace Microsoft.Agents.AI; /// internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent { - /// The delegate to use as the implementation of . + /// The delegate to use as the implementation of . private readonly Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, Task>? _runFunc; - /// The delegate to use as the implementation of . + /// The delegate to use as the implementation of . /// - /// When non-, this delegate is used as the implementation of and + /// When non-, this delegate is used as the implementation of and /// will be invoked with the same arguments as the method itself. - /// When , will delegate directly to the inner agent. + /// When , will delegate directly to the inner agent. /// private readonly Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, IAsyncEnumerable>? _runStreamingFunc; - /// The delegate to use as the implementation of both and . + /// The delegate to use as the implementation of both and . private readonly Func, AgentThread?, AgentRunOptions?, Func, AgentThread?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task>? _sharedFunc; /// @@ -36,7 +38,7 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent /// /// The inner agent. /// - /// A delegate that provides the implementation for both and . + /// A delegate that provides the implementation for both and . /// In addition to the arguments for the operation, it's provided with a delegate to the inner agent that should be /// used to perform the operation on the inner agent. It will handle both the non-streaming and streaming cases. /// @@ -61,13 +63,13 @@ public AnonymousDelegatingAIAgent( /// /// The inner agent. /// - /// A delegate that provides the implementation for . When , - /// must be non-null, and the implementation of + /// A delegate that provides the implementation for . When , + /// must be non-null, and the implementation of /// will use for the implementation. /// /// - /// A delegate that provides the implementation for . When , - /// must be non-null, and the implementation of + /// A delegate that provides the implementation for . When , + /// must be non-null, and the implementation of /// will use for the implementation. /// /// is . From df1ea8d5c01f8f00257e7dc67e62c7cbb9913376 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 16:21:50 +0000 Subject: [PATCH 09/15] Remove pragma and update crefs to RunCoreAsync/RunCoreStreamingAsync Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --- .../AnonymousDelegatingAIAgent.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs index 4456cbc74b..542bafdbf4 100644 --- a/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs @@ -1,7 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. -#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved - using System; using System.Collections.Generic; using System.Diagnostics; @@ -19,18 +17,18 @@ namespace Microsoft.Agents.AI; /// internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent { - /// The delegate to use as the implementation of . + /// The delegate to use as the implementation of . private readonly Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, Task>? _runFunc; - /// The delegate to use as the implementation of . + /// The delegate to use as the implementation of . /// - /// When non-, this delegate is used as the implementation of and + /// When non-, this delegate is used as the implementation of and /// will be invoked with the same arguments as the method itself. - /// When , will delegate directly to the inner agent. + /// When , will delegate directly to the inner agent. /// private readonly Func, AgentThread?, AgentRunOptions?, AIAgent, CancellationToken, IAsyncEnumerable>? _runStreamingFunc; - /// The delegate to use as the implementation of both and . + /// The delegate to use as the implementation of both and . private readonly Func, AgentThread?, AgentRunOptions?, Func, AgentThread?, AgentRunOptions?, CancellationToken, Task>, CancellationToken, Task>? _sharedFunc; /// @@ -38,7 +36,7 @@ internal sealed class AnonymousDelegatingAIAgent : DelegatingAIAgent /// /// The inner agent. /// - /// A delegate that provides the implementation for both and . + /// A delegate that provides the implementation for both and . /// In addition to the arguments for the operation, it's provided with a delegate to the inner agent that should be /// used to perform the operation on the inner agent. It will handle both the non-streaming and streaming cases. /// @@ -63,13 +61,13 @@ public AnonymousDelegatingAIAgent( /// /// The inner agent. /// - /// A delegate that provides the implementation for . When , - /// must be non-null, and the implementation of + /// A delegate that provides the implementation for . When , + /// must be non-null, and the implementation of /// will use for the implementation. /// /// - /// A delegate that provides the implementation for . When , - /// must be non-null, and the implementation of + /// A delegate that provides the implementation for . When , + /// must be non-null, and the implementation of /// will use for the implementation. /// /// is . From c9b93f463638e339bdab5cad635490d057a662b5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 10 Dec 2025 16:50:28 +0000 Subject: [PATCH 10/15] Fix EntityAgentWrapper to call base.RunCoreAsync/RunCoreStreamingAsync Co-authored-by: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com> --- .../src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs index 07da951c50..eb4d53ce27 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/EntityAgentWrapper.cs @@ -27,7 +27,7 @@ protected override async Task RunCoreAsync( AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - AgentRunResponse response = await base.RunAsync( + AgentRunResponse response = await base.RunCoreAsync( messages, thread, this.GetAgentEntityRunOptions(options), @@ -43,7 +43,7 @@ protected override async IAsyncEnumerable RunCoreStreami AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - await foreach (AgentRunResponseUpdate update in base.RunStreamingAsync( + await foreach (AgentRunResponseUpdate update in base.RunCoreStreamingAsync( messages, thread, this.GetAgentEntityRunOptions(options), From 207f1d60b78048ebf4954f0cfc7aa06563fadf3a Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Wed, 10 Dec 2025 18:01:10 +0000 Subject: [PATCH 11/15] fix compilation issues --- .../AgenticUI/AgenticUIAgent.cs | 6 +- .../PredictiveStateUpdatesAgent.cs | 6 +- .../SharedState/SharedStateAgent.cs | 6 +- .../ServerFunctionApprovalClientAgent.cs | 6 +- .../ServerFunctionApprovalServerAgent.cs | 6 +- .../Client/StatefulAgent.cs | 6 +- .../Server/SharedStateAgent.cs | 6 +- .../OpenAIChatClientAgent.cs | 8 +-- .../OpenAIResponseClientAgent.cs | 8 +-- .../M365Agent/Agents/WeatherForecastAgent.cs | 4 +- .../AIAgentWithOpenAIExtensionsTests.cs | 15 ++++- .../PurviewWrapperTests.cs | 67 ++++++++++--------- .../AgentWorkflowBuilderTests.cs | 2 +- 13 files changed, 82 insertions(+), 64 deletions(-) diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs b/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs index 05a7d86f15..d79787d260 100644 --- a/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs +++ b/dotnet/samples/AGUIClientServer/AGUIDojoServer/AgenticUI/AgenticUIAgent.cs @@ -19,12 +19,12 @@ public AgenticUIAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializerOp this._jsonSerializerOptions = jsonSerializerOptions; } - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); + return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs b/dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs index 8ac9928fbe..ab9ca2fca3 100644 --- a/dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs +++ b/dotnet/samples/AGUIClientServer/AGUIDojoServer/PredictiveStateUpdates/PredictiveStateUpdatesAgent.cs @@ -20,12 +20,12 @@ public PredictiveStateUpdatesAgent(AIAgent innerAgent, JsonSerializerOptions jso this._jsonSerializerOptions = jsonSerializerOptions; } - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); + return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs b/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs index c10450fcfb..1a1e58860a 100644 --- a/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs +++ b/dotnet/samples/AGUIClientServer/AGUIDojoServer/SharedState/SharedStateAgent.cs @@ -19,12 +19,12 @@ public SharedStateAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializer this._jsonSerializerOptions = jsonSerializerOptions; } - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); + return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs b/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs index 41538085db..9f7812cc50 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Client/ServerFunctionApprovalClientAgent.cs @@ -22,17 +22,17 @@ public ServerFunctionApprovalClientAgent(AIAgent innerAgent, JsonSerializerOptio this._jsonSerializerOptions = jsonSerializerOptions; } - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunStreamingAsync(messages, thread, options, cancellationToken) + return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken) .ToAgentRunResponseAsync(cancellationToken); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs b/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs index f515e97531..69e3db58c7 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step04_HumanInLoop/Server/ServerFunctionApprovalServerAgent.cs @@ -22,17 +22,17 @@ public ServerFunctionApprovalAgent(AIAgent innerAgent, JsonSerializerOptions jso this._jsonSerializerOptions = jsonSerializerOptions; } - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunStreamingAsync(messages, thread, options, cancellationToken) + return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken) .ToAgentRunResponseAsync(cancellationToken); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/StatefulAgent.cs b/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/StatefulAgent.cs index 8321efaa73..d5fd9f187b 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/StatefulAgent.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Client/StatefulAgent.cs @@ -35,18 +35,18 @@ public StatefulAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializerOpt } /// - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunStreamingAsync(messages, thread, options, cancellationToken) + return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken) .ToAgentRunResponseAsync(cancellationToken); } /// - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs b/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs index 4588c7bd60..603698b579 100644 --- a/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs +++ b/dotnet/samples/GettingStarted/AGUI/Step05_StateManagement/Server/SharedStateAgent.cs @@ -17,17 +17,17 @@ public SharedStateAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializer this._jsonSerializerOptions = jsonSerializerOptions; } - public override Task RunAsync( + protected override Task RunCoreAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunStreamingAsync(messages, thread, options, cancellationToken) + return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken) .ToAgentRunResponseAsync(cancellationToken); } - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs index b295bfecea..a0b59d1053 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs +++ b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step03_CreateFromChatClient/OpenAIChatClientAgent.cs @@ -87,10 +87,10 @@ public virtual IAsyncEnumerable RunStreamingAsync } /// - public sealed override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - base.RunAsync(messages, thread, options, cancellationToken); + protected sealed override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunCoreAsync(messages, thread, options, cancellationToken); /// - public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - base.RunStreamingAsync(messages, thread, options, cancellationToken); + protected override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunCoreStreamingAsync(messages, thread, options, cancellationToken); } diff --git a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs index 456de02836..5833273d8c 100644 --- a/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs +++ b/dotnet/samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step04_CreateFromOpenAIResponseClient/OpenAIResponseClientAgent.cs @@ -105,10 +105,10 @@ public virtual async IAsyncEnumerable RunStreamingAsync } /// - public sealed override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - base.RunAsync(messages, thread, options, cancellationToken); + protected sealed override Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunCoreAsync(messages, thread, options, cancellationToken); /// - public sealed override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => - base.RunStreamingAsync(messages, thread, options, cancellationToken); + protected sealed override IAsyncEnumerable RunCoreStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + base.RunCoreStreamingAsync(messages, thread, options, cancellationToken); } diff --git a/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs b/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs index 740b959a7a..ff7af20ba9 100644 --- a/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs +++ b/dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs @@ -48,9 +48,9 @@ public WeatherForecastAgent(IChatClient chatClient) { } - public override async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected override async Task RunCoreAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - var response = await base.RunAsync(messages, thread, options, cancellationToken); + var response = await base.RunCoreAsync(messages, thread, options, cancellationToken); // If the agent returned a valid structured output response // we might be able to enhance the response with an adaptive card. diff --git a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs index de8c459be0..339ee382d3 100644 --- a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Moq; +using Moq.Protected; using ChatMessage = Microsoft.Extensions.AI.ChatMessage; using ChatRole = Microsoft.Extensions.AI.ChatRole; using OpenAIChatMessage = OpenAI.Chat.ChatMessage; @@ -76,7 +77,12 @@ public async Task RunAsync_CallsUnderlyingAgentAsync() var responseMessage = new ChatMessage(ChatRole.Assistant, [new TextContent(ResponseText)]); mockAgent - .Setup(a => a.RunAsync(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) + .Protected() + .Setup>("RunAsync", + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny()) .ReturnsAsync(new AgentRunResponse([responseMessage])); // Act @@ -160,7 +166,12 @@ public async Task RunStreamingAsync_CallsUnderlyingAgentAsync() }; mockAgent - .Setup(a => a.RunStreamingAsync(It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) + .Protected() + .Setup>("RunStreamingAsync", + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny()) .Returns(ToAsyncEnumerableAsync(responseUpdates)); // Act diff --git a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs index 22b729dda4..0c09b694dd 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.AI; using Microsoft.Extensions.Logging.Abstractions; using Moq; +using Moq.Protected; namespace Microsoft.Agents.AI.Purview.UnitTests; @@ -295,11 +296,12 @@ public async Task ProcessAgentContentAsync_WithAllowedPromptAndBlockedResponse_R var mockAgent = new Mock(); var innerResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Sensitive response")); - mockAgent.Setup(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + mockAgent.Protected() + .Setup>("RunAsync", + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny()) .ReturnsAsync(innerResponse); this._mockProcessor.SetupSequence(x => x.ProcessMessagesAsync( @@ -333,11 +335,12 @@ public async Task ProcessAgentContentAsync_WithAllowedPromptAndResponse_ReturnsI var mockAgent = new Mock(); var innerResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Safe response")); - mockAgent.Setup(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + mockAgent.Protected() + .Setup>("RunAsync", + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny()) .ReturnsAsync(innerResponse); this._mockProcessor.Setup(x => x.ProcessMessagesAsync( @@ -375,11 +378,12 @@ public async Task ProcessAgentContentAsync_WithIgnoreExceptions_ContinuesOnError var expectedResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Response from inner agent")); var mockAgent = new Mock(); - mockAgent.Setup(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + mockAgent.Protected() + .Setup>("RunAsync", + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny()) .ReturnsAsync(expectedResponse); this._mockProcessor.SetupSequence(x => x.ProcessMessagesAsync( @@ -441,11 +445,12 @@ public async Task ProcessAgentContentAsync_ExtractsThreadIdFromMessageAdditional var expectedResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Response")); var mockAgent = new Mock(); - mockAgent.Setup(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + mockAgent.Protected() + .Setup>("RunAsync", + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny()) .ReturnsAsync(expectedResponse); this._mockProcessor.Setup(x => x.ProcessMessagesAsync( @@ -482,11 +487,12 @@ public async Task ProcessAgentContentAsync_GeneratesThreadId_WhenNotProvidedAsyn var expectedResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Response")); var mockAgent = new Mock(); - mockAgent.Setup(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + mockAgent.Protected() + .Setup>("RunAsync", + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny()) .ReturnsAsync(expectedResponse); string? capturedThreadId = null; @@ -521,11 +527,12 @@ public async Task ProcessAgentContentAsync_PassesResolvedUserId_ToResponseProces var mockAgent = new Mock(); var innerResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Response")); - mockAgent.Setup(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny())) + mockAgent.Protected() + .Setup>("RunAsync", + It.IsAny>(), + It.IsAny(), + It.IsAny(), + It.IsAny()) .ReturnsAsync(innerResponse); var callCount = 0; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs index 6198303270..785ab210a8 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs @@ -409,7 +409,7 @@ public async Task BuildGroupChat_AgentsRunInOrderAsync(int maxIterations) private sealed class DoubleEchoAgentWithBarrier(string name, StrongBox> barrier, StrongBox remaining) : DoubleEchoAgent(name) { - public override async IAsyncEnumerable RunStreamingAsync( + protected override async IAsyncEnumerable RunCoreStreamingAsync( IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (Interlocked.Decrement(ref remaining.Value) == 0) From 5817f3be245ba94e3001348403797da71e80bcb6 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Wed, 10 Dec 2025 18:17:17 +0000 Subject: [PATCH 12/15] fix compilatio issue --- dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs b/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs index 7f43ea09df..03b85d1ef5 100644 --- a/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs @@ -72,7 +72,7 @@ protected override async Task RunCoreAsync( try { - AgentRunResponse response = await base.RunAsync(messages, thread, options, cancellationToken).ConfigureAwait(false); + AgentRunResponse response = await base.RunCoreAsync(messages, thread, options, cancellationToken).ConfigureAwait(false); if (this._logger.IsEnabled(LogLevel.Debug)) { @@ -119,7 +119,7 @@ protected override async IAsyncEnumerable RunCoreStreami IAsyncEnumerator e; try { - e = base.RunStreamingAsync(messages, thread, options, cancellationToken).GetAsyncEnumerator(cancellationToken); + e = base.RunCoreStreamingAsync(messages, thread, options, cancellationToken).GetAsyncEnumerator(cancellationToken); } catch (OperationCanceledException) { From a85b590a3293abe4ffed603cd4f7b3ea208db5ce Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Thu, 11 Dec 2025 09:53:55 +0000 Subject: [PATCH 13/15] fix tests --- .../PurviewWrapperTests.cs | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs index 0c09b694dd..eafc67f7fc 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Purview.UnitTests/PurviewWrapperTests.cs @@ -278,11 +278,13 @@ public async Task ProcessAgentContentAsync_WithBlockedPrompt_ReturnsBlockedMessa Assert.Single(result.Messages); Assert.Equal(ChatRole.System, result.Messages[0].Role); Assert.Equal("Prompt blocked by policy", result.Messages[0].Text); - mockAgent.Verify(x => x.RunAsync( - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny()), Times.Never); + + mockAgent.Protected().Verify("RunCoreAsync", + Times.Never(), + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()); } [Fact] @@ -297,11 +299,11 @@ public async Task ProcessAgentContentAsync_WithAllowedPromptAndBlockedResponse_R var innerResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Sensitive response")); mockAgent.Protected() - .Setup>("RunAsync", - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny()) + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .ReturnsAsync(innerResponse); this._mockProcessor.SetupSequence(x => x.ProcessMessagesAsync( @@ -336,11 +338,11 @@ public async Task ProcessAgentContentAsync_WithAllowedPromptAndResponse_ReturnsI var innerResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Safe response")); mockAgent.Protected() - .Setup>("RunAsync", - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny()) + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .ReturnsAsync(innerResponse); this._mockProcessor.Setup(x => x.ProcessMessagesAsync( @@ -379,11 +381,11 @@ public async Task ProcessAgentContentAsync_WithIgnoreExceptions_ContinuesOnError var expectedResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Response from inner agent")); var mockAgent = new Mock(); mockAgent.Protected() - .Setup>("RunAsync", - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny()) + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .ReturnsAsync(expectedResponse); this._mockProcessor.SetupSequence(x => x.ProcessMessagesAsync( @@ -446,11 +448,11 @@ public async Task ProcessAgentContentAsync_ExtractsThreadIdFromMessageAdditional var expectedResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Response")); var mockAgent = new Mock(); mockAgent.Protected() - .Setup>("RunAsync", - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny()) + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .ReturnsAsync(expectedResponse); this._mockProcessor.Setup(x => x.ProcessMessagesAsync( @@ -488,11 +490,11 @@ public async Task ProcessAgentContentAsync_GeneratesThreadId_WhenNotProvidedAsyn var expectedResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Response")); var mockAgent = new Mock(); mockAgent.Protected() - .Setup>("RunAsync", - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny()) + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .ReturnsAsync(expectedResponse); string? capturedThreadId = null; @@ -528,11 +530,11 @@ public async Task ProcessAgentContentAsync_PassesResolvedUserId_ToResponseProces var innerResponse = new AgentRunResponse(new ChatMessage(ChatRole.Assistant, "Response")); mockAgent.Protected() - .Setup>("RunAsync", - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny()) + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .ReturnsAsync(innerResponse); var callCount = 0; From 00eca8b4ef7bf9157a912d0a1809d6a8378eba4d Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Thu, 11 Dec 2025 10:39:14 +0000 Subject: [PATCH 14/15] fix unit tests --- .../AIAgentWithOpenAIExtensionsTests.cs | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs index 339ee382d3..60c37c9b82 100644 --- a/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.OpenAI.UnitTests/Extensions/AIAgentWithOpenAIExtensionsTests.cs @@ -78,26 +78,27 @@ public async Task RunAsync_CallsUnderlyingAgentAsync() mockAgent .Protected() - .Setup>("RunAsync", - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny()) + .Setup>("RunCoreAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .ReturnsAsync(new AgentRunResponse([responseMessage])); // Act var result = await mockAgent.Object.RunAsync(openAiMessages, mockThread.Object, options, cancellationToken); // Assert - mockAgent.Verify( - a => a.RunAsync( - It.Is>(msgs => + mockAgent.Protected() + .Verify("RunCoreAsync", + Times.Once(), + ItExpr.Is>(msgs => msgs.ToList().Count == 1 && msgs.ToList()[0].Text == TestMessageText), mockThread.Object, options, - cancellationToken), - Times.Once); + cancellationToken + ); Assert.NotNull(result); Assert.NotEmpty(result.Content); @@ -167,11 +168,11 @@ public async Task RunStreamingAsync_CallsUnderlyingAgentAsync() mockAgent .Protected() - .Setup>("RunStreamingAsync", - It.IsAny>(), - It.IsAny(), - It.IsAny(), - It.IsAny()) + .Setup>("RunCoreStreamingAsync", + ItExpr.IsAny>(), + ItExpr.IsAny(), + ItExpr.IsAny(), + ItExpr.IsAny()) .Returns(ToAsyncEnumerableAsync(responseUpdates)); // Act @@ -183,15 +184,16 @@ public async Task RunStreamingAsync_CallsUnderlyingAgentAsync() } // Assert - mockAgent.Verify( - a => a.RunStreamingAsync( - It.Is>(msgs => + mockAgent.Protected() + .Verify("RunCoreStreamingAsync", + Times.Once(), + ItExpr.Is>(msgs => msgs.ToList().Count == 1 && msgs.ToList()[0].Text == TestMessageText), mockThread.Object, options, - cancellationToken), - Times.Once); + cancellationToken + ); Assert.True(updateCount > 0, "Expected at least one streaming update"); } From ece7787e4c149d00ad69296dd853b049b622e597 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Thu, 11 Dec 2025 13:07:04 +0000 Subject: [PATCH 15/15] fix unit test --- .../AgentWorkflowBuilderTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs index 785ab210a8..c45ef8726e 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/AgentWorkflowBuilderTests.cs @@ -419,7 +419,7 @@ protected override async IAsyncEnumerable RunCoreStreami await barrier.Value!.Task.ConfigureAwait(false); - await foreach (var update in base.RunStreamingAsync(messages, thread, options, cancellationToken)) + await foreach (var update in base.RunCoreStreamingAsync(messages, thread, options, cancellationToken)) { await Task.Yield(); yield return update;