mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
39e071c430
* feat: Make Executor id field mandatory When checkpointing is involved, it is critical to keep executor ids consistent between runs, even when recreating a new object tree for the workflow. The default id-setting mechanism generated a guid for part of the id, making it not work when restoring from a checkpoint. This change prevents this situation from arising. * feat: Enable running untyped Workflows With the change to enable delay-instantiation of executors and support for async Executor factory methods, we must instantiate the starting executor to know what are the valid input types for the workflow. To avoid forcing instantiation every time, and to better support workflows with multiple input types, we enable support for build and interacting with the base Workflow type without type annotations, and remove the requirement to know a valid input type when initiating a run. * feat: Support Output from any executor and multiple outputs.
76 lines
2.6 KiB
C#
76 lines
2.6 KiB
C#
// Copyright (c) Microsoft. All rights reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Agents.Workflows.Execution;
|
|
|
|
namespace Microsoft.Agents.Workflows.UnitTests;
|
|
|
|
public class TestRunContext : IRunnerContext
|
|
{
|
|
private sealed class BoundContext(string executorId, TestRunContext runnerContext) : IWorkflowContext
|
|
{
|
|
public ValueTask AddEventAsync(WorkflowEvent workflowEvent)
|
|
=> runnerContext.AddEventAsync(workflowEvent);
|
|
|
|
public ValueTask YieldOutputAsync(object output)
|
|
=> this.AddEventAsync(new WorkflowOutputEvent(output, executorId));
|
|
|
|
public ValueTask RequestHaltAsync()
|
|
=> this.AddEventAsync(new RequestHaltEvent());
|
|
|
|
public ValueTask QueueClearScopeAsync(string? scopeName = null)
|
|
=> default;
|
|
|
|
public ValueTask QueueStateUpdateAsync<T>(string key, T? value, string? scopeName = null)
|
|
=> default;
|
|
|
|
public ValueTask<T?> ReadStateAsync<T>(string key, string? scopeName = null)
|
|
=> new(default(T?));
|
|
|
|
public ValueTask<HashSet<string>> ReadStateKeysAsync(string? scopeName = null)
|
|
=> new([]);
|
|
|
|
public ValueTask SendMessageAsync(object message, string? targetId = null)
|
|
=> runnerContext.SendMessageAsync(executorId, message, targetId);
|
|
}
|
|
|
|
public List<WorkflowEvent> Events { get; } = [];
|
|
|
|
public ValueTask AddEventAsync(WorkflowEvent workflowEvent)
|
|
{
|
|
this.Events.Add(workflowEvent);
|
|
return default;
|
|
}
|
|
|
|
public IWorkflowContext Bind(string executorId) => new BoundContext(executorId, this);
|
|
|
|
public List<ExternalRequest> ExternalRequests { get; } = [];
|
|
public ValueTask PostAsync(ExternalRequest request)
|
|
{
|
|
this.ExternalRequests.Add(request);
|
|
return default;
|
|
}
|
|
|
|
internal Dictionary<string, List<MessageEnvelope>> QueuedMessages { get; } = [];
|
|
public ValueTask SendMessageAsync(string sourceId, object message, string? targetId = null)
|
|
{
|
|
if (!this.QueuedMessages.TryGetValue(sourceId, out List<MessageEnvelope>? deliveryQueue))
|
|
{
|
|
this.QueuedMessages[sourceId] = deliveryQueue = [];
|
|
}
|
|
|
|
deliveryQueue.Add(new(message, targetId: targetId));
|
|
return default;
|
|
}
|
|
|
|
StepContext IRunnerContext.Advance() =>
|
|
throw new NotImplementedException();
|
|
|
|
public Dictionary<string, Executor> Executors { get; } = [];
|
|
|
|
ValueTask<Executor> IRunnerContext.EnsureExecutorAsync(string executorId, IStepTracer? tracer) =>
|
|
new(this.Executors[executorId]);
|
|
}
|