From 63a22830336116133d5f595cf4be51ebf11f44c4 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 21 Jan 2026 20:19:23 -0800 Subject: [PATCH] Cleanup --- .../DurableWorkflowOptions.cs | 6 +-- .../ExecutorRegistry.cs | 21 ++++++-- .../WorkflowHelper.cs | 12 ++--- ...ableWorkflowFunctionMetadataTransformer.cs | 12 ++--- .../Checkpointing/ExecutorInfo.cs | 22 +------- .../Microsoft.Agents.AI.Workflows/Workflow.cs | 54 ++----------------- 6 files changed, 37 insertions(+), 90 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableWorkflowOptions.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableWorkflowOptions.cs index 25603c4178..c4deeb309c 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableWorkflowOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/DurableWorkflowOptions.cs @@ -79,7 +79,7 @@ public sealed class DurableWorkflowOptions private static void RegisterExecutors(Workflow workflow, ExecutorRegistry registry) { - foreach (KeyValuePair executor in workflow.ReflectExecutors()) + foreach (KeyValuePair executor in workflow.ReflectExecutors()) { int underscoreIndex = executor.Key.IndexOf('_'); string executorName = underscoreIndex > 0 ? executor.Key[..underscoreIndex] : executor.Key; @@ -89,9 +89,9 @@ public sealed class DurableWorkflowOptions private static void RegisterAgenticExecutors(Workflow workflow, DurableAgentsOptions agentOptions) { - foreach (AIAgent agent in workflow.EnumerateAgentExecutors()) + foreach (KeyValuePair executor in workflow.ReflectExecutors()) { - if (agent.Name is not null && !agentOptions.ContainsAgent(agent.Name)) + if (executor.Value.RawValue is AIAgent agent && agent.Name is not null && !agentOptions.ContainsAgent(agent.Name)) { agentOptions.AddAIAgent(agent, workflowOnly: true); } diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/ExecutorRegistry.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/ExecutorRegistry.cs index 26871f76da..41017746b5 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/ExecutorRegistry.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/ExecutorRegistry.cs @@ -39,7 +39,13 @@ internal sealed class ExecutorRegistry ArgumentException.ThrowIfNullOrEmpty(executorId); ArgumentNullException.ThrowIfNull(workflow); - this._executors.TryAdd(executorName, new ExecutorRegistration(executorId, workflow)); + Dictionary bindings = workflow.ReflectExecutors(); + if (!bindings.TryGetValue(executorId, out ExecutorBinding? binding)) + { + throw new InvalidOperationException($"Executor '{executorId}' not found in workflow."); + } + + this._executors.TryAdd(executorName, new ExecutorRegistration(executorId, binding)); } } @@ -47,8 +53,8 @@ internal sealed class ExecutorRegistry /// Represents a registered executor with its associated workflow. /// /// The full executor ID (may include GUID suffix). -/// The workflow containing the executor. -internal sealed record ExecutorRegistration(string ExecutorId, Workflow Workflow) +/// The executor binding from the workflow. +internal sealed record ExecutorRegistration(string ExecutorId, ExecutorBinding Binding) { /// /// Creates an instance of the executor. @@ -56,8 +62,13 @@ internal sealed record ExecutorRegistration(string ExecutorId, Workflow Workflow /// A unique identifier for the run context. /// The cancellation token. /// The created executor instance. - public ValueTask CreateExecutorInstanceAsync(string runId, CancellationToken cancellationToken = default) + public async ValueTask CreateExecutorInstanceAsync(string runId, CancellationToken cancellationToken = default) { - return this.Workflow.CreateExecutorInstanceAsync(this.ExecutorId, runId, cancellationToken); + if (this.Binding.FactoryAsync is null) + { + throw new InvalidOperationException($"Cannot create executor '{this.ExecutorId}': Binding is a placeholder."); + } + + return await this.Binding.FactoryAsync(runId).ConfigureAwait(false); } } diff --git a/dotnet/src/Microsoft.Agents.AI.DurableTask/WorkflowHelper.cs b/dotnet/src/Microsoft.Agents.AI.DurableTask/WorkflowHelper.cs index 600761fda5..2a0cdc586a 100644 --- a/dotnet/src/Microsoft.Agents.AI.DurableTask/WorkflowHelper.cs +++ b/dotnet/src/Microsoft.Agents.AI.DurableTask/WorkflowHelper.cs @@ -87,7 +87,7 @@ public static class WorkflowHelper { ArgumentNullException.ThrowIfNull(workflow); - Dictionary executors = workflow.ReflectExecutors(); + Dictionary executors = workflow.ReflectExecutors(); Dictionary> edges = workflow.ReflectEdges(); WorkflowExecutionPlan plan = new(); @@ -177,9 +177,9 @@ public static class WorkflowHelper { processed.Add(executorId); - if (executors.TryGetValue(executorId, out ExecutorInfo? executorInfo)) + if (executors.TryGetValue(executorId, out ExecutorBinding? executorBinding)) { - bool isAgentic = IsAgentExecutorType(executorInfo.ExecutorType); + bool isAgentic = IsAgentExecutorType(executorBinding.ExecutorType); levelExecutors.Add(new WorkflowExecutorInfo(executorId, isAgentic)); } @@ -202,13 +202,13 @@ public static class WorkflowHelper /// /// The executor type to check. /// true if the executor is an agentic executor; otherwise, false. - internal static bool IsAgentExecutorType(TypeId executorType) + internal static bool IsAgentExecutorType(Type executorType) { // hack for now. In the future, the MAF type could expose something which can help with this. // Check if the type name or assembly indicates it's an agent executor // This includes AgentRunStreamingExecutor, AgentExecutor, ChatClientAgent wrappers, etc. - string typeName = executorType.TypeName; - string assemblyName = executorType.AssemblyName; + string typeName = executorType.FullName ?? executorType.Name; + string assemblyName = executorType.Assembly.GetName().Name ?? string.Empty; return typeName.Contains("AIAgentHostExecutor", StringComparison.OrdinalIgnoreCase) && assemblyName.Contains("Microsoft.Agents.AI", StringComparison.OrdinalIgnoreCase); diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/DurableWorkflowFunctionMetadataTransformer.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/DurableWorkflowFunctionMetadataTransformer.cs index 8bd7835458..1f8a6aea98 100644 --- a/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/DurableWorkflowFunctionMetadataTransformer.cs +++ b/dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/DurableWorkflowFunctionMetadataTransformer.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using Microsoft.Agents.AI.DurableTask; -using Microsoft.Agents.AI.Workflows.Checkpointing; +using Microsoft.Agents.AI.Workflows; using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; using Microsoft.Extensions.Logging; @@ -66,11 +66,11 @@ internal sealed class DurableWorkflowFunctionMetadataTransformer : IFunctionMeta } } - Dictionary executorInfos = workflow.Value.ReflectExecutors(); + Dictionary executorBindings = workflow.Value.ReflectExecutors(); foreach (string executorId in executorIds) { - if (executorInfos.TryGetValue(executorId, out ExecutorInfo? executorInfo)) + if (executorBindings.TryGetValue(executorId, out ExecutorBinding? executorBinding)) { string executorName = WorkflowNamingHelper.GetExecutorName(executorId); string functionName = WorkflowNamingHelper.ToOrchestrationFunctionName(executorName); @@ -83,14 +83,14 @@ internal sealed class DurableWorkflowFunctionMetadataTransformer : IFunctionMeta } // Check if the executor type is an agent-related type - if (WorkflowHelper.IsAgentExecutorType(executorInfo.ExecutorType)) + if (WorkflowHelper.IsAgentExecutorType(executorBinding.ExecutorType)) { - this._logger.LogAddingAgentEntityFunction(executorId, executorInfo.ExecutorType.TypeName, workflow.Key); + this._logger.LogAddingAgentEntityFunction(executorId, executorBinding.ExecutorType.FullName ?? executorBinding.ExecutorType.Name, workflow.Key); //original.Add(CreateAgentTrigger(functionName)); } else { - this._logger.LogAddingActivityFunction(executorId, executorInfo.ExecutorType.TypeName, workflow.Key); + this._logger.LogAddingActivityFunction(executorId, executorBinding.ExecutorType.FullName ?? executorBinding.ExecutorType.Name, workflow.Key); original.Add(CreateActivityTrigger(functionName)); } } diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/ExecutorInfo.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/ExecutorInfo.cs index 93fa110e99..6e019b4928 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/ExecutorInfo.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/ExecutorInfo.cs @@ -2,36 +2,16 @@ namespace Microsoft.Agents.AI.Workflows.Checkpointing; -/// -/// Represents information about an executor in a workflow, including its type and identifier. -/// -/// The type identifier of the executor. -/// The unique identifier of the executor instance. -public sealed record class ExecutorInfo(TypeId ExecutorType, string ExecutorId) +internal sealed record class ExecutorInfo(TypeId ExecutorType, string ExecutorId) { - /// - /// Determines whether this executor info matches a specific executor type by generic parameter. - /// - /// The executor type to match against. - /// true if the executor type and ID match; otherwise, false. public bool IsMatch() where T : Executor => this.ExecutorType.IsMatch() && this.ExecutorId == typeof(T).Name; - /// - /// Determines whether this executor info matches a given executor instance. - /// - /// The executor instance to match against. - /// true if the executor type and ID match; otherwise, false. public bool IsMatch(Executor executor) => this.ExecutorType.IsMatch(executor.GetType()) && this.ExecutorId == executor.Id; - /// - /// Determines whether this executor info matches a given executor binding. - /// - /// The executor binding to match against. - /// true if the executor type and ID match; otherwise, false. public bool IsMatch(ExecutorBinding binding) => this.ExecutorType.IsMatch(binding.ExecutorType) && this.ExecutorId == binding.Id; diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Workflow.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Workflow.cs index c48961843a..24a3857a0b 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Workflow.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Workflow.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft. All rights reserved. +// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; @@ -37,15 +37,12 @@ public class Workflow } /// - /// Gets information about all executors in the workflow, keyed by their ID. + /// Gets all executor bindings in the workflow, keyed by their ID. /// - /// A dictionary mapping executor IDs to their . - public Dictionary ReflectExecutors() + /// A dictionary mapping executor IDs to their . + public Dictionary ReflectExecutors() { - return this.ExecutorBindings.Values.ToDictionary( - keySelector: binding => binding.Id, - elementSelector: RepresentationExtensions.ToExecutorInfo - ); + return new Dictionary(this.ExecutorBindings); } internal Dictionary Ports { get; init; } = []; @@ -226,45 +223,4 @@ public class Workflow return startExecutor.DescribeProtocol(); } - - /// - /// Enumerates all AIAgent instances that are directly bound in this workflow. - /// - /// - /// This method only returns agents that are directly bound as executor values. - /// It does not include agents created by factory functions, as those require - /// an IServiceProvider to instantiate. - /// - /// An enumerable collection of AIAgent instances found in the workflow. - public IEnumerable EnumerateAgentExecutors() - { - foreach (ExecutorBinding binding in this.ExecutorBindings.Values) - { - if (binding.RawValue is AIAgent agent) - { - yield return agent; - } - } - } - - /// - /// Creates an instance of the specified executor. - /// - /// The identifier of the executor to create. - /// A unique identifier for the run context. - /// The to monitor for cancellation requests. - /// A representing the asynchronous operation. - /// - /// This method is useful for Azure Functions scenarios where you need to create executor instances - /// outside of the normal workflow execution flow. - /// - public async ValueTask CreateExecutorInstanceAsync(string executorId, string runId, CancellationToken cancellationToken = default) - { - if (!this.ExecutorBindings.TryGetValue(executorId, out ExecutorBinding? binding)) - { - throw new InvalidOperationException($"Executor '{executorId}' not found in workflow."); - } - - return await binding.CreateInstanceAsync(runId).ConfigureAwait(false); - } }