.NET: [BREAKING] Enable sharing of workflow instances across concurrently executing runs (#1464)

* refactor: remove unused internals

* feat: Execution Mode for sharing a workflow among concurrent runs

* feat: Update WorkflowHostAgent to support concurrent execution

* Also update AsAgent APIs to support injecting a CheckpointManager and an IWorkflowExecutionEnvironment

* fix: Make Read logic consistent in DeclarativeWorkflowContext
This commit is contained in:
Jacob Alber
2025-10-15 21:34:17 +00:00
committed by GitHub
parent 69e1ab0409
commit 331c750515
67 changed files with 2152 additions and 1347 deletions
@@ -21,21 +21,46 @@ namespace Microsoft.Agents.AI.Workflows.InProc;
/// scenarios where workflow execution does not require executor distribution. </para></remarks>
internal sealed class InProcessRunner : ISuperStepRunner, ICheckpointingHandle
{
public InProcessRunner(Workflow workflow, ICheckpointManager? checkpointManager, string? runId = null, object? workflowOwnership = null, bool subworkflow = false, IEnumerable<Type>? knownValidInputTypes = null)
public static InProcessRunner CreateTopLevelRunner(Workflow workflow, ICheckpointManager? checkpointManager, string? runId = null, bool enableConcurrentRuns = false, IEnumerable<Type>? knownValidInputTypes = null)
{
return new InProcessRunner(workflow,
checkpointManager,
runId,
enableConcurrentRuns: enableConcurrentRuns,
knownValidInputTypes: knownValidInputTypes);
}
public static InProcessRunner CreateSubworkflowRunner(Workflow workflow, ICheckpointManager? checkpointManager, string? runId = null, object? existingOwnerSignoff = null, bool enableConcurrentRuns = false, IEnumerable<Type>? knownValidInputTypes = null)
{
return new InProcessRunner(workflow,
checkpointManager,
runId,
existingOwnerSignoff: existingOwnerSignoff,
enableConcurrentRuns: enableConcurrentRuns,
knownValidInputTypes: knownValidInputTypes,
subworkflow: true);
}
private InProcessRunner(Workflow workflow, ICheckpointManager? checkpointManager, string? runId = null, object? existingOwnerSignoff = null, bool subworkflow = false, bool enableConcurrentRuns = false, IEnumerable<Type>? knownValidInputTypes = null)
{
if (enableConcurrentRuns && !workflow.AllowConcurrent)
{
throw new InvalidOperationException("Workflow must only consist of cross-run share-capable or factory-created executors. Executors " +
$"not supporting concurrent: {string.Join(", ", workflow.NonConcurrentExecutorIds)}");
}
this.RunId = runId ?? Guid.NewGuid().ToString("N");
this.StartExecutorId = workflow.StartExecutorId;
this.Workflow = Throw.IfNull(workflow);
this.RunContext = new InProcessRunnerContext(workflow, this.RunId, withCheckpointing: checkpointManager != null, this.OutgoingEvents, this.StepTracer, workflowOwnership, subworkflow);
this.RunContext = new InProcessRunnerContext(workflow, this.RunId, withCheckpointing: checkpointManager != null, this.OutgoingEvents, this.StepTracer, existingOwnerSignoff, subworkflow, enableConcurrentRuns);
this.CheckpointManager = checkpointManager;
this._knownValidInputTypes = knownValidInputTypes != null
? [.. knownValidInputTypes]
: [];
// Initialize the runners for each of the edges, along with the state for edges that
// need it.
// Initialize the runners for each of the edges, along with the state for edges that need it.
this.EdgeMap = new EdgeMap(this.RunContext, this.Workflow.Edges, this.Workflow.Ports.Values, this.Workflow.StartExecutorId, this.StepTracer);
}