// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Agents.AI.DurableTask;
using Microsoft.Agents.AI.DurableTask.Workflows;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
namespace Microsoft.Agents.AI.Hosting.AzureFunctions;
///
/// Extension methods for the class.
///
public static class FunctionsApplicationBuilderExtensions
{
///
/// Configures the application to use durable agents with a builder pattern.
///
/// The functions application builder.
/// A delegate to configure the durable agents.
/// The functions application builder.
public static FunctionsApplicationBuilder ConfigureDurableAgents(
this FunctionsApplicationBuilder builder,
Action configure)
{
ArgumentNullException.ThrowIfNull(configure);
// Create/get shared options BEFORE the DurableTask library call so it can find them.
FunctionsDurableOptions sharedOptions = GetOrCreateSharedOptions(builder.Services);
// The main agent services registration is done in Microsoft.DurableTask.Agents.
builder.Services.ConfigureDurableAgents(configure);
// Ensure all agents registered through this path have default FunctionsAgentOptions.
// This distinguishes them from agents auto-registered by workflows.
DurableAgentsOptionsExtensions.EnsureDefaultOptionsForAll(sharedOptions.Agents.GetAgentFactories().Keys);
builder.Services.TryAddSingleton(_ =>
new DefaultFunctionsAgentOptionsProvider(DurableAgentsOptionsExtensions.GetAgentOptionsSnapshot()));
builder.Services.AddSingleton();
// Handling of built-in function execution for Agent HTTP, MCP tool, or Entity invocations.
builder.UseWhen(static context =>
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RunAgentHttpFunctionEntryPoint, StringComparison.Ordinal) ||
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RunAgentMcpToolFunctionEntryPoint, StringComparison.Ordinal) ||
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RunAgentEntityFunctionEntryPoint, StringComparison.Ordinal));
builder.Services.AddSingleton();
return builder;
}
///
/// Configures durable options for the functions application, allowing customization of Durable Task framework
/// settings.
///
/// This method ensures that a single shared instance is used across all
/// configuration calls. If any workflows have been added, it configures the necessary orchestrations and registers
/// required middleware.
/// The functions application builder to configure. Cannot be null.
/// An action that configures the instance. Cannot be null.
/// The updated instance, enabling method chaining.
public static FunctionsApplicationBuilder ConfigureDurableOptions(
this FunctionsApplicationBuilder builder,
Action configure)
{
ArgumentNullException.ThrowIfNull(builder);
ArgumentNullException.ThrowIfNull(configure);
// Ensure FunctionsDurableOptions is registered BEFORE the core extension creates a plain DurableOptions
FunctionsDurableOptions sharedOptions = GetOrCreateSharedOptions(builder.Services);
builder.Services.ConfigureDurableOptions(configure);
if (DurableAgentsOptionsExtensions.GetAgentOptionsSnapshot().Count > 0)
{
builder.Services.TryAddSingleton(_ =>
new DefaultFunctionsAgentOptionsProvider(DurableAgentsOptionsExtensions.GetAgentOptionsSnapshot()));
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton());
}
if (sharedOptions.Workflows.Workflows.Count > 0)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton());
}
EnsureMiddlewareRegistered(builder);
return builder;
}
///
/// Configures durable workflow support for the specified Azure Functions application builder.
///
/// The instance to configure for durable workflows.
/// An action that configures the , allowing customization of durable workflow behavior.
/// The updated instance, enabling method chaining.
public static FunctionsApplicationBuilder ConfigureDurableWorkflows(
this FunctionsApplicationBuilder builder,
Action configure)
{
ArgumentNullException.ThrowIfNull(configure);
return builder.ConfigureDurableOptions(options => configure(options.Workflows));
}
private static void EnsureMiddlewareRegistered(FunctionsApplicationBuilder builder)
{
// Guard against registering the middleware filter multiple times in the pipeline.
if (builder.Services.Any(d => d.ServiceType == typeof(BuiltInFunctionExecutor)))
{
return;
}
builder.UseWhen(static context =>
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RunAgentHttpFunctionEntryPoint, StringComparison.Ordinal) ||
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RunAgentMcpToolFunctionEntryPoint, StringComparison.Ordinal) ||
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RunAgentEntityFunctionEntryPoint, StringComparison.Ordinal) ||
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RunWorkflowOrchestrationHttpFunctionEntryPoint, StringComparison.Ordinal) ||
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RunWorkflowOrchestrationFunctionEntryPoint, StringComparison.Ordinal) ||
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.InvokeWorkflowActivityFunctionEntryPoint, StringComparison.Ordinal) ||
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.GetWorkflowStatusHttpFunctionEntryPoint, StringComparison.Ordinal) ||
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RespondToWorkflowHttpFunctionEntryPoint, StringComparison.Ordinal) ||
string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RunWorkflowMcpToolFunctionEntryPoint, StringComparison.Ordinal)
);
builder.Services.TryAddSingleton();
}
///
/// Gets or creates a shared instance from the service collection.
///
private static FunctionsDurableOptions GetOrCreateSharedOptions(IServiceCollection services)
{
ServiceDescriptor? existingDescriptor = services.FirstOrDefault(
d => d.ServiceType == typeof(DurableOptions) && d.ImplementationInstance is not null);
if (existingDescriptor?.ImplementationInstance is FunctionsDurableOptions existing)
{
return existing;
}
FunctionsDurableOptions options = new();
services.AddSingleton(options);
services.AddSingleton(options);
return options;
}
}