mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: [BREAKING] refactor: Normalize WorkflowBuilder APIs (#1935)
* [BREAKING] refactor: Normalize WorkflowBuilder APIs * "partitioner" => "assigner" * normalize ordering so sources always to the left of targets for edges * normalize parameter ordering so sources and targets are always first arguments * remove `params` (users should use collection expressions instead) * refactor: Align name with Python
This commit is contained in:
committed by
GitHub
Unverified
parent
33f84f9ed2
commit
94a5ba3448
@@ -23,8 +23,8 @@ internal static class WorkflowFactory
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
return new WorkflowBuilder(startExecutor)
|
||||
.AddFanOutEdge(startExecutor, targets: [frenchAgent, englishAgent])
|
||||
.AddFanInEdge(aggregationExecutor, sources: [frenchAgent, englishAgent])
|
||||
.AddFanOutEdge(startExecutor, [frenchAgent, englishAgent])
|
||||
.AddFanInEdge([frenchAgent, englishAgent], aggregationExecutor)
|
||||
.WithOutputFrom(aggregationExecutor)
|
||||
.Build();
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ public static class Program
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
var workflow = new WorkflowBuilder(startExecutor)
|
||||
.AddFanOutEdge(startExecutor, targets: [physicist, chemist])
|
||||
.AddFanInEdge(aggregationExecutor, sources: [physicist, chemist])
|
||||
.AddFanOutEdge(startExecutor, [physicist, chemist])
|
||||
.AddFanInEdge([physicist, chemist], aggregationExecutor)
|
||||
.WithOutputFrom(aggregationExecutor)
|
||||
.Build();
|
||||
|
||||
|
||||
@@ -62,10 +62,10 @@ public static class Program
|
||||
|
||||
// Step 4: Build the concurrent workflow with fan-out/fan-in pattern
|
||||
return new WorkflowBuilder(splitter)
|
||||
.AddFanOutEdge(splitter, targets: [.. mappers]) // Split -> many mappers
|
||||
.AddFanInEdge(shuffler, sources: [.. mappers]) // All mappers -> shuffle
|
||||
.AddFanOutEdge(shuffler, targets: [.. reducers]) // Shuffle -> many reducers
|
||||
.AddFanInEdge(completion, sources: [.. reducers]) // All reducers -> completion
|
||||
.AddFanOutEdge(splitter, [.. mappers]) // Split -> many mappers
|
||||
.AddFanInEdge([.. mappers], shuffler) // All mappers -> shuffle
|
||||
.AddFanOutEdge(shuffler, [.. reducers]) // Shuffle -> many reducers
|
||||
.AddFanInEdge([.. reducers], completion) // All reducers -> completion
|
||||
.WithOutputFrom(completion)
|
||||
.Build();
|
||||
}
|
||||
|
||||
+3
-3
@@ -60,13 +60,13 @@ public static class Program
|
||||
WorkflowBuilder builder = new(emailAnalysisExecutor);
|
||||
builder.AddFanOutEdge(
|
||||
emailAnalysisExecutor,
|
||||
targets: [
|
||||
[
|
||||
handleSpamExecutor,
|
||||
emailAssistantExecutor,
|
||||
emailSummaryExecutor,
|
||||
handleUncertainExecutor,
|
||||
],
|
||||
partitioner: GetPartitioner()
|
||||
GetTargetAssigner()
|
||||
)
|
||||
// After the email assistant writes a response, it will be sent to the send email executor
|
||||
.AddEdge(emailAssistantExecutor, sendEmailExecutor)
|
||||
@@ -105,7 +105,7 @@ public static class Program
|
||||
/// Creates a partitioner for routing messages based on the analysis result.
|
||||
/// </summary>
|
||||
/// <returns>A function that takes an analysis result and returns the target partitions.</returns>
|
||||
private static Func<AnalysisResult?, int, IEnumerable<int>> GetPartitioner()
|
||||
private static Func<AnalysisResult?, int, IEnumerable<int>> GetTargetAssigner()
|
||||
{
|
||||
return (analysisResult, targetCount) =>
|
||||
{
|
||||
|
||||
+2
-2
@@ -24,8 +24,8 @@ internal static class WorkflowHelper
|
||||
|
||||
// Build the workflow by adding executors and connecting them
|
||||
return new WorkflowBuilder(startExecutor)
|
||||
.AddFanOutEdge(startExecutor, targets: [frenchAgent, englishAgent])
|
||||
.AddFanInEdge(aggregationExecutor, sources: [frenchAgent, englishAgent])
|
||||
.AddFanOutEdge(startExecutor, [frenchAgent, englishAgent])
|
||||
.AddFanInEdge([frenchAgent, englishAgent], aggregationExecutor)
|
||||
.WithOutputFrom(aggregationExecutor)
|
||||
.Build();
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ public static class Program
|
||||
|
||||
// Build the workflow by connecting executors sequentially
|
||||
var workflow = new WorkflowBuilder(fileRead)
|
||||
.AddFanOutEdge(fileRead, targets: [wordCount, paragraphCount])
|
||||
.AddFanInEdge(aggregate, sources: [wordCount, paragraphCount])
|
||||
.AddFanOutEdge(fileRead, [wordCount, paragraphCount])
|
||||
.AddFanInEdge([wordCount, paragraphCount], aggregate)
|
||||
.WithOutputFrom(aggregate)
|
||||
.Build();
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ public static partial class AgentWorkflowBuilder
|
||||
// provenance tracking exposed in the workflow context passed to a handler.
|
||||
ExecutorBinding[] agentExecutors = (from agent in agents select (ExecutorBinding)new AgentRunStreamingExecutor(agent, includeInputInOutput: false)).ToArray();
|
||||
ExecutorBinding[] accumulators = [.. from agent in agentExecutors select (ExecutorBinding)new CollectChatMessagesExecutor($"Batcher/{agent.Id}")];
|
||||
builder.AddFanOutEdge(start, targets: agentExecutors);
|
||||
builder.AddFanOutEdge(start, agentExecutors);
|
||||
for (int i = 0; i < agentExecutors.Length; i++)
|
||||
{
|
||||
builder.AddEdge(agentExecutors[i], accumulators[i]);
|
||||
@@ -143,7 +143,7 @@ public static partial class AgentWorkflowBuilder
|
||||
|
||||
ExecutorBinding end = endFactory.BindExecutor(ConcurrentEndExecutor.ExecutorId);
|
||||
|
||||
builder.AddFanInEdge(end, sources: accumulators);
|
||||
builder.AddFanInEdge(accumulators, end);
|
||||
|
||||
builder = builder.WithOutputFrom(end);
|
||||
if (workflowName is not null)
|
||||
|
||||
@@ -84,7 +84,7 @@ public sealed class SwitchBuilder
|
||||
List<(Func<object?, bool> Predicate, HashSet<int> OutgoingIndicies)> caseMap = this._caseMap;
|
||||
HashSet<int> defaultIndicies = this._defaultIndicies;
|
||||
|
||||
return builder.AddFanOutEdge<object>(source, CasePartitioner, [.. this._executors]);
|
||||
return builder.AddFanOutEdge<object>(source, this._executors, CasePartitioner);
|
||||
|
||||
IEnumerable<int> CasePartitioner(object? input, int targetCount)
|
||||
{
|
||||
|
||||
@@ -272,12 +272,12 @@ public class WorkflowBuilder
|
||||
/// <param name="source">The source executor from which the fan-out edge originates. Cannot be null.</param>
|
||||
/// <param name="targets">One or more target executors that will receive the fan-out edge. Cannot be null or empty.</param>
|
||||
/// <returns>The current instance of <see cref="WorkflowBuilder"/>.</returns>
|
||||
public WorkflowBuilder AddFanOutEdge(ExecutorBinding source, params IEnumerable<ExecutorBinding> targets)
|
||||
=> this.AddFanOutEdge<object>(source, null, targets);
|
||||
public WorkflowBuilder AddFanOutEdge(ExecutorBinding source, IEnumerable<ExecutorBinding> targets)
|
||||
=> this.AddFanOutEdge<object>(source, targets, null);
|
||||
|
||||
internal static Func<object?, int, IEnumerable<int>>? CreateEdgeAssignerFunc<T>(Func<T?, int, IEnumerable<int>>? partitioner)
|
||||
internal static Func<object?, int, IEnumerable<int>>? CreateTargetAssignerFunc<T>(Func<T?, int, IEnumerable<int>>? targetAssigner)
|
||||
{
|
||||
if (partitioner is null)
|
||||
if (targetAssigner is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
@@ -289,7 +289,7 @@ public class WorkflowBuilder
|
||||
maybeObj = portableValue.AsType(typeof(T));
|
||||
}
|
||||
|
||||
return partitioner(maybeObj is T typed ? typed : default, count);
|
||||
return targetAssigner(maybeObj is T typed ? typed : default, count);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -300,11 +300,11 @@ public class WorkflowBuilder
|
||||
/// <remarks>If a partitioner function is provided, it will be used to distribute input across the target
|
||||
/// executors. The order of targets determines their mapping in the partitioning process.</remarks>
|
||||
/// <param name="source">The source executor from which the fan-out edge originates. Cannot be null.</param>
|
||||
/// <param name="partitioner">An optional function that determines how input is partitioned among the target executors.
|
||||
/// If null, messages will route to all targets.</param>
|
||||
/// <param name="targets">One or more target executors that will receive the fan-out edge. Cannot be null or empty.</param>
|
||||
/// <returns>The current instance of <see cref="WorkflowBuilder"/>.</returns>
|
||||
public WorkflowBuilder AddFanOutEdge<T>(ExecutorBinding source, Func<T?, int, IEnumerable<int>>? partitioner = null, params IEnumerable<ExecutorBinding> targets)
|
||||
/// <param name="targetSelector">An optional function that determines how input is assigned among the target executors.
|
||||
/// If null, messages will route to all targets.</param>
|
||||
public WorkflowBuilder AddFanOutEdge<T>(ExecutorBinding source, IEnumerable<ExecutorBinding> targets, Func<T?, int, IEnumerable<int>>? targetSelector = null)
|
||||
{
|
||||
Throw.IfNull(source);
|
||||
Throw.IfNull(targets);
|
||||
@@ -321,7 +321,7 @@ public class WorkflowBuilder
|
||||
this.Track(source).Id,
|
||||
sinkIds,
|
||||
this.TakeEdgeId(),
|
||||
CreateEdgeAssignerFunc(partitioner));
|
||||
CreateTargetAssignerFunc(targetSelector));
|
||||
|
||||
this.EnsureEdgesFor(source.Id).Add(new(fanOutEdge));
|
||||
|
||||
@@ -335,10 +335,10 @@ public class WorkflowBuilder
|
||||
/// <remarks>This method establishes a fan-in relationship, allowing the target executor to be activated
|
||||
/// based on the completion or state of multiple sources. The trigger parameter can be used to customize activation
|
||||
/// behavior.</remarks>
|
||||
/// <param name="target">The target executor that receives input from the specified source executors. Cannot be null.</param>
|
||||
/// <param name="sources">One or more source executors that provide input to the target. Cannot be null or empty.</param>
|
||||
/// <param name="target">The target executor that receives input from the specified source executors. Cannot be null.</param>
|
||||
/// <returns>The current instance of <see cref="WorkflowBuilder"/>.</returns>
|
||||
public WorkflowBuilder AddFanInEdge(ExecutorBinding target, params IEnumerable<ExecutorBinding> sources)
|
||||
public WorkflowBuilder AddFanInEdge(IEnumerable<ExecutorBinding> sources, ExecutorBinding target)
|
||||
{
|
||||
Throw.IfNull(target);
|
||||
Throw.IfNull(sources);
|
||||
@@ -364,8 +364,14 @@ public class WorkflowBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="AddFanInEdge(IEnumerable{ExecutorBinding}, ExecutorBinding)"/>
|
||||
[Obsolete("Use AddFanInEdge(IEnumerable<ExecutorBinding>, ExecutorBinding) instead.")]
|
||||
public WorkflowBuilder AddFanInEdge(ExecutorBinding target, params IEnumerable<ExecutorBinding> sources)
|
||||
=> this.AddFanInEdge(sources, target);
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
// Validate that there are no unbound executors
|
||||
if (this._unboundExecutors.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
|
||||
@@ -22,10 +22,10 @@ public static class WorkflowBuilderExtensions
|
||||
/// <typeparam name="TMessage">The type of message to forward.</typeparam>
|
||||
/// <param name="builder">The <see cref="WorkflowBuilder"/> to which the edges will be added.</param>
|
||||
/// <param name="source">The source executor from which messages will be forwarded.</param>
|
||||
/// <param name="executors">The target executors to which messages will be forwarded.</param>
|
||||
/// <param name="target">The target executor to which messages will be forwarded.</param>
|
||||
/// <returns>The updated <see cref="WorkflowBuilder"/> instance.</returns>
|
||||
public static WorkflowBuilder ForwardMessage<TMessage>(this WorkflowBuilder builder, ExecutorBinding source, params IEnumerable<ExecutorBinding> executors)
|
||||
=> builder.ForwardMessage<TMessage>(source, condition: null, executors);
|
||||
public static WorkflowBuilder ForwardMessage<TMessage>(this WorkflowBuilder builder, ExecutorBinding source, ExecutorBinding target)
|
||||
=> builder.ForwardMessage<TMessage>(source, [target], condition: null);
|
||||
|
||||
/// <summary>
|
||||
/// Adds edges to the workflow that forward messages of the specified type from the source executor to
|
||||
@@ -34,26 +34,38 @@ public static class WorkflowBuilderExtensions
|
||||
/// <typeparam name="TMessage">The type of message to forward.</typeparam>
|
||||
/// <param name="builder">The <see cref="WorkflowBuilder"/> to which the edges will be added.</param>
|
||||
/// <param name="source">The source executor from which messages will be forwarded.</param>
|
||||
/// <param name="targets">The target executors to which messages will be forwarded.</param>
|
||||
/// <returns>The updated <see cref="WorkflowBuilder"/> instance.</returns>
|
||||
public static WorkflowBuilder ForwardMessage<TMessage>(this WorkflowBuilder builder, ExecutorBinding source, IEnumerable<ExecutorBinding> targets)
|
||||
=> builder.ForwardMessage<TMessage>(source, targets, condition: null);
|
||||
|
||||
/// <summary>
|
||||
/// Adds edges to the workflow that forward messages of the specified type from the source executor to
|
||||
/// one or more target executors.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">The type of message to forward.</typeparam>
|
||||
/// <param name="builder">The <see cref="WorkflowBuilder"/> to which the edges will be added.</param>
|
||||
/// <param name="source">The source executor from which messages will be forwarded.</param>
|
||||
/// <param name="targets">The target executors to which messages will be forwarded.</param>
|
||||
/// <param name="condition">An optional condition that messages must satisfy to be forwarded. If <see langword="null"/>,
|
||||
/// all messages of type <typeparamref name="TMessage"/> will be forwarded.</param>
|
||||
/// <param name="executors">The target executors to which messages will be forwarded.</param>
|
||||
/// <returns>The updated <see cref="WorkflowBuilder"/> instance.</returns>
|
||||
public static WorkflowBuilder ForwardMessage<TMessage>(this WorkflowBuilder builder, ExecutorBinding source, Func<TMessage, bool>? condition = null, params IEnumerable<ExecutorBinding> executors)
|
||||
public static WorkflowBuilder ForwardMessage<TMessage>(this WorkflowBuilder builder, ExecutorBinding source, IEnumerable<ExecutorBinding> targets, Func<TMessage, bool>? condition = null)
|
||||
{
|
||||
Throw.IfNull(executors);
|
||||
Throw.IfNull(targets);
|
||||
|
||||
Func<object?, bool> predicate = WorkflowBuilder.CreateConditionFunc<TMessage>(IsAllowedTypeAndMatchingCondition)!;
|
||||
|
||||
#if NET
|
||||
if (executors.TryGetNonEnumeratedCount(out int count) && count == 1)
|
||||
if (targets.TryGetNonEnumeratedCount(out int count) && count == 1)
|
||||
#else
|
||||
if (executors is ICollection<ExecutorBinding> { Count: 1 })
|
||||
if (targets is ICollection<ExecutorBinding> { Count: 1 })
|
||||
#endif
|
||||
{
|
||||
return builder.AddEdge(source, executors.First(), predicate);
|
||||
return builder.AddEdge(source, targets.First(), predicate);
|
||||
}
|
||||
|
||||
return builder.AddSwitch(source, (switch_) => switch_.AddCase(predicate, executors));
|
||||
return builder.AddSwitch(source, (switch_) => switch_.AddCase(predicate, targets));
|
||||
|
||||
// The reason we can check for "not null" here is that CreateConditionFunc<T> will do the correct unwrapping
|
||||
// logic for PortableValues.
|
||||
@@ -66,24 +78,35 @@ public static class WorkflowBuilderExtensions
|
||||
/// <typeparam name="TMessage">The type of messages to exclude from being forwarded to the executors.</typeparam>
|
||||
/// <param name="builder">The <see cref="WorkflowBuilder"/> instance to which the edges will be added.</param>
|
||||
/// <param name="source">The source executor from which messages will be forwarded.</param>
|
||||
/// <param name="executors">The target executors to which messages, except those of type <typeparamref name="TMessage"/>, will be forwarded.</param>
|
||||
/// <param name="target">The target executor to which messages, except those of type <typeparamref name="TMessage"/>, will be forwarded.</param>
|
||||
/// <returns>The updated <see cref="WorkflowBuilder"/> instance with the added edges.</returns>
|
||||
public static WorkflowBuilder ForwardExcept<TMessage>(this WorkflowBuilder builder, ExecutorBinding source, params IEnumerable<ExecutorBinding> executors)
|
||||
public static WorkflowBuilder ForwardExcept<TMessage>(this WorkflowBuilder builder, ExecutorBinding source, ExecutorBinding target)
|
||||
=> builder.ForwardExcept<TMessage>(source, [target]);
|
||||
|
||||
/// <summary>
|
||||
/// Adds edges from the specified source to the provided executors, excluding messages of a specified type.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMessage">The type of messages to exclude from being forwarded to the executors.</typeparam>
|
||||
/// <param name="builder">The <see cref="WorkflowBuilder"/> instance to which the edges will be added.</param>
|
||||
/// <param name="source">The source executor from which messages will be forwarded.</param>
|
||||
/// <param name="targets">The target executors to which messages, except those of type <typeparamref name="TMessage"/>, will be forwarded.</param>
|
||||
/// <returns>The updated <see cref="WorkflowBuilder"/> instance with the added edges.</returns>
|
||||
public static WorkflowBuilder ForwardExcept<TMessage>(this WorkflowBuilder builder, ExecutorBinding source, IEnumerable<ExecutorBinding> targets)
|
||||
{
|
||||
Throw.IfNull(executors);
|
||||
Throw.IfNull(targets);
|
||||
|
||||
Func<object?, bool> predicate = WorkflowBuilder.CreateConditionFunc<TMessage>((Func<object?, bool>)IsAllowedType)!;
|
||||
|
||||
#if NET
|
||||
if (executors.TryGetNonEnumeratedCount(out int count) && count == 1)
|
||||
if (targets.TryGetNonEnumeratedCount(out int count) && count == 1)
|
||||
#else
|
||||
if (executors is ICollection<ExecutorBinding> { Count: 1 })
|
||||
if (targets is ICollection<ExecutorBinding> { Count: 1 })
|
||||
#endif
|
||||
{
|
||||
return builder.AddEdge(source, executors.First(), predicate);
|
||||
return builder.AddEdge(source, targets.First(), predicate);
|
||||
}
|
||||
|
||||
return builder.AddSwitch(source, (switch_) => switch_.AddCase(predicate, executors));
|
||||
return builder.AddSwitch(source, (switch_) => switch_.AddCase(predicate, targets));
|
||||
|
||||
// The reason we can check for "null" here is that CreateConditionFunc<T> will do the correct unwrapping
|
||||
// logic for PortableValues.
|
||||
@@ -98,11 +121,11 @@ public static class WorkflowBuilderExtensions
|
||||
/// executor in the order provided.</remarks>
|
||||
/// <param name="builder">The workflow builder to which the executor chain will be added. </param>
|
||||
/// <param name="source">The initial executor in the chain. Cannot be null.</param>
|
||||
/// <param name="allowRepetition">If set to <see langword="true"/>, the same executor can be added to the chain multiple times.</param>
|
||||
/// <param name="executors">An ordered array of executors to be added to the chain after the source.</param>
|
||||
/// <param name="executors">An ordered sequence of executors to be added to the chain after the source.</param>
|
||||
/// <returns>The original workflow builder instance with the specified executor chain added.</returns>
|
||||
/// <param name="allowRepetition">If set to <see langword="true"/>, the same executor can be added to the chain multiple times.</param>
|
||||
/// <exception cref="ArgumentException">Thrown if there is a cycle in the chain.</exception>
|
||||
public static WorkflowBuilder AddChain(this WorkflowBuilder builder, ExecutorBinding source, bool allowRepetition = false, params IEnumerable<ExecutorBinding> executors)
|
||||
public static WorkflowBuilder AddChain(this WorkflowBuilder builder, ExecutorBinding source, IList<ExecutorBinding> executors, bool allowRepetition = false)
|
||||
{
|
||||
Throw.IfNull(builder);
|
||||
Throw.IfNull(source);
|
||||
|
||||
+9
-9
@@ -68,10 +68,10 @@ internal static class Step9EntryPoint
|
||||
|
||||
var requestPort = RequestPort.Create<TRequest, TResponse>(id);
|
||||
|
||||
return builder.ForwardMessage<ExternalRequest>(source, executors: [filter], condition: message => message.DataIs<TRequest>())
|
||||
.ForwardMessage<ExternalRequest>(filter, executors: [requestPort], condition: message => message.DataIs<TRequest>())
|
||||
.ForwardMessage<ExternalResponse>(requestPort, executors: [filter], condition: message => message.DataIs<TResponse>())
|
||||
.ForwardMessage<ExternalResponse>(filter, executors: [source], condition: message => message.DataIs<TResponse>());
|
||||
return builder.ForwardMessage<ExternalRequest>(source, targets: [filter], condition: message => message.DataIs<TRequest>())
|
||||
.ForwardMessage<ExternalRequest>(filter, targets: [requestPort], condition: message => message.DataIs<TRequest>())
|
||||
.ForwardMessage<ExternalResponse>(requestPort, targets: [filter], condition: message => message.DataIs<TResponse>())
|
||||
.ForwardMessage<ExternalResponse>(filter, targets: [source], condition: message => message.DataIs<TResponse>());
|
||||
}
|
||||
|
||||
public static WorkflowBuilder AddExternalRequest<TRequest, TResponse>(this WorkflowBuilder builder, ExecutorBinding source, string? id = null)
|
||||
@@ -88,10 +88,10 @@ internal static class Step9EntryPoint
|
||||
|
||||
public static WorkflowBuilder AddExternalRequest<TRequest, TResponse>(this WorkflowBuilder builder, ExecutorBinding source, RequestPort<TRequest, TResponse> inputPort)
|
||||
{
|
||||
return builder.ForwardMessage<TRequest>(source, inputPort)
|
||||
.ForwardMessage<ExternalRequest>(source, inputPort)
|
||||
.ForwardMessage<TResponse>(inputPort, source)
|
||||
.ForwardMessage<ExternalResponse>(inputPort, source);
|
||||
return builder.ForwardMessage<TRequest>(source, [inputPort])
|
||||
.ForwardMessage<ExternalRequest>(source, [inputPort])
|
||||
.ForwardMessage<TResponse>(inputPort, [source])
|
||||
.ForwardMessage<ExternalResponse>(inputPort, [source]);
|
||||
}
|
||||
|
||||
public static Workflow CreateSubWorkflow()
|
||||
@@ -113,7 +113,7 @@ internal static class Step9EntryPoint
|
||||
ExecutorBinding subworkflow = CreateSubWorkflow().BindAsExecutor("ResourceWorkflow");
|
||||
|
||||
return new WorkflowBuilder(coordinator)
|
||||
.AddChain(coordinator, allowRepetition: true, subworkflow, coordinator)
|
||||
.AddChain(coordinator, [subworkflow, coordinator], allowRepetition: true)
|
||||
.AddPassthroughRequestHandler<ResourceRequest, ResourceResponse>(subworkflow, cache)
|
||||
.AddPassthroughRequestHandler<PolicyCheckRequest, PolicyResponse>(subworkflow, policyEngine)
|
||||
.WithOutputFrom(coordinator)
|
||||
|
||||
@@ -111,8 +111,8 @@ public class WorkflowVisualizerTests
|
||||
|
||||
// Build a connected workflow: start fans out to s1 and s2, which then fan-in to t
|
||||
var workflow = new WorkflowBuilder("start")
|
||||
.AddFanOutEdge(start, s1, s2)
|
||||
.AddFanInEdge(t, s1, s2) // AddFanInEdge(target, sources)
|
||||
.AddFanOutEdge(start, [s1, s2])
|
||||
.AddFanInEdge([s1, s2], t) // AddFanInEdge(target, sources)
|
||||
.Build();
|
||||
|
||||
var dotContent = workflow.ToDotString();
|
||||
@@ -174,7 +174,7 @@ public class WorkflowVisualizerTests
|
||||
var target3 = new MockExecutor("target3");
|
||||
|
||||
var workflow = new WorkflowBuilder("start")
|
||||
.AddFanOutEdge(start, target1, target2, target3)
|
||||
.AddFanOutEdge(start, [target1, target2, target3])
|
||||
.Build();
|
||||
|
||||
var dotContent = workflow.ToDotString();
|
||||
@@ -199,8 +199,8 @@ public class WorkflowVisualizerTests
|
||||
|
||||
var workflow = new WorkflowBuilder("start")
|
||||
.AddEdge<string>(start, a, Condition) // Conditional edge
|
||||
.AddFanOutEdge(a, b, c) // Fan-out
|
||||
.AddFanInEdge(end, b, c) // Fan-in - AddFanInEdge(target, sources)
|
||||
.AddFanOutEdge(a, [b, c]) // Fan-out
|
||||
.AddFanInEdge([b, c], end) // Fan-in - AddFanInEdge(target, sources)
|
||||
.Build();
|
||||
|
||||
var dotContent = workflow.ToDotString();
|
||||
@@ -307,8 +307,8 @@ public class WorkflowVisualizerTests
|
||||
var t = new ListStrTargetExecutor("t");
|
||||
|
||||
var workflow = new WorkflowBuilder("start")
|
||||
.AddFanOutEdge(start, s1, s2)
|
||||
.AddFanInEdge(t, s1, s2)
|
||||
.AddFanOutEdge(start, [s1, s2])
|
||||
.AddFanInEdge([s1, s2], t)
|
||||
.Build();
|
||||
|
||||
var mermaidContent = workflow.ToMermaidString();
|
||||
@@ -378,8 +378,8 @@ public class WorkflowVisualizerTests
|
||||
|
||||
var workflow = new WorkflowBuilder("start")
|
||||
.AddEdge<string>(start, a, Condition) // Conditional edge
|
||||
.AddFanOutEdge(a, b, c) // Fan-out
|
||||
.AddFanInEdge(end, b, c) // Fan-in
|
||||
.AddFanOutEdge(a, [b, c]) // Fan-out
|
||||
.AddFanInEdge([b, c], end) // Fan-in
|
||||
.Build();
|
||||
|
||||
var mermaidContent = workflow.ToMermaidString();
|
||||
|
||||
Reference in New Issue
Block a user