From a2ee840eef8a7bdc341f4e25ac08c2e11bb13c88 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:02:27 -0700 Subject: [PATCH] .NET: Make ChatProtocolExecutor public (#1781) * Initial plan * Make ChatProtocolExecutor and ChatProtocolExecutorOptions public with XML documentation Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com> * Address PR review feedback - remove remarks, fix parameter descriptions Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com> * ci: Empty Commit to kick over CI --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lokitoth <6936551+lokitoth@users.noreply.github.com> Co-authored-by: Jacob Alber --- .../ChatProtocolExecutor.cs | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/ChatProtocolExecutor.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/ChatProtocolExecutor.cs index 480c2e0ce3..56fb326338 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/ChatProtocolExecutor.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/ChatProtocolExecutor.cs @@ -8,23 +8,40 @@ using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI.Workflows; -internal class ChatProtocolExecutorOptions +/// +/// Provides configuration options for . +/// +public class ChatProtocolExecutorOptions { + /// + /// Gets or sets the chat role to use when converting string messages to instances. + /// If set, the executor will accept string messages and convert them to chat messages with this role. + /// public ChatRole? StringMessageChatRole { get; set; } } -// TODO: Make this a public type (in a later PR; todo: make an issue) -internal abstract class ChatProtocolExecutor : StatefulExecutor> +/// +/// Provides a base class for executors that implement the Agent Workflow Chat Protocol. +/// This executor maintains a list of chat messages and processes them when a turn is taken. +/// +public abstract class ChatProtocolExecutor : StatefulExecutor> { private readonly static Func> s_initFunction = () => []; private readonly ChatRole? _stringMessageChatRole; - internal ChatProtocolExecutor(string id, ChatProtocolExecutorOptions? options = null, bool declareCrossRunShareable = false) + /// + /// Initializes a new instance of the class. + /// + /// The unique identifier for this executor instance. Cannot be null or empty. + /// Optional configuration settings for the executor. If null, default options are used. + /// Declare that this executor may be used simultaneously by multiple runs safely. + protected ChatProtocolExecutor(string id, ChatProtocolExecutorOptions? options = null, bool declareCrossRunShareable = false) : base(id, () => [], declareCrossRunShareable: declareCrossRunShareable) { this._stringMessageChatRole = options?.StringMessageChatRole; } + /// protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) { if (this._stringMessageChatRole.HasValue) @@ -40,6 +57,13 @@ internal abstract class ChatProtocolExecutor : StatefulExecutor(this.TakeTurnAsync); } + /// + /// Adds a single chat message to the accumulated messages for the current turn. + /// + /// The chat message to add. + /// The workflow context in which the executor executes. + /// The to monitor for cancellation requests. + /// A representing the asynchronous operation. protected ValueTask AddMessageAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default) { return this.InvokeWithStateAsync(ForwardMessageAsync, context, cancellationToken: cancellationToken); @@ -52,6 +76,13 @@ internal abstract class ChatProtocolExecutor : StatefulExecutor + /// Adds multiple chat messages to the accumulated messages for the current turn. + /// + /// The collection of chat messages to add. + /// The workflow context in which the executor executes. + /// The to monitor for cancellation requests. + /// A representing the asynchronous operation. protected ValueTask AddMessagesAsync(IEnumerable messages, IWorkflowContext context, CancellationToken cancellationToken = default) { return this.InvokeWithStateAsync(ForwardMessageAsync, context, cancellationToken: cancellationToken); @@ -64,6 +95,13 @@ internal abstract class ChatProtocolExecutor : StatefulExecutor + /// Handles a turn token by processing all accumulated chat messages and then resetting the message state. + /// + /// The turn token that triggers message processing. + /// The workflow context in which the executor executes. + /// The to monitor for cancellation requests. + /// A representing the asynchronous operation. public ValueTask TakeTurnAsync(TurnToken token, IWorkflowContext context, CancellationToken cancellationToken = default) { return this.InvokeWithStateAsync(InvokeTakeTurnAsync, context, cancellationToken: cancellationToken); @@ -81,5 +119,13 @@ internal abstract class ChatProtocolExecutor : StatefulExecutor + /// When overridden in a derived class, processes the accumulated chat messages for a single turn. + /// + /// The list of chat messages accumulated since the last turn. + /// The workflow context in which the executor executes. + /// Indicates whether events should be emitted during processing. If null, the default behavior is used. + /// The to monitor for cancellation requests. + /// A representing the asynchronous operation. protected abstract ValueTask TakeTurnAsync(List messages, IWorkflowContext context, bool? emitEvents, CancellationToken cancellationToken = default); }