From 8af4918f56d1fe642f067bf40af76f92013ebd26 Mon Sep 17 00:00:00 2001 From: westey <164392973+westey-m@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:58:46 +0100 Subject: [PATCH] .NET: BREAKING Update AIAgent.Run to take IEnumerable instead of IReadonlyCollection (#729) * Update AIAgent,Run to take IEnumerable instead of IReadonlyCollection * Address PR comment. * Cast to IReadonlyCollection since this is typically all that is required to avoid unecessary allocation. * Update OrchestratingAgent protected methods as well. --- .../Program.cs | 6 ++--- .../Program.cs | 2 +- .../ConcurrentOrchestration.cs | 6 ++--- .../GroupChat/GroupChatOrchestration.cs | 4 ++-- .../Handoffs/HandoffOrchestration.cs | 4 ++-- .../OrchestratingAgent.cs | 18 +++++++------- .../SequentialOrchestration.cs | 7 +++--- .../WorkflowHostAgent.cs | 6 ++--- .../WorkflowMessageStore.cs | 2 +- .../A2AAgent.cs | 6 ++--- .../Extensions/ChatMessageExtensions.cs | 2 +- .../AIAgent.cs | 15 +++++------- .../AgentThread.cs | 2 +- .../DelegatingAIAgent.cs | 4 ++-- .../IChatMessageStore.cs | 2 +- .../InMemoryChatMessageStore.cs | 9 ++----- .../CopilotStudioAgent.cs | 4 ++-- .../AgentProxy.cs | 10 ++++---- .../OpenAIChatClientAgent.cs | 4 ++-- .../ChatCompletion/ChatClientAgent.cs | 17 ++++++------- .../OpenTelemetryAgent.cs | 24 +++++++++---------- .../MockAgent.cs | 4 ++-- .../OrchestrationResultTests.cs | 8 +++---- .../RepresentationTests.cs | 4 ++-- .../Sample/06_GroupChat_Workflow.cs | 14 ++++++----- .../SpecializedExecutorSmokeTests.cs | 4 ++-- .../AIAgentTests.cs | 6 ++--- .../AgentActorTests.cs | 4 ++-- .../AgentAIFunctionFactoryTests.cs | 4 ++-- 29 files changed, 99 insertions(+), 103 deletions(-) diff --git a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs index df315dbfaa..a2713c80d2 100644 --- a/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs +++ b/dotnet/samples/GettingStarted/AgentProviders/Agent_With_CustomImplementation/Program.cs @@ -30,7 +30,7 @@ namespace SampleApp // Custom agent that parrot's the user input back in upper case. internal sealed class UpperCaseParrotAgent : AIAgent { - public override async Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public override async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { // Create a thread if the user didn't supply one. thread ??= this.GetNewThread(); @@ -50,7 +50,7 @@ namespace SampleApp }; } - public override async IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + public override async IAsyncEnumerable RunStreamingAsync(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(); @@ -76,7 +76,7 @@ namespace SampleApp } } - private static IEnumerable CloneAndToUpperCase(IReadOnlyCollection messages, string agentName) => messages.Select(x => + private static IEnumerable CloneAndToUpperCase(IEnumerable messages, string agentName) => messages.Select(x => { // Clone the message and update its author to be the agent. var messageClone = x.Clone(); diff --git a/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs b/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs index 11cbd7670f..8db527a2af 100644 --- a/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs +++ b/dotnet/samples/GettingStarted/Agents/Agent_Step07_3rdPartyThreadStorage/Program.cs @@ -82,7 +82,7 @@ namespace SampleApp public string? ThreadId => this._threadId; - public async Task AddMessagesAsync(IReadOnlyCollection messages, CancellationToken cancellationToken) + public async Task AddMessagesAsync(IEnumerable messages, CancellationToken cancellationToken) { this._threadId ??= Guid.NewGuid().ToString(); diff --git a/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs index 3953b7fd0f..009c327e0e 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/ConcurrentOrchestration.cs @@ -57,11 +57,11 @@ public partial class ConcurrentOrchestration : OrchestratingAgent } /// - protected override Task RunCoreAsync(IReadOnlyCollection messages, OrchestratingAgentContext context, CancellationToken cancellationToken) => - this.ResumeAsync(messages, new AgentRunResponse?[this.Agents.Count], context, cancellationToken); + protected override Task RunCoreAsync(IEnumerable messages, OrchestratingAgentContext context, CancellationToken cancellationToken) => + this.ResumeAsync(messages as IReadOnlyCollection ?? messages.ToList(), new AgentRunResponse?[this.Agents.Count], context, cancellationToken); /// - protected override Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected override Task ResumeCoreAsync(JsonElement checkpointState, IEnumerable newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) { var state = checkpointState.Deserialize(OrchestrationJsonContext.Default.ConcurrentState) ?? throw new InvalidOperationException("The checkpoint state is invalid."); diff --git a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs index 0f0973e768..5afd420a27 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/GroupChat/GroupChatOrchestration.cs @@ -43,7 +43,7 @@ public sealed partial class GroupChatOrchestration : OrchestratingAgent public Func>? InteractiveCallback { get; set; } /// - protected override Task RunCoreAsync(IReadOnlyCollection messages, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected override Task RunCoreAsync(IEnumerable messages, OrchestratingAgentContext context, CancellationToken cancellationToken) { List allMessages = [.. messages]; int originalMessageCount = allMessages.Count; @@ -51,7 +51,7 @@ public sealed partial class GroupChatOrchestration : OrchestratingAgent } /// - protected override Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected override Task ResumeCoreAsync(JsonElement checkpointState, IEnumerable newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) { var state = checkpointState.Deserialize(OrchestrationJsonContext.Default.GroupChatState) ?? throw new InvalidOperationException("The checkpoint state is invalid."); diff --git a/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs index 8ef64910e7..23d29f9f69 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/Handoffs/HandoffOrchestration.cs @@ -44,7 +44,7 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent public Func>? InteractiveCallback { get; set; } /// - protected override Task RunCoreAsync(IReadOnlyCollection messages, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected override Task RunCoreAsync(IEnumerable messages, OrchestratingAgentContext context, CancellationToken cancellationToken) { List allMessages = [.. messages]; int originalMessageCount = allMessages.Count; @@ -52,7 +52,7 @@ public sealed partial class HandoffOrchestration : OrchestratingAgent } /// - protected override Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected override Task ResumeCoreAsync(JsonElement checkpointState, IEnumerable newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) { var state = checkpointState.Deserialize(OrchestrationJsonContext.Default.HandoffState) ?? throw new InvalidOperationException("The checkpoint state is invalid."); diff --git a/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs b/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs index 8da88c20bc..18c70cab3a 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/OrchestratingAgent.cs @@ -65,7 +65,7 @@ public abstract partial class OrchestratingAgent : AIAgent /// public sealed override async Task RunAsync( - IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { _ = Throw.IfNull(messages); @@ -87,7 +87,7 @@ public abstract partial class OrchestratingAgent : AIAgent /// public sealed override async IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { // TODO: There should be a RunAsync overload that returns an OrchestratingAgentStreamingResponse, which this then delegates to. @@ -106,12 +106,12 @@ public abstract partial class OrchestratingAgent : AIAgent /// The runtime associated with the orchestration. /// The to monitor for cancellation requests. The default is . public async ValueTask RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentRunOptions? options = null, IActorRuntimeContext? runtime = null, CancellationToken cancellationToken = default) { - Throw.IfNull(messages, nameof(messages)); + var readonlyCollectionMessages = Throw.IfNull(messages) as IReadOnlyCollection ?? messages.ToList(); cancellationToken.ThrowIfCancellationRequested(); ILogger logger = this.LoggerFactory.CreateLogger(this.GetType().Name); @@ -131,8 +131,8 @@ public abstract partial class OrchestratingAgent : AIAgent JsonElement? checkpoint = await this.ReadCheckpointAsync(context, cancellationToken).ConfigureAwait(false); Task completion = checkpoint is null ? - this.RunCoreAsync(messages, context, cancellationToken) : - this.ResumeCoreAsync(checkpoint.Value, messages, context, cancellationToken); + this.RunCoreAsync(readonlyCollectionMessages, context, cancellationToken) : + this.ResumeCoreAsync(checkpoint.Value, readonlyCollectionMessages, context, cancellationToken); if (logger.IsEnabled(LogLevel.Trace)) { @@ -148,7 +148,7 @@ public abstract partial class OrchestratingAgent : AIAgent /// The input message. /// The context for this operation. /// A cancellation token that can be used to cancel the operation. - protected abstract Task RunCoreAsync(IReadOnlyCollection messages, OrchestratingAgentContext context, CancellationToken cancellationToken); + protected abstract Task RunCoreAsync(IEnumerable messages, OrchestratingAgentContext context, CancellationToken cancellationToken); /// /// Resumes processing of the orchestration. @@ -157,7 +157,7 @@ public abstract partial class OrchestratingAgent : AIAgent /// The new messages to be processed in addition to the checkpoint state. /// The context for this operation. /// A cancellation token that can be used to cancel the operation. - protected abstract Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken); + protected abstract Task ResumeCoreAsync(JsonElement checkpointState, IEnumerable newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken); /// /// Runs the agent with input messages and respond with both streamed and regular messages. @@ -168,7 +168,7 @@ public abstract partial class OrchestratingAgent : AIAgent /// Options to use when invoking the agent. /// A cancellation token that can be used to cancel the operation. /// A task that returns the response . - protected static async ValueTask RunAsync(AIAgent agent, OrchestratingAgentContext context, IReadOnlyCollection input, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + protected static async ValueTask RunAsync(AIAgent agent, OrchestratingAgentContext context, IEnumerable input, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { // Utilize streaming iff a streaming callback is provided; otherwise, use the non-streaming API. AgentRunResponse response; diff --git a/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs b/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs index 1c113cfbde..14a4bb1cdd 100644 --- a/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs +++ b/dotnet/src/Microsoft.Agents.Orchestration/SequentialOrchestration.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -28,11 +29,11 @@ public sealed partial class SequentialOrchestration : OrchestratingAgent } /// - protected override Task RunCoreAsync(IReadOnlyCollection messages, OrchestratingAgentContext context, CancellationToken cancellationToken) => - this.ResumeAsync(0, messages, context, cancellationToken); + protected override Task RunCoreAsync(IEnumerable messages, OrchestratingAgentContext context, CancellationToken cancellationToken) => + this.ResumeAsync(0, messages as IReadOnlyCollection ?? messages.ToList(), context, cancellationToken); /// - protected override Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) + protected override Task ResumeCoreAsync(JsonElement checkpointState, IEnumerable newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) { var state = checkpointState.Deserialize(OrchestrationJsonContext.Default.SequentialState) ?? throw new InvalidOperationException("The checkpoint state is invalid."); diff --git a/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs b/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs index 3751cd2c9f..d0a48288d5 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/WorkflowHostAgent.cs @@ -96,7 +96,7 @@ internal class WorkflowHostAgent : AIAgent } } - private async ValueTask UpdateThreadAsync(IReadOnlyCollection messages, AgentThread? thread = null, CancellationToken cancellation = default) + private async ValueTask UpdateThreadAsync(IEnumerable messages, AgentThread? thread = null, CancellationToken cancellation = default) { if (thread is null) { @@ -114,7 +114,7 @@ internal class WorkflowHostAgent : AIAgent public override async Task RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) @@ -134,7 +134,7 @@ internal class WorkflowHostAgent : AIAgent public override async IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) diff --git a/dotnet/src/Microsoft.Agents.Workflows/WorkflowMessageStore.cs b/dotnet/src/Microsoft.Agents.Workflows/WorkflowMessageStore.cs index 35de73ce0b..dd2aed2750 100644 --- a/dotnet/src/Microsoft.Agents.Workflows/WorkflowMessageStore.cs +++ b/dotnet/src/Microsoft.Agents.Workflows/WorkflowMessageStore.cs @@ -25,7 +25,7 @@ internal class WorkflowMessageStore : IChatMessageStore this._chatMessages.AddRange(messages); } - public Task AddMessagesAsync(IReadOnlyCollection messages, CancellationToken cancellationToken) + public Task AddMessagesAsync(IEnumerable messages, CancellationToken cancellationToken) { this._chatMessages.AddRange(messages); diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AAgent.cs index 061b988137..802744bf9a 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/A2AAgent.cs @@ -52,7 +52,7 @@ internal sealed class A2AAgent : AIAgent } /// - public override async Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public override async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { ValidateInputMessages(messages); @@ -98,7 +98,7 @@ internal sealed class A2AAgent : AIAgent } /// - public override async IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { ValidateInputMessages(messages); @@ -147,7 +147,7 @@ internal sealed class A2AAgent : AIAgent /// public override string? Description => this._description ?? base.Description; - private static void ValidateInputMessages(IReadOnlyCollection messages) + private static void ValidateInputMessages(IEnumerable messages) { _ = Throw.IfNull(messages); diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/ChatMessageExtensions.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/ChatMessageExtensions.cs index f030750dcd..f7a2b09013 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/ChatMessageExtensions.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.A2A/Extensions/ChatMessageExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.Extensions.AI.Agents.A2A; /// internal static class ChatMessageExtensions { - internal static Message ToA2AMessage(this IReadOnlyCollection messages) + internal static Message ToA2AMessage(this IEnumerable messages) { List allParts = []; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs index 05bdf49039..395f075029 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AIAgent.cs @@ -112,7 +112,7 @@ public abstract class AIAgent AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunAsync((IReadOnlyCollection)[], thread, options, cancellationToken); + return this.RunAsync((IEnumerable)[], thread, options, cancellationToken); } /// @@ -174,7 +174,7 @@ public abstract class AIAgent /// The to monitor for cancellation requests. The default is . /// A containing the list of items. public abstract Task RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default); @@ -194,7 +194,7 @@ public abstract class AIAgent AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - return this.RunStreamingAsync((IReadOnlyCollection)[], thread, options, cancellationToken); + return this.RunStreamingAsync((IEnumerable)[], thread, options, cancellationToken); } /// @@ -256,7 +256,7 @@ public abstract class AIAgent /// The to monitor for cancellation requests. The default is . /// An async list of response items that each contain a . public abstract IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default); @@ -285,14 +285,11 @@ public abstract class AIAgent /// The messages to pass to the thread. /// The to monitor for cancellation requests. The default is . /// An async task that completes once the notification is complete. - protected static async Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IReadOnlyCollection messages, CancellationToken cancellationToken) + protected static async Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IEnumerable messages, CancellationToken cancellationToken) { _ = Throw.IfNull(thread); _ = Throw.IfNull(messages); - if (messages.Count > 0) - { - await thread.OnNewMessagesAsync(messages, cancellationToken).ConfigureAwait(false); - } + await thread.OnNewMessagesAsync(messages, cancellationToken).ConfigureAwait(false); } } diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs index afc92bc60d..74a5c8108e 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/AgentThread.cs @@ -139,7 +139,7 @@ public class AgentThread /// The to monitor for cancellation requests. The default is . /// A task that completes when the context has been updated. /// The thread has been deleted. - protected internal virtual async Task OnNewMessagesAsync(IReadOnlyCollection newMessages, CancellationToken cancellationToken = default) + protected internal virtual async Task OnNewMessagesAsync(IEnumerable newMessages, CancellationToken cancellationToken = default) { switch (this) { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/DelegatingAIAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/DelegatingAIAgent.cs index e2e0fd7f2d..81cc93628c 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/DelegatingAIAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/DelegatingAIAgent.cs @@ -54,7 +54,7 @@ public class DelegatingAIAgent : AIAgent /// public override Task RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) @@ -62,7 +62,7 @@ public class DelegatingAIAgent : AIAgent /// public override IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs index ad6303f36e..e9c3bf374a 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/IChatMessageStore.cs @@ -42,7 +42,7 @@ public interface IChatMessageStore /// The messages to add. /// The to monitor for cancellation requests. The default is . /// An async task. - Task AddMessagesAsync(IReadOnlyCollection messages, CancellationToken cancellationToken = default); + Task AddMessagesAsync(IEnumerable messages, CancellationToken cancellationToken = default); /// /// Deserializes the state contained in the provided into the properties on this store. diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs index 61e56c5f18..6f59fd696c 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Abstractions/InMemoryChatMessageStore.cs @@ -61,15 +61,10 @@ public sealed class InMemoryChatMessageStore : IList, IChatMessageS } /// - public async Task AddMessagesAsync(IReadOnlyCollection messages, CancellationToken cancellationToken) + public async Task AddMessagesAsync(IEnumerable messages, CancellationToken cancellationToken) { _ = Throw.IfNull(messages); - if (messages.Count == 0) - { - return; - } - this._messages.AddRange(messages); if (this._reducerTriggerEvent == ChatReducerTriggerEvent.AfterMessageAdded && this._chatReducer is not null) @@ -172,7 +167,7 @@ public sealed class InMemoryChatMessageStore : IList, IChatMessageS { /// /// Trigger the reducer when a new message is added. - /// will only complete when reducer processing is done. + /// will only complete when reducer processing is done. /// AfterMessageAdded, diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs index 30aa346d3c..dbd15c2b6e 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.CopilotStudio/CopilotStudioAgent.cs @@ -41,7 +41,7 @@ public class CopilotStudioAgent : AIAgent /// public override async Task RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) @@ -74,7 +74,7 @@ public class CopilotStudioAgent : AIAgent /// public override async IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentProxy.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentProxy.cs index 731f54da72..5a0b41bb8a 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentProxy.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.Hosting/AgentProxy.cs @@ -45,7 +45,7 @@ public sealed class AgentProxy : AIAgent /// public override async Task RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) @@ -57,7 +57,7 @@ public sealed class AgentProxy : AIAgent /// public override async IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) @@ -70,7 +70,7 @@ public sealed class AgentProxy : AIAgent } } - private async Task RunAsync(IReadOnlyCollection messages, string threadId, CancellationToken cancellationToken) + private async Task RunAsync(IEnumerable messages, string threadId, CancellationToken cancellationToken) { var handle = await this.RunCoreAsync(messages, threadId, cancellationToken).ConfigureAwait(false); var response = await handle.GetResponseAsync(cancellationToken).ConfigureAwait(false); @@ -85,7 +85,7 @@ public sealed class AgentProxy : AIAgent } private async IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, string threadId, [EnumeratorCancellation] CancellationToken cancellationToken) { @@ -123,7 +123,7 @@ public sealed class AgentProxy : AIAgent return agentProxyThread.ConversationId!; } - private async Task RunCoreAsync(IReadOnlyCollection messages, string threadId, CancellationToken cancellationToken) + private async Task RunCoreAsync(IEnumerable messages, string threadId, CancellationToken cancellationToken) { List newMessages = [.. messages]; diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs index 2c9dbdcee3..e583d5d4e5 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents.OpenAI/OpenAIChatClientAgent.cs @@ -85,7 +85,7 @@ public class OpenAIChatClientAgent : AIAgent /// public sealed override Task RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) @@ -93,7 +93,7 @@ public class OpenAIChatClientAgent : AIAgent /// public sealed override IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs index f100fee198..3deafcdc6f 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/ChatCompletion/ChatClientAgent.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -99,15 +100,15 @@ public sealed class ChatClientAgent : AIAgent /// public override async Task RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - _ = Throw.IfNull(messages); + var inputMessages = Throw.IfNull(messages) as IReadOnlyCollection ?? messages.ToList(); (AgentThread safeThread, ChatOptions? chatOptions, List threadMessages) = - await this.PrepareThreadAndMessagesAsync(thread, messages, options, cancellationToken).ConfigureAwait(false); + await this.PrepareThreadAndMessagesAsync(thread, inputMessages, options, cancellationToken).ConfigureAwait(false); var agentName = this.GetLoggingAgentName(); @@ -115,14 +116,14 @@ public sealed class ChatClientAgent : AIAgent ChatResponse chatResponse = await this.ChatClient.GetResponseAsync(threadMessages, chatOptions, cancellationToken).ConfigureAwait(false); - this._logger.LogAgentChatClientInvokedAgent(nameof(RunAsync), this.Id, agentName, this._chatClientType, messages.Count); + this._logger.LogAgentChatClientInvokedAgent(nameof(RunAsync), this.Id, agentName, this._chatClientType, inputMessages.Count); // We can derive the type of supported thread from whether we have a conversation id, // so let's update it and set the conversation id for the service thread case. this.UpdateThreadWithTypeAndConversationId(safeThread, chatResponse.ConversationId); // Only notify the thread of new messages if the chatResponse was successful to avoid inconsistent messages state in the thread. - await NotifyThreadOfNewMessagesAsync(safeThread, messages, cancellationToken).ConfigureAwait(false); + await NotifyThreadOfNewMessagesAsync(safeThread, inputMessages, cancellationToken).ConfigureAwait(false); // Ensure that the author name is set for each message in the response. foreach (ChatMessage chatResponseMessage in chatResponse.Messages) @@ -140,12 +141,12 @@ public sealed class ChatClientAgent : AIAgent /// public override async IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - var inputMessages = Throw.IfNull(messages); + var inputMessages = Throw.IfNull(messages) as IReadOnlyCollection ?? messages.ToList(); (AgentThread safeThread, ChatOptions? chatOptions, List threadMessages) = await this.PrepareThreadAndMessagesAsync(thread, inputMessages, options, cancellationToken).ConfigureAwait(false); @@ -334,7 +335,7 @@ public sealed class ChatClientAgent : AIAgent /// A tuple containing the thread, chat options, and thread messages. private async Task<(AgentThread AgentThread, ChatOptions? ChatOptions, List ThreadMessages)> PrepareThreadAndMessagesAsync( AgentThread? thread, - IReadOnlyCollection inputMessages, + IEnumerable inputMessages, AgentRunOptions? runOptions, CancellationToken cancellationToken) { diff --git a/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs b/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs index 7f8b46c82b..90f547a1c2 100644 --- a/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs +++ b/dotnet/src/Microsoft.Extensions.AI.Agents/OpenTelemetryAgent.cs @@ -123,23 +123,23 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable /// public override async Task RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { - _ = Throw.IfNull(messages); + var inputMessages = Throw.IfNull(messages) as IReadOnlyCollection ?? messages.ToList(); - using Activity? activity = this.CreateAndConfigureActivity(OpenTelemetryConsts.GenAI.Operation.NameValues.InvokeAgent, messages, thread); + using Activity? activity = this.CreateAndConfigureActivity(OpenTelemetryConsts.GenAI.Operation.NameValues.InvokeAgent, inputMessages, thread); Stopwatch? stopwatch = this._operationDurationHistogram.Enabled ? Stopwatch.StartNew() : null; - this.LogChatMessages(messages); + this.LogChatMessages(inputMessages); AgentRunResponse? response = null; Exception? error = null; try { - response = await base.RunAsync(messages, thread, options, cancellationToken).ConfigureAwait(false); + response = await base.RunAsync(inputMessages, thread, options, cancellationToken).ConfigureAwait(false); return response; } catch (Exception ex) @@ -149,30 +149,30 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable } finally { - this.TraceResponse(activity, response, error, stopwatch, messages.Count, isStreaming: false); + this.TraceResponse(activity, response, error, stopwatch, inputMessages.Count, isStreaming: false); } } /// public override async IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - _ = Throw.IfNull(messages); + var inputMessages = Throw.IfNull(messages) as IReadOnlyCollection ?? messages.ToList(); - using Activity? activity = this.CreateAndConfigureActivity(OpenTelemetryConsts.GenAI.Operation.NameValues.InvokeAgent, messages, thread); + using Activity? activity = this.CreateAndConfigureActivity(OpenTelemetryConsts.GenAI.Operation.NameValues.InvokeAgent, inputMessages, thread); Stopwatch? stopwatch = this._operationDurationHistogram.Enabled ? Stopwatch.StartNew() : null; IAsyncEnumerable updates; try { - updates = base.RunStreamingAsync(messages, thread, options, cancellationToken); + updates = base.RunStreamingAsync(inputMessages, thread, options, cancellationToken); } catch (Exception ex) { - this.TraceResponse(activity, response: null, ex, stopwatch, messages.Count, isStreaming: true); + this.TraceResponse(activity, response: null, ex, stopwatch, inputMessages.Count, isStreaming: true); throw; } @@ -206,7 +206,7 @@ public sealed partial class OpenTelemetryAgent : DelegatingAIAgent, IDisposable } finally { - this.TraceResponse(activity, trackedUpdates.ToAgentRunResponse(), error, stopwatch, messages.Count, isStreaming: true); + this.TraceResponse(activity, trackedUpdates.ToAgentRunResponse(), error, stopwatch, inputMessages.Count, isStreaming: true); await responseEnumerator.DisposeAsync().ConfigureAwait(false); } } diff --git a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/MockAgent.cs b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/MockAgent.cs index bf0a428aa3..09ddd5c0cb 100644 --- a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/MockAgent.cs +++ b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/MockAgent.cs @@ -36,7 +36,7 @@ internal sealed class MockAgent(int index) : AIAgent return new AgentThread() { ConversationId = Guid.NewGuid().ToString() }; } - public override Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { this.InvokeCount++; if (thread == null) @@ -48,7 +48,7 @@ internal sealed class MockAgent(int index) : AIAgent return Task.FromResult(new AgentRunResponse(messages: [.. this.Response])); } - public override IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { this.InvokeCount++; diff --git a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/OrchestrationResultTests.cs b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/OrchestrationResultTests.cs index 9d9f238cca..70c1601425 100644 --- a/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/OrchestrationResultTests.cs +++ b/dotnet/tests/Microsoft.Agents.Orchestration.UnitTests/OrchestrationResultTests.cs @@ -80,18 +80,18 @@ public class OrchestrationResultTests private sealed class MockOrchestratingAgent() : OrchestratingAgent([new MockAgent()]) { - protected override Task RunCoreAsync(IReadOnlyCollection messages, OrchestratingAgentContext context, CancellationToken cancellationToken) => + protected override Task RunCoreAsync(IEnumerable messages, OrchestratingAgentContext context, CancellationToken cancellationToken) => throw new NotSupportedException(); - protected override Task ResumeCoreAsync(JsonElement checkpointState, IReadOnlyCollection newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) => + protected override Task ResumeCoreAsync(JsonElement checkpointState, IEnumerable newMessages, OrchestratingAgentContext context, CancellationToken cancellationToken) => throw new NotSupportedException(); } private sealed class MockAgent : AIAgent { - public override Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotSupportedException(); - public override IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => + public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) => throw new NotSupportedException(); } } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs index 294b58a4de..ef0978db75 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/RepresentationTests.cs @@ -22,12 +22,12 @@ public class RepresentationTests private sealed class TestAgent : AIAgent { - public override Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } - public override IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs index 47e5128cd9..18a855c803 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/Sample/06_GroupChat_Workflow.cs @@ -95,7 +95,7 @@ internal sealed class HelloAgent(string id = nameof(HelloAgent)) : AIAgent public override string Id => id; public override string? Name => id; - public override async Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public override async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { IEnumerable update = [ await this.RunStreamingAsync(messages, thread, options, cancellationToken) @@ -105,7 +105,7 @@ internal sealed class HelloAgent(string id = nameof(HelloAgent)) : AIAgent return update.ToAgentRunResponse(); } - public override async IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { AgentRunResponseUpdate response = new(ChatRole.Assistant, "Hello World!") { @@ -126,7 +126,7 @@ internal sealed class EchoAgent(string id = nameof(EchoAgent)) : AIAgent public override string Id => id; public override string? Name => id; - public override async Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public override async Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { IEnumerable update = [ await this.RunStreamingAsync(messages, thread, options, cancellationToken) @@ -136,15 +136,17 @@ internal sealed class EchoAgent(string id = nameof(EchoAgent)) : AIAgent return update.ToAgentRunResponse(); } - public override async IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - if (messages.Count == 0) + var messagesList = messages as IReadOnlyCollection ?? messages.ToList(); + + if (messagesList.Count == 0) { throw new ArgumentException("No messages provided to echo.", nameof(messages)); } StringBuilder collectedText = new(Prefix); - foreach (string messageText in messages.Select(message => message.Text) + foreach (string messageText in messagesList.Select(message => message.Text) .Where(text => !string.IsNullOrEmpty(text))) { collectedText.AppendLine(messageText); diff --git a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs index 959ec2c39c..5e67966711 100644 --- a/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs +++ b/dotnet/tests/Microsoft.Agents.Workflows.UnitTests/SpecializedExecutorSmokeTests.cs @@ -56,7 +56,7 @@ public class SpecializedExecutorSmokeTests public List Messages { get; } = Validate(messages) ?? []; - public override Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { return Task.FromResult(new AgentRunResponse(this.Messages) { @@ -65,7 +65,7 @@ public class SpecializedExecutorSmokeTests }); } - public override async IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) + public override async IAsyncEnumerable RunStreamingAsync(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.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs index 411e0bfb7e..04cd8584c8 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Abstractions.UnitTests/AIAgentTests.cs @@ -361,13 +361,13 @@ public class AIAgentTests private sealed class MockAgent : AIAgent { - public static new Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IReadOnlyCollection messages, CancellationToken cancellationToken) + public static new Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IEnumerable messages, CancellationToken cancellationToken) { return AIAgent.NotifyThreadOfNewMessagesAsync(thread, messages, cancellationToken); } public override Task RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) @@ -376,7 +376,7 @@ public class AIAgentTests } public override IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentActorTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentActorTests.cs index 1584960e9b..d6eeb63b7a 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentActorTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.Hosting.UnitTests/AgentActorTests.cs @@ -175,7 +175,7 @@ public class AgentActorTests public bool RunStreamingAsyncCalled { get; private set; } public AgentThread? ThreadUsedInRunStreamingAsync { get; private set; } - public override Task RunAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) + public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) { this.ThreadUsedInRunStreamingAsync = thread; return Task.FromResult(new AgentRunResponse @@ -184,7 +184,7 @@ public class AgentActorTests }); } - public override async IAsyncEnumerable RunStreamingAsync(IReadOnlyCollection messages, AgentThread? thread = null, AgentRunOptions? options = null, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default) + public override async IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default) { this.RunStreamingAsyncCalled = true; this.ThreadUsedInRunStreamingAsync = thread; diff --git a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentAIFunctionFactoryTests.cs b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentAIFunctionFactoryTests.cs index d1c10c5f40..014c9b6759 100644 --- a/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentAIFunctionFactoryTests.cs +++ b/dotnet/tests/Microsoft.Extensions.AI.Agents.UnitTests/AgentAIFunctionFactoryTests.cs @@ -306,7 +306,7 @@ public class AgentAIFunctionFactoryTests public int RunAsyncCallCount { get; private set; } public override Task RunAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) @@ -324,7 +324,7 @@ public class AgentAIFunctionFactoryTests } public override async IAsyncEnumerable RunStreamingAsync( - IReadOnlyCollection messages, + IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)