// 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;
}
}