Files
agent-framework/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/WorkflowInfo.cs
T
Jacob AlberandGitHub b25b0af49b .NET: [BREAKING] Unify ExecutorIsh and ExecutorRegistration, unify/simplify APIs (#1637)
* refactor: Unify ExecutorIsh and ExecutorRegistration => ExecutorBinding

* Switch to more modern Record type-tree for Sum Types
* Unify APIs for getting ExecutorBinding
* Fix an issue where workflows consisting entirely of cross-run shareable executors which are not instance-resettable do not properly clear state when running non-concurrently.

* feat: Simplify function-to-executor pattern

* refactor: Normalize API naming
2025-11-03 18:20:35 +00:00

93 lines
3.1 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Workflows.Checkpointing;
internal sealed class WorkflowInfo
{
[JsonConstructor]
internal WorkflowInfo(
Dictionary<string, ExecutorInfo> executors,
Dictionary<string, List<EdgeInfo>> edges,
HashSet<RequestPortInfo> requestPorts,
string startExecutorId,
HashSet<string>? outputExecutorIds)
{
this.Executors = Throw.IfNull(executors);
this.Edges = Throw.IfNull(edges);
this.RequestPorts = Throw.IfNull(requestPorts);
this.StartExecutorId = Throw.IfNullOrEmpty(startExecutorId);
this.OutputExecutorIds = outputExecutorIds ?? [];
}
public Dictionary<string, ExecutorInfo> Executors { get; }
public Dictionary<string, List<EdgeInfo>> Edges { get; }
public HashSet<RequestPortInfo> RequestPorts { get; }
public TypeId? InputType { get; }
public string StartExecutorId { get; }
public HashSet<string> OutputExecutorIds { get; }
public bool IsMatch(Workflow workflow)
{
if (workflow is null)
{
return false;
}
if (this.StartExecutorId != workflow.StartExecutorId)
{
return false;
}
// Validate the executors
if (workflow.ExecutorBindings.Count != this.Executors.Count ||
this.Executors.Keys.Any(
executorId => workflow.ExecutorBindings.TryGetValue(executorId, out ExecutorBinding? binding)
&& !this.Executors[executorId].IsMatch(binding)))
{
return false;
}
// Validate the edges
if (workflow.Edges.Count != this.Edges.Count ||
this.Edges.Keys.Any(
sourceId =>
// If the sourceId is not present in the workflow edges, or
!workflow.Edges.TryGetValue(sourceId, out var edgeList) ||
// If the edge list count does not match, or
edgeList.Count != this.Edges[sourceId].Count ||
// If any edge in the workflow edge list does not match the corresponding edge in this.Edges[sourceId]
!edgeList.All(edge => this.Edges[sourceId].Any(e => e.IsMatch(edge)))
))
{
return false;
}
// Validate the input ports
if (workflow.Ports.Count != this.RequestPorts.Count ||
this.RequestPorts.Any(portInfo =>
!workflow.Ports.TryGetValue(portInfo.PortId, out RequestPort? port) ||
!portInfo.RequestType.IsMatch(port.Request) ||
!portInfo.ResponseType.IsMatch(port.Response)))
{
return false;
}
// Validate the outputs
if (workflow.OutputExecutors.Count != this.OutputExecutorIds.Count ||
this.OutputExecutorIds.Any(id => !workflow.OutputExecutors.Contains(id)))
{
return false;
}
return true;
}
}