// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; using System.Diagnostics; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI.Workflows; /// /// Provides a builder for constructing a switch-like control flow that maps predicates to one or more executors. /// Enables the configuration of case-based and default execution logic for dynamic input handling. /// public sealed class SwitchBuilder { private readonly List _executors = []; private readonly Dictionary _executorIndicies = []; private readonly List<(Func Predicate, HashSet OutgoingIndicies)> _caseMap = []; private readonly HashSet _defaultIndicies = []; /// /// Adds a case to the switch builder that associates a predicate with one or more executors. /// /// /// Cases are evaluated in the order they are added. /// /// A function that determines whether the associated executors should be considered for execution. The function /// receives an input object and returns to select the case; otherwise, . /// One or more executors to associate with the predicate. Each executor will be invoked if the predicate matches. /// Cannot be null. /// The current instance, allowing for method chaining. public SwitchBuilder AddCase(Func predicate, params IEnumerable executors) { Throw.IfNull(predicate); Throw.IfNull(executors); HashSet indicies = []; foreach (ExecutorBinding executor in executors) { if (!this._executorIndicies.TryGetValue(executor.Id, out int index)) { index = this._executors.Count; this._executors.Add(executor); this._executorIndicies[executor.Id] = index; } indicies.Add(index); } Func casePredicate = WorkflowBuilder.CreateConditionFunc(predicate)!; this._caseMap.Add((casePredicate, indicies)); return this; } /// /// Adds one or more executors to be used as the default case when no other predicates match. /// /// /// public SwitchBuilder WithDefault(params IEnumerable executors) { Throw.IfNull(executors); foreach (ExecutorBinding executor in executors) { if (!this._executorIndicies.TryGetValue(executor.Id, out int index)) { index = this._executors.Count; this._executors.Add(executor); this._executorIndicies[executor.Id] = index; } this._defaultIndicies.Add(index); } return this; } internal WorkflowBuilder ReduceToFanOut(WorkflowBuilder builder, ExecutorBinding source) { List<(Func Predicate, HashSet OutgoingIndicies)> caseMap = this._caseMap; HashSet defaultIndicies = this._defaultIndicies; return builder.AddFanOutEdge(source, this._executors, EdgeSelector); IEnumerable EdgeSelector(object? input, int targetCount) { Debug.Assert(targetCount == this._executors.Count); for (int i = 0; i < caseMap.Count; i++) { (Func predicate, HashSet outgoingIndicies) = caseMap[i]; if (predicate(input)) { return outgoingIndicies; } } return defaultIndicies; } } }