Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 @@ -19,12 +19,12 @@ public AgenticUIAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializerOp
this._jsonSerializerOptions = jsonSerializerOptions;
}

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.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken);
return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken);
}

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 @@ -20,12 +20,12 @@ public PredictiveStateUpdatesAgent(AIAgent innerAgent, JsonSerializerOptions jso
this._jsonSerializerOptions = jsonSerializerOptions;
}

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.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken);
return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken);
}

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 @@ -19,12 +19,12 @@ public SharedStateAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializer
this._jsonSerializerOptions = jsonSerializerOptions;
}

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.RunStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken);
return this.RunCoreStreamingAsync(messages, thread, options, cancellationToken).ToAgentRunResponseAsync(cancellationToken);
}

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 @@ -22,17 +22,17 @@ public ServerFunctionApprovalClientAgent(AIAgent innerAgent, JsonSerializerOptio
this._jsonSerializerOptions = jsonSerializerOptions;
}

public override Task<AgentRunResponse> RunAsync(
protected override Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> 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<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 @@ -22,17 +22,17 @@ public ServerFunctionApprovalAgent(AIAgent innerAgent, JsonSerializerOptions jso
this._jsonSerializerOptions = jsonSerializerOptions;
}

public override Task<AgentRunResponse> RunAsync(
protected override Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> 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<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 @@ -35,18 +35,18 @@ public StatefulAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializerOpt
}

/// <inheritdoc />
public override Task<AgentRunResponse> RunAsync(
protected override Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> 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);
}

/// <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 @@ -17,17 +17,17 @@ public SharedStateAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializer
this._jsonSerializerOptions = jsonSerializerOptions;
}

public override Task<AgentRunResponse> RunAsync(
protected override Task<AgentRunResponse> RunCoreAsync(
IEnumerable<ChatMessage> 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<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 @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ public virtual IAsyncEnumerable<StreamingChatCompletionUpdate> RunStreamingAsync
}

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

/// <inheritdoc/>
public override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(IEnumerable<Microsoft.Extensions.AI.ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
base.RunStreamingAsync(messages, thread, options, cancellationToken);
protected override IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(IEnumerable<Microsoft.Extensions.AI.ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
base.RunCoreStreamingAsync(messages, thread, options, cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ public virtual async IAsyncEnumerable<StreamingResponseUpdate> RunStreamingAsync
}

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

/// <inheritdoc/>
public sealed override IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
base.RunStreamingAsync(messages, thread, options, cancellationToken);
protected sealed override IAsyncEnumerable<AgentRunResponseUpdate> RunCoreStreamingAsync(IEnumerable<ChatMessage> messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) =>
base.RunCoreStreamingAsync(messages, thread, options, cancellationToken);
}
4 changes: 2 additions & 2 deletions dotnet/samples/M365Agent/Agents/WeatherForecastAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ public WeatherForecastAgent(IChatClient chatClient)
{
}

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)
{
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.
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 @@ -230,6 +230,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 @@ -239,7 +268,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 @@ -324,6 +353,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 @@ -332,7 +389,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
Loading
Loading