.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 <jaalber@microsoft.com>
This commit is contained in:
Copilot
2025-10-29 12:02:27 -07:00
committed by GitHub
Unverified
parent 8a3cab38ee
commit a2ee840eef
@@ -8,23 +8,40 @@ using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI.Workflows;
internal class ChatProtocolExecutorOptions
/// <summary>
/// Provides configuration options for <see cref="ChatProtocolExecutor"/>.
/// </summary>
public class ChatProtocolExecutorOptions
{
/// <summary>
/// Gets or sets the chat role to use when converting string messages to <see cref="ChatMessage"/> instances.
/// If set, the executor will accept string messages and convert them to chat messages with this role.
/// </summary>
public ChatRole? StringMessageChatRole { get; set; }
}
// TODO: Make this a public type (in a later PR; todo: make an issue)
internal abstract class ChatProtocolExecutor : StatefulExecutor<List<ChatMessage>>
/// <summary>
/// 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.
/// </summary>
public abstract class ChatProtocolExecutor : StatefulExecutor<List<ChatMessage>>
{
private readonly static Func<List<ChatMessage>> s_initFunction = () => [];
private readonly ChatRole? _stringMessageChatRole;
internal ChatProtocolExecutor(string id, ChatProtocolExecutorOptions? options = null, bool declareCrossRunShareable = false)
/// <summary>
/// Initializes a new instance of the <see cref="ChatProtocolExecutor"/> class.
/// </summary>
/// <param name="id">The unique identifier for this executor instance. Cannot be null or empty.</param>
/// <param name="options">Optional configuration settings for the executor. If null, default options are used.</param>
/// <param name="declareCrossRunShareable">Declare that this executor may be used simultaneously by multiple runs safely.</param>
protected ChatProtocolExecutor(string id, ChatProtocolExecutorOptions? options = null, bool declareCrossRunShareable = false)
: base(id, () => [], declareCrossRunShareable: declareCrossRunShareable)
{
this._stringMessageChatRole = options?.StringMessageChatRole;
}
/// <inheritdoc/>
protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder)
{
if (this._stringMessageChatRole.HasValue)
@@ -40,6 +57,13 @@ internal abstract class ChatProtocolExecutor : StatefulExecutor<List<ChatMessage
.AddHandler<TurnToken>(this.TakeTurnAsync);
}
/// <summary>
/// Adds a single chat message to the accumulated messages for the current turn.
/// </summary>
/// <param name="message">The chat message to add.</param>
/// <param name="context">The workflow context in which the executor executes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
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<List<ChatMessage
}
}
/// <summary>
/// Adds multiple chat messages to the accumulated messages for the current turn.
/// </summary>
/// <param name="messages">The collection of chat messages to add.</param>
/// <param name="context">The workflow context in which the executor executes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
protected ValueTask AddMessagesAsync(IEnumerable<ChatMessage> messages, IWorkflowContext context, CancellationToken cancellationToken = default)
{
return this.InvokeWithStateAsync(ForwardMessageAsync, context, cancellationToken: cancellationToken);
@@ -64,6 +95,13 @@ internal abstract class ChatProtocolExecutor : StatefulExecutor<List<ChatMessage
}
}
/// <summary>
/// Handles a turn token by processing all accumulated chat messages and then resetting the message state.
/// </summary>
/// <param name="token">The turn token that triggers message processing.</param>
/// <param name="context">The workflow context in which the executor executes.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
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<List<ChatMessage
}
}
/// <summary>
/// When overridden in a derived class, processes the accumulated chat messages for a single turn.
/// </summary>
/// <param name="messages">The list of chat messages accumulated since the last turn.</param>
/// <param name="context">The workflow context in which the executor executes.</param>
/// <param name="emitEvents">Indicates whether events should be emitted during processing. If null, the default behavior is used.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param>
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
protected abstract ValueTask TakeTurnAsync(List<ChatMessage> messages, IWorkflowContext context, bool? emitEvents, CancellationToken cancellationToken = default);
}