// Copyright (c) Microsoft. All rights reserved.
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows;
///
/// Extension methods for configuring executors and functions as instances.
///
public static class ExecutorBindingExtensions
{
///
/// Configures an instance for use in a workflow.
///
///
/// Note that Executor Ids must be unique within a workflow.
///
/// The executor instance.
/// An instance wrapping the specified .
public static ExecutorBinding BindExecutor(this Executor executor)
=> new ExecutorInstanceBinding(executor);
///
/// Configures a factory method for creating an of type , using the
/// type name as the id.
///
///
/// Note that Executor Ids must be unique within a workflow.
///
/// Although this will generally result in a delay-instantiated once messages are available
/// for it, it will be instantiated if a for the is requested,
/// and it is the starting executor.
///
/// The type of the resulting executor
/// The factory method.
/// An instance that resolves to the result of the factory call when messages get sent to it.
public static ExecutorBinding BindExecutor(this Func> factoryAsync)
where TExecutor : Executor
=> BindExecutor((config, sessionId) => factoryAsync(config.Id, sessionId), id: typeof(TExecutor).Name, options: null);
///
/// Configures a factory method for creating an of type , using the
/// type name as the id.
///
///
/// Note that Executor Ids must be unique within a workflow.
///
/// Although this will generally result in a delay-instantiated once messages are available
/// for it, it will be instantiated if a for the is requested,
/// and it is the starting executor.
///
/// The type of the resulting executor
/// The factory method.
/// An instance that resolves to the result of the factory call when messages get sent to it.
[Obsolete("Use BindExecutor() instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static ExecutorBinding ConfigureFactory(this Func> factoryAsync)
where TExecutor : Executor
=> factoryAsync.BindExecutor();
///
/// Configures a factory method for creating an of type , with
/// the specified id.
///
///
/// Although this will generally result in a delay-instantiated once messages are available
/// for it, it will be instantiated if a for the is requested,
/// and it is the starting executor.
///
/// The type of the resulting executor
/// The factory method.
/// An id for the executor to be instantiated.
/// An instance that resolves to the result of the factory call when messages get sent to it.
public static ExecutorBinding BindExecutor(this Func> factoryAsync, string id)
where TExecutor : Executor
=> BindExecutor((_, sessionId) => factoryAsync(id, sessionId), id, options: null);
///
/// Configures a factory method for creating an of type , with
/// the specified id.
///
///
/// Although this will generally result in a delay-instantiated once messages are available
/// for it, it will be instantiated if a for the is requested,
/// and it is the starting executor.
///
/// The type of the resulting executor
/// The factory method.
/// An id for the executor to be instantiated.
/// An instance that resolves to the result of the factory call when messages get sent to it.
[Obsolete("Use BindExecutor() instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static ExecutorBinding ConfigureFactory(this Func> factoryAsync, string id)
where TExecutor : Executor
=> factoryAsync.BindExecutor(id);
///
/// Configures a factory method for creating an of type , with
/// the specified id and options.
///
///
/// Although this will generally result in a delay-instantiated once messages are available
/// for it, it will be instantiated if a for the is requested,
/// and it is the starting executor.
///
/// The type of the resulting executor
/// The type of options object to be passed to the factory method.
/// The factory method.
/// An id for the executor to be instantiated.
/// An optional parameter specifying the options.
/// An instance that resolves to the result of the factory call when messages get sent to it.
public static ExecutorBinding BindExecutor(this Func, string, ValueTask> factoryAsync, string id, TOptions? options = null)
where TExecutor : Executor
where TOptions : ExecutorOptions
{
Configured configured = new(factoryAsync, id, options);
return new ConfiguredExecutorBinding(configured.Super(), typeof(TExecutor));
}
///
/// Configures a factory method for creating an of type , with
/// the specified id and options.
///
///
/// Although this will generally result in a delay-instantiated once messages are available
/// for it, it will be instantiated if a for the is requested,
/// and it is the starting executor.
///
/// The type of the resulting executor
/// The type of options object to be passed to the factory method.
/// The factory method.
/// An id for the executor to be instantiated.
/// An optional parameter specifying the options.
/// An instance that resolves to the result of the factory call when messages get sent to it.
[Obsolete("Use BindExecutor() instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static ExecutorBinding ConfigureFactory(this Func, string, ValueTask> factoryAsync, string id, TOptions? options = null)
where TExecutor : Executor
where TOptions : ExecutorOptions
=> factoryAsync.BindExecutor(id, options);
private static ConfiguredExecutorBinding ToBinding(this FunctionExecutor executor, Delegate raw)
=> new(Configured.FromInstance(executor, raw: raw)
.Super, Executor>(),
typeof(FunctionExecutor));
private static ConfiguredExecutorBinding ToBinding(this FunctionExecutor executor, Delegate raw)
=> new(Configured.FromInstance(executor, raw: raw)
.Super, Executor>(),
typeof(FunctionExecutor));
///
/// Configures a sub-workflow executor for the specified workflow, using the provided identifier and options.
///
/// The workflow instance to be executed as a sub-workflow. Cannot be null.
/// A unique identifier for the sub-workflow execution. Used to distinguish this sub-workflow instance.
/// Optional configuration options for the sub-workflow executor. If null, default options are used.
/// An ExecutorRegistration instance representing the configured sub-workflow executor.
[Obsolete("Use BindAsExecutor() instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static ExecutorBinding ConfigureSubWorkflow(this Workflow workflow, string id, ExecutorOptions? options = null)
=> workflow.BindAsExecutor(id, options);
///
/// Configures a sub-workflow executor for the specified workflow, using the provided identifier and options.
///
/// The workflow instance to be executed as a sub-workflow. Cannot be null.
/// A unique identifier for the sub-workflow execution. Used to distinguish this sub-workflow instance.
/// Optional configuration options for the sub-workflow executor. If null, default options are used.
/// An instance representing the configured sub-workflow executor.
public static ExecutorBinding BindAsExecutor(this Workflow workflow, string id, ExecutorOptions? options = null)
=> new SubworkflowBinding(workflow, id, options);
///
/// Configures a function-based asynchronous message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// A delegate that defines the asynchronous function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func messageHandlerAsync, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> new FunctionExecutor(id, messageHandlerAsync, options, declareCrossRunShareable: threadsafe).ToBinding(messageHandlerAsync);
///
/// Configures a function-based asynchronous message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// A delegate that defines the asynchronous function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func messageHandlerAsync, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Func)((input, _, __) => messageHandlerAsync(input)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based asynchronous message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// A delegate that defines the asynchronous function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func messageHandlerAsync, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Func)((input, ctx, __) => messageHandlerAsync(input, ctx)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based asynchronous message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// A delegate that defines the asynchronous function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func messageHandlerAsync, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Func)((input, _, ct) => messageHandlerAsync(input, ct)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// A delegate that defines the function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Action messageHandler, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> new FunctionExecutor(id, messageHandler, options, declareCrossRunShareable: threadsafe).ToBinding(messageHandler);
///
/// Configures a function-based message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// A delegate that defines the function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Action messageHandler, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Action)((input, _, __) => messageHandler(input)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// A delegate that defines the function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Action messageHandler, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Action)((input, ctx, __) => messageHandler(input, ctx)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// A delegate that defines the function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Action messageHandler, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Action)((input, _, ct) => messageHandler(input, ct)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based asynchronous message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// The type of output message.
/// A delegate that defines the asynchronous function to execute for each input message.
/// A unique identifier for the executor.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func> messageHandlerAsync, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> new FunctionExecutor(Throw.IfNull(id), messageHandlerAsync, options, declareCrossRunShareable: threadsafe).ToBinding(messageHandlerAsync);
///
/// Configures a function-based asynchronous message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// The type of output message.
/// A delegate that defines the asynchronous function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func> messageHandlerAsync, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Func>)((input, _, __) => messageHandlerAsync(input)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based asynchronous message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// The type of output message.
/// A delegate that defines the asynchronous function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func> messageHandlerAsync, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Func>)((input, ctx, __) => messageHandlerAsync(input, ctx)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based asynchronous message handler as an executor with the specified identifier and
/// options.
///
/// The type of input message.
/// The type of output message.
/// A delegate that defines the asynchronous function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func> messageHandlerAsync, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Func>)((input, _, ct) => messageHandlerAsync(input, ct)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based message handler as an executor with the specified identifier and options.
///
/// The type of input message.
/// The type of output message.
/// A delegate that defines the function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func messageHandler, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> new FunctionExecutor(id, messageHandler, options, declareCrossRunShareable: threadsafe).ToBinding(messageHandler);
///
/// Configures a function-based message handler as an executor with the specified identifier and options.
///
/// The type of input message.
/// The type of output message.
/// A delegate that defines the function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func messageHandler, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Func)((input, _, __) => messageHandler(input)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based message handler as an executor with the specified identifier and options.
///
/// The type of input message.
/// The type of output message.
/// A delegate that defines the function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func messageHandler, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Func)((input, ctx, __) => messageHandler(input, ctx)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based message handler as an executor with the specified identifier and options.
///
/// The type of input message.
/// The type of output message.
/// A delegate that defines the function to execute for each input message.
/// An optional unique identifier for the executor. If null, will use the function argument as an id.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func messageHandler, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> ((Func)((input, _, ct) => messageHandler(input, ct)))
.BindAsExecutor(id, options, threadsafe);
///
/// Configures a function-based aggregating executor with the specified identifier and options.
///
/// The type of input message.
/// The type of the accumulating object.
/// A delegate the defines the aggregation procedure
/// A unique identifier for the executor.
/// Configuration options for the executor. If null, default options will be used.
/// Declare that the message handler may be used simultaneously by multiple runs concurrently.
/// An instance that wraps the provided asynchronous message handler and configuration.
public static ExecutorBinding BindAsExecutor(this Func aggregatorFunc, string id, ExecutorOptions? options = null, bool threadsafe = false)
=> new AggregatingExecutor(id, aggregatorFunc, options, declareCrossRunShareable: threadsafe);
///
/// Configure an as an executor for use in a workflow.
///
/// The agent instance.
/// Specifies whether the agent should emit streaming events.
/// An instance that wraps the provided agent.
public static ExecutorBinding BindAsExecutor(this AIAgent agent, bool emitEvents)
=> new AIAgentBinding(agent, emitEvents);
///
/// Configure an as an executor for use in a workflow.
///
/// The agent instance.
/// Optional configuration options for the AI agent executor. If null, default options are used.
/// An instance that wraps the provided agent.
public static ExecutorBinding BindAsExecutor(this AIAgent agent, AIAgentHostOptions? options = null)
=> new AIAgentBinding(agent, options);
///
/// Configure a as an executor for use in a workflow.
///
/// The port configuration.
/// Specifies whether the port should accept requests already wrapped in
/// .
/// A instance that wraps the provided port.
public static ExecutorBinding BindAsExecutor(this RequestPort port, bool allowWrappedRequests = true)
=> new RequestPortBinding(port, allowWrappedRequests);
}