// Copyright (c) Microsoft. All rights reserved. namespace Microsoft.Agents.AI.DurableTask; /// /// Provides helper methods for workflow naming conventions used in durable orchestrations. /// internal static class WorkflowNamingHelper { /// /// The prefix used for durable workflow orchestration function names. /// public const string OrchestrationFunctionPrefix = "dafx-"; /// /// Converts a workflow name to its corresponding orchestration function name. /// /// The workflow name. /// The orchestration function name. /// Thrown when the workflow name is null or empty. public static string ToOrchestrationFunctionName(string workflowName) { ArgumentException.ThrowIfNullOrEmpty(workflowName); return $"{OrchestrationFunctionPrefix}{workflowName}"; } /// /// Converts an orchestration function name back to its workflow name. /// /// The orchestration function name. /// The workflow name. /// Thrown when the orchestration function name is null, empty, or doesn't have the expected prefix. public static string ToWorkflowName(string orchestrationFunctionName) { ArgumentException.ThrowIfNullOrEmpty(orchestrationFunctionName); if (!orchestrationFunctionName.StartsWith(OrchestrationFunctionPrefix, StringComparison.Ordinal)) { throw new ArgumentException( $"Orchestration function name '{orchestrationFunctionName}' does not start with the expected '{OrchestrationFunctionPrefix}' prefix.", nameof(orchestrationFunctionName)); } string workflowName = orchestrationFunctionName[OrchestrationFunctionPrefix.Length..]; if (string.IsNullOrEmpty(workflowName)) { throw new ArgumentException( $"Orchestration function name '{orchestrationFunctionName}' does not contain a workflow name after the prefix.", nameof(orchestrationFunctionName)); } return workflowName; } /// /// Tries to convert an orchestration function name back to its workflow name. /// /// The orchestration function name. /// When this method returns, contains the workflow name if the conversion succeeded, or null if it failed. /// true if the conversion succeeded; otherwise, false. public static bool TryGetWorkflowName(string? orchestrationFunctionName, out string? workflowName) { workflowName = null; if (string.IsNullOrEmpty(orchestrationFunctionName)) { return false; } if (!orchestrationFunctionName.StartsWith(OrchestrationFunctionPrefix, StringComparison.Ordinal)) { return false; } workflowName = orchestrationFunctionName[OrchestrationFunctionPrefix.Length..]; return !string.IsNullOrEmpty(workflowName); } /// /// The suffix separator used when the workflow builder appends a GUID to executor IDs. /// /// /// For agentic executors, the workflow builder appends a GUID suffix to ensure uniqueness. /// For example: "Physicist_8884e71021334ce49517fa2b17b1695b". /// private const char ExecutorIdSuffixSeparator = '_'; /// /// Extracts the executor name from an executor ID. /// /// /// /// For non-agentic executors, the executor ID is the same as the executor name (e.g., "OrderParser"). /// /// /// For agentic executors, the workflow builder appends a GUID suffix separated by an underscore /// (e.g., "Physicist_8884e71021334ce49517fa2b17b1695b"). This method extracts just the name portion. /// /// /// The executor ID, which may contain a GUID suffix. /// The executor name without any GUID suffix. /// Thrown when the executor ID is null or empty. public static string GetExecutorName(string executorId) { ArgumentException.ThrowIfNullOrEmpty(executorId); int separatorIndex = executorId.IndexOf(ExecutorIdSuffixSeparator); return separatorIndex > 0 ? executorId[..separatorIndex] : executorId; } /// /// Determines whether the executor ID contains a GUID suffix. /// /// The executor ID to check. /// true if the executor ID contains a suffix; otherwise, false. public static bool HasExecutorIdSuffix(string? executorId) { if (string.IsNullOrEmpty(executorId)) { return false; } int separatorIndex = executorId.IndexOf(ExecutorIdSuffixSeparator); return separatorIndex > 0 && separatorIndex < executorId.Length - 1; } }