Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8b23f71
Initial plan
Copilot Dec 10, 2025
322052e
Refactor AIAgent: Make RunAsync and RunStreamingAsync non-abstract, a…
Copilot Dec 10, 2025
dc987ed
Fix infinite recursion in test implementations
Copilot Dec 10, 2025
cef4fa8
Make RunAsync and RunStreamingAsync non-virtual as requested
Copilot Dec 10, 2025
6f8fb49
Fix DelegatingAIAgent subclasses to use RunCoreAsync/RunCoreStreaming…
Copilot Dec 10, 2025
95bfe14
Fix XML documentation references in AnonymousDelegatingAIAgent
Copilot Dec 10, 2025
04fdb4c
Restore <see cref> tags with proper qualified signatures in Anonymous…
Copilot Dec 10, 2025
c1ed679
Rollback unnecessary XML documentation changes in AnonymousDelegating…
Copilot Dec 10, 2025
df1ea8d
Remove pragma and update crefs to RunCoreAsync/RunCoreStreamingAsync
Copilot Dec 10, 2025
c295c14
Merge branch 'main' into copilot/make-run-methods-non-abstract
SergeyMenshykh Dec 10, 2025
c9b93f4
Fix EntityAgentWrapper to call base.RunCoreAsync/RunCoreStreamingAsync
Copilot Dec 10, 2025
207f1d6
fix compilation issues
SergeyMenshykh Dec 10, 2025
f880427
Merge branch 'main' into copilot/make-run-methods-non-abstract
SergeyMenshykh Dec 10, 2025
5817f3b
fix compilatio issue
SergeyMenshykh Dec 10, 2025
94e70ea
Merge branch 'copilot/make-run-methods-non-abstract' of https://githu…
SergeyMenshykh Dec 10, 2025
a85b590
fix tests
SergeyMenshykh Dec 11, 2025
00eca8b
fix unit tests
SergeyMenshykh Dec 11, 2025
90021bf
Merge branch 'main' into copilot/make-run-methods-non-abstract
SergeyMenshykh Dec 11, 2025
ece7787
fix unit test
SergeyMenshykh Dec 11, 2025
e9cfa1c
Merge branch 'copilot/make-run-methods-non-abstract' of https://githu…
SergeyMenshykh Dec 11, 2025
a8449e5
Merge branch 'main' into copilot/make-run-methods-non-abstract
SergeyMenshykh Dec 11, 2025
f0300e3
Merge branch 'main' into copilot/make-run-methods-non-abstract
crickman Dec 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<AgentRunResponse> RunAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
protected override async Task<AgentRunResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
// Create a thread if the user didn't supply one.
thread ??= this.GetNewThread();
Expand All @@ -58,7 +58,7 @@ public override async Task<AgentRunResponse> RunAsync(IEnumerable<ChatMessage> m
};
}

public override async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
protected override async IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
// Create a thread if the user didn't supply one.
thread ??= this.GetNewThread();
Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/Microsoft.Agents.AI.A2A/A2AAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json
=> new A2AAgentThread(serializedThread, jsonSerializerOptions);

/// <inheritdoc/>
public override async Task<AgentRunResponse> RunAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
protected override async Task<AgentRunResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
_ = Throw.IfNull(messages);

Expand Down Expand Up @@ -134,7 +134,7 @@ public override async Task<AgentRunResponse> RunAsync(IEnumerable<ChatMessage> m
}

/// <inheritdoc/>
public override async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
protected override async IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
_ = Throw.IfNull(messages);

Expand Down
61 changes: 59 additions & 2 deletions dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,35 @@ public Task<AgentRunResponse> RunAsync(
/// <returns>A task that represents the asynchronous operation. The task result contains an <see cref="AgentRunResponse"/> with the agent's output.</returns>
/// <remarks>
/// <para>
/// This method delegates to <see cref="RunCoreAsync"/> 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.
/// </para>
/// <para>
/// The messages are processed in the order provided and become part of the conversation history.
/// The agent's response will also be added to <paramref name="thread"/> if one is provided.
/// </para>
/// </remarks>
public Task<AgentRunResponse> RunAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default) =>
this.RunCoreAsync(messages, thread, options, cancellationToken);

/// <summary>
/// Core implementation of the agent invocation logic with a collection of chat messages.
/// </summary>
/// <param name="messages">The collection of messages to send to the agent for processing.</param>
/// <param name="thread">
/// The conversation thread to use for this invocation. If <see langword="null"/>, a new thread will be created.
/// The thread will be updated with the input messages and any response messages generated during invocation.
/// </param>
/// <param name="options">Optional configuration parameters for controlling the agent's invocation behavior.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains an <see cref="AgentRunResponse"/> with the agent's output.</returns>
/// <remarks>
/// <para>
/// 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.
Expand All @@ -230,7 +259,7 @@ public Task<AgentRunResponse> RunAsync(
/// The agent's response will also be added to <paramref name="thread"/> if one is provided.
/// </para>
/// </remarks>
public abstract Task<AgentRunResponse> RunAsync(
protected abstract Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down Expand Up @@ -315,6 +344,34 @@ public IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
/// <returns>An asynchronous enumerable of <see cref="AgentRunResponseUpdate"/> instances representing the streaming response.</returns>
/// <remarks>
/// <para>
/// This method delegates to <see cref="RunCoreStreamingAsync"/> 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.
/// </para>
/// <para>
/// Each <see cref="AgentRunResponseUpdate"/> represents a portion of the complete response, allowing consumers
/// to display partial results, implement progressive loading, or provide immediate feedback to users.
/// </para>
/// </remarks>
public IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default) =>
this.RunCoreStreamingAsync(messages, thread, options, cancellationToken);

/// <summary>
/// Core implementation of the agent streaming invocation logic with a collection of chat messages.
/// </summary>
/// <param name="messages">The collection of messages to send to the agent for processing.</param>
/// <param name="thread">
/// The conversation thread to use for this invocation. If <see langword="null"/>, a new thread will be created.
/// The thread will be updated with the input messages and any response updates generated during invocation.
/// </param>
/// <param name="options">Optional configuration parameters for controlling the agent's invocation behavior.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
/// <returns>An asynchronous enumerable of <see cref="AgentRunResponseUpdate"/> instances representing the streaming response.</returns>
/// <remarks>
/// <para>
/// 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.
/// </para>
Expand All @@ -323,7 +380,7 @@ public IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
/// to display partial results, implement progressive loading, or provide immediate feedback to users.
/// </para>
/// </remarks>
public abstract IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
protected abstract IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,15 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json
=> this.InnerAgent.DeserializeThread(serializedThread, jsonSerializerOptions);

/// <inheritdoc />
public override Task<AgentRunResponse> RunAsync(
protected override Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default)
=> this.InnerAgent.RunAsync(messages, thread, options, cancellationToken);

/// <inheritdoc />
public override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
protected override IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public override AgentThread DeserializeThread(JsonElement serializedThread, Json
=> new CopilotStudioAgentThread(serializedThread, jsonSerializerOptions);

/// <inheritdoc/>
public override async Task<AgentRunResponse> RunAsync(
protected override async Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down Expand Up @@ -96,7 +96,7 @@ public override async Task<AgentRunResponse> RunAsync(
}

/// <inheritdoc/>
public override async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
protected override async IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/Microsoft.Agents.AI.DurableTask/DurableAIAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public override AgentThread DeserializeThread(
/// <exception cref="AgentNotRegisteredException">Thrown when the agent has not been registered.</exception>
/// <exception cref="ArgumentException">Thrown when the provided thread is not valid for a durable agent.</exception>
/// <exception cref="NotSupportedException">Thrown when cancellation is requested (cancellation is not supported for durable agents).</exception>
public override async Task<AgentRunResponse> RunAsync(
protected override async Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down Expand Up @@ -128,7 +128,7 @@ public override async Task<AgentRunResponse> RunAsync(
/// <param name="options">Optional run options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A streaming response enumerable.</returns>
public override async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
protected override async IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public override AgentThread GetNewThread()
return new DurableAgentThread(AgentSessionId.WithRandomKey(this.Name!));
}

public override async Task<AgentRunResponse> RunAsync(
protected override async Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down Expand Up @@ -70,7 +70,7 @@ public override async Task<AgentRunResponse> RunAsync(
return await agentRunHandle.ReadAgentResponseAsync(cancellationToken);
}

public override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
protected override IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<AgentRunResponse> RunAsync(
protected override async Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand All @@ -37,7 +37,7 @@ public override async Task<AgentRunResponse> RunAsync(
return response;
}

public override async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
protected override async IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/Microsoft.Agents.AI.Purview/PurviewAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,13 @@ public override AgentThread GetNewThread()
}

/// <inheritdoc/>
public override Task<AgentRunResponse> RunAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
protected override Task<AgentRunResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
return this._purviewWrapper.ProcessAgentContentAsync(messages, thread, options, this._innerAgent, cancellationToken);
}

/// <inheritdoc/>
public override async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
protected override async IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(IEnumerable<ChatMessage> 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())
Expand Down
8 changes: 4 additions & 4 deletions dotnet/src/Microsoft.Agents.AI.Workflows/WorkflowHostAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ private async ValueTask<WorkflowThread> UpdateThreadAsync(IEnumerable<ChatMessag
return workflowThread;
}

public override async
Task<AgentRunResponse> RunAsync(
protected override async
Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand All @@ -101,8 +101,8 @@ Task<AgentRunResponse> RunAsync(
return merger.ComputeMerged(workflowThread.LastResponseId!, this.Id, this.Name);
}

public override async
IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
protected override async
IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down
6 changes: 4 additions & 2 deletions dotnet/src/Microsoft.Agents.AI/AnonymousDelegatingAIAgent.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -85,7 +87,7 @@ public AnonymousDelegatingAIAgent(
}

/// <inheritdoc/>
public override Task<AgentRunResponse> RunAsync(
protected override Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down Expand Up @@ -132,7 +134,7 @@ await this._sharedFunc(
}

/// <inheritdoc/>
public override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
protected override IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public ChatClientAgent(IChatClient chatClient, ChatClientAgentOptions? options,
internal ChatOptions? ChatOptions => this._agentOptions?.ChatOptions;

/// <inheritdoc/>
public override Task<AgentRunResponse> RunAsync(
protected override Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down Expand Up @@ -193,7 +193,7 @@ private static IChatClient ApplyRunOptionsTransformations(AgentRunOptions? optio
}

/// <inheritdoc/>
public override async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
protected override async IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ internal FunctionInvocationDelegatingAgent(AIAgent innerAgent, Func<AIAgent, Fun
this._delegateFunc = delegateFunc;
}

public override Task<AgentRunResponse> RunAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
protected override Task<AgentRunResponse> RunCoreAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
=> this.InnerAgent.RunAsync(messages, thread, this.AgentRunOptionsWithFunctionMiddleware(options), cancellationToken);

public override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
protected override IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(IEnumerable<ChatMessage> 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
Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/Microsoft.Agents.AI/LoggingAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public JsonSerializerOptions JsonSerializerOptions
}

/// <inheritdoc/>
public override async Task<AgentRunResponse> RunAsync(
protected override async Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
if (this._logger.IsEnabled(LogLevel.Debug))
Expand Down Expand Up @@ -101,7 +101,7 @@ public override async Task<AgentRunResponse> RunAsync(
}

/// <inheritdoc/>
public override async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
protected override async IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
if (this._logger.IsEnabled(LogLevel.Debug))
Expand Down
Loading