// Copyright (c) Microsoft. All rights reserved. using Microsoft.Agents.AI.DurableTask; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Builder; using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; using Microsoft.DurableTask; using Microsoft.DurableTask.Worker; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Microsoft.Agents.AI.Hosting.AzureFunctions; /// /// Extension methods for configuring durable options (agents and workflows). /// public static class DurableOptionsExtensions { /// /// Configures durable agents and workflows in a unified way. /// /// The Functions application builder. /// A delegate to configure the durable options. /// The Functions application builder for method chaining. /// /// This method provides a unified configuration point for both durable agents and workflows. /// Agents configured here will be automatically registered when referenced in workflows. /// public static FunctionsApplicationBuilder ConfigureDurableOptions( this FunctionsApplicationBuilder builder, Action configure) { ArgumentNullException.ThrowIfNull(configure); DurableOptions options = new(); configure(options); // Register the unified DurableOptions for components that need both agents and workflows builder.Services.AddSingleton(options); // Register AgentsOption for backward compatibility. Can be removed if needed, after syncing with team. builder.Services.AddSingleton(options.Agents); // Configure agents using the existing infrastructure builder.Services.ConfigureDurableAgents(agentOpts => { // Copy agent registrations from the unified options to the service-level options foreach (KeyValuePair> agentFactory in options.Agents.GetAgentFactories()) { agentOpts.AddAIAgentFactory( agentFactory.Key, agentFactory.Value, options.Agents.GetTimeToLive(agentFactory.Key)); } // Copy TTL settings agentOpts.DefaultTimeToLive = options.Agents.DefaultTimeToLive; agentOpts.MinimumTimeToLiveSignalDelay = options.Agents.MinimumTimeToLiveSignalDelay; }); 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.RunWorkflowOrechstrtationHttpFunctionEntryPoint, StringComparison.Ordinal) || string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.InvokeWorkflowActivityFunctionEntryPoint, StringComparison.Ordinal) || string.Equals(context.FunctionDefinition.EntryPoint, BuiltInFunctions.RunAgentEntityFunctionEntryPoint, StringComparison.Ordinal)); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.ConfigureDurableWorker().AddTasks(t => t.AddOrchestratorFunc>( "WorkflowRunnerOrchestration", async (tc, inputBindingData) => { FunctionContext? functionContext = tc.GetFunctionContext(); if (functionContext == null) { throw new InvalidOperationException("FunctionContext is not available in the orchestration context."); } var logger = tc.CreateReplaySafeLogger("WorkflowRunnerOrchestration"); DurableWorkflowRunner runner = functionContext.InstanceServices.GetRequiredService(); var orchestrationResult = await runner.RunWorkflowOrchestrationAsync(tc, inputBindingData, logger); if (logger.IsEnabled(LogLevel.Information)) { logger.LogInformation("Durable workflow orchestration completed. Result:{Result}", string.Join(",", orchestrationResult)); } return orchestrationResult; })); builder.Services.AddSingleton(); return builder; } }