mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: [BREAKING] Workflows API Review Naming Changes (Part 1?) (#4090)
* refactor: Normalize Run/RunStreaming with AIAgent * refactor: Clarify Session vs. Run -level concepts * Rename RunId to SessionId to better match Run/Session terminology in AIAgent * [BREAKING]: Will break existing checkpointed sessions in CosmosDb due to field rename * refactor: Rename and simplify interface around getting typed data out of ExternalRequest/Response * Also adds hints around using value types in PortableValue * refactor: Rename AddFanInEdge to AddFanInBarrierEdge This will prevent a breaking change later when we introduce a programmable FanIn edge, analogous to the FanOut edge's EdgeSelector. The goal, in the long run is to support a number of different FanIn scenarios, with naive FanIn (no barrier) by default, similar to FanOut. * refactor: AsAgent(this Workflow, ...) => AsAIAgent(...) * misc - part1: SwitchBuilder internal --------- Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com>
This commit is contained in:
co-authored by
Dmytro Struk
parent
5fd260e11d
commit
0086d38f58
+39
-39
@@ -74,7 +74,7 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
this._database = await this._cosmosClient.CreateDatabaseIfNotExistsAsync(s_testDatabaseId);
|
||||
await this._database.CreateContainerIfNotExistsAsync(
|
||||
TestContainerId,
|
||||
"/runId",
|
||||
"/sessionId",
|
||||
throughput: 400);
|
||||
|
||||
this._emulatorAvailable = true;
|
||||
@@ -184,15 +184,15 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
|
||||
// Arrange
|
||||
using var store = new CosmosCheckpointStore(this._cosmosClient!, s_testDatabaseId, TestContainerId);
|
||||
var runId = Guid.NewGuid().ToString();
|
||||
var sessionId = Guid.NewGuid().ToString();
|
||||
var checkpointValue = JsonSerializer.SerializeToElement(new { data = "test checkpoint" }, s_jsonOptions);
|
||||
|
||||
// Act
|
||||
var checkpointInfo = await store.CreateCheckpointAsync(runId, checkpointValue);
|
||||
var checkpointInfo = await store.CreateCheckpointAsync(sessionId, checkpointValue);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(checkpointInfo);
|
||||
Assert.Equal(runId, checkpointInfo.RunId);
|
||||
Assert.Equal(sessionId, checkpointInfo.SessionId);
|
||||
Assert.NotNull(checkpointInfo.CheckpointId);
|
||||
Assert.NotEmpty(checkpointInfo.CheckpointId);
|
||||
}
|
||||
@@ -204,13 +204,13 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
|
||||
// Arrange
|
||||
using var store = new CosmosCheckpointStore(this._cosmosClient!, s_testDatabaseId, TestContainerId);
|
||||
var runId = Guid.NewGuid().ToString();
|
||||
var sessionId = Guid.NewGuid().ToString();
|
||||
var originalData = new { message = "Hello, World!", timestamp = DateTimeOffset.UtcNow };
|
||||
var checkpointValue = JsonSerializer.SerializeToElement(originalData, s_jsonOptions);
|
||||
|
||||
// Act
|
||||
var checkpointInfo = await store.CreateCheckpointAsync(runId, checkpointValue);
|
||||
var retrievedValue = await store.RetrieveCheckpointAsync(runId, checkpointInfo);
|
||||
var checkpointInfo = await store.CreateCheckpointAsync(sessionId, checkpointValue);
|
||||
var retrievedValue = await store.RetrieveCheckpointAsync(sessionId, checkpointInfo);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(JsonValueKind.Object, retrievedValue.ValueKind);
|
||||
@@ -225,12 +225,12 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
|
||||
// Arrange
|
||||
using var store = new CosmosCheckpointStore(this._cosmosClient!, s_testDatabaseId, TestContainerId);
|
||||
var runId = Guid.NewGuid().ToString();
|
||||
var fakeCheckpointInfo = new CheckpointInfo(runId, "nonexistent-checkpoint");
|
||||
var sessionId = Guid.NewGuid().ToString();
|
||||
var fakeCheckpointInfo = new CheckpointInfo(sessionId, "nonexistent-checkpoint");
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
store.RetrieveCheckpointAsync(runId, fakeCheckpointInfo).AsTask());
|
||||
store.RetrieveCheckpointAsync(sessionId, fakeCheckpointInfo).AsTask());
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
@@ -240,10 +240,10 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
|
||||
// Arrange
|
||||
using var store = new CosmosCheckpointStore(this._cosmosClient!, s_testDatabaseId, TestContainerId);
|
||||
var runId = Guid.NewGuid().ToString();
|
||||
var sessionId = Guid.NewGuid().ToString();
|
||||
|
||||
// Act
|
||||
var index = await store.RetrieveIndexAsync(runId);
|
||||
var index = await store.RetrieveIndexAsync(sessionId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(index);
|
||||
@@ -257,16 +257,16 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
|
||||
// Arrange
|
||||
using var store = new CosmosCheckpointStore(this._cosmosClient!, s_testDatabaseId, TestContainerId);
|
||||
var runId = Guid.NewGuid().ToString();
|
||||
var sessionId = Guid.NewGuid().ToString();
|
||||
var checkpointValue = JsonSerializer.SerializeToElement(new { data = "test" }, s_jsonOptions);
|
||||
|
||||
// Create multiple checkpoints
|
||||
var checkpoint1 = await store.CreateCheckpointAsync(runId, checkpointValue);
|
||||
var checkpoint2 = await store.CreateCheckpointAsync(runId, checkpointValue);
|
||||
var checkpoint3 = await store.CreateCheckpointAsync(runId, checkpointValue);
|
||||
var checkpoint1 = await store.CreateCheckpointAsync(sessionId, checkpointValue);
|
||||
var checkpoint2 = await store.CreateCheckpointAsync(sessionId, checkpointValue);
|
||||
var checkpoint3 = await store.CreateCheckpointAsync(sessionId, checkpointValue);
|
||||
|
||||
// Act
|
||||
var index = (await store.RetrieveIndexAsync(runId)).ToList();
|
||||
var index = (await store.RetrieveIndexAsync(sessionId)).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, index.Count);
|
||||
@@ -282,17 +282,17 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
|
||||
// Arrange
|
||||
using var store = new CosmosCheckpointStore(this._cosmosClient!, s_testDatabaseId, TestContainerId);
|
||||
var runId = Guid.NewGuid().ToString();
|
||||
var sessionId = Guid.NewGuid().ToString();
|
||||
var checkpointValue = JsonSerializer.SerializeToElement(new { data = "test" }, s_jsonOptions);
|
||||
|
||||
// Act
|
||||
var parentCheckpoint = await store.CreateCheckpointAsync(runId, checkpointValue);
|
||||
var childCheckpoint = await store.CreateCheckpointAsync(runId, checkpointValue, parentCheckpoint);
|
||||
var parentCheckpoint = await store.CreateCheckpointAsync(sessionId, checkpointValue);
|
||||
var childCheckpoint = await store.CreateCheckpointAsync(sessionId, checkpointValue, parentCheckpoint);
|
||||
|
||||
// Assert
|
||||
Assert.NotEqual(parentCheckpoint.CheckpointId, childCheckpoint.CheckpointId);
|
||||
Assert.Equal(runId, parentCheckpoint.RunId);
|
||||
Assert.Equal(runId, childCheckpoint.RunId);
|
||||
Assert.Equal(sessionId, parentCheckpoint.SessionId);
|
||||
Assert.Equal(sessionId, childCheckpoint.SessionId);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
@@ -302,20 +302,20 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
|
||||
// Arrange
|
||||
using var store = new CosmosCheckpointStore(this._cosmosClient!, s_testDatabaseId, TestContainerId);
|
||||
var runId = Guid.NewGuid().ToString();
|
||||
var sessionId = Guid.NewGuid().ToString();
|
||||
var checkpointValue = JsonSerializer.SerializeToElement(new { data = "test" }, s_jsonOptions);
|
||||
|
||||
// Create parent and child checkpoints
|
||||
var parent = await store.CreateCheckpointAsync(runId, checkpointValue);
|
||||
var child1 = await store.CreateCheckpointAsync(runId, checkpointValue, parent);
|
||||
var child2 = await store.CreateCheckpointAsync(runId, checkpointValue, parent);
|
||||
var parent = await store.CreateCheckpointAsync(sessionId, checkpointValue);
|
||||
var child1 = await store.CreateCheckpointAsync(sessionId, checkpointValue, parent);
|
||||
var child2 = await store.CreateCheckpointAsync(sessionId, checkpointValue, parent);
|
||||
|
||||
// Create an orphan checkpoint
|
||||
var orphan = await store.CreateCheckpointAsync(runId, checkpointValue);
|
||||
var orphan = await store.CreateCheckpointAsync(sessionId, checkpointValue);
|
||||
|
||||
// Act
|
||||
var allCheckpoints = (await store.RetrieveIndexAsync(runId)).ToList();
|
||||
var childrenOfParent = (await store.RetrieveIndexAsync(runId, parent)).ToList();
|
||||
var allCheckpoints = (await store.RetrieveIndexAsync(sessionId)).ToList();
|
||||
var childrenOfParent = (await store.RetrieveIndexAsync(sessionId, parent)).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Equal(4, allCheckpoints.Count); // parent + 2 children + orphan
|
||||
@@ -338,16 +338,16 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
|
||||
// Arrange
|
||||
using var store = new CosmosCheckpointStore(this._cosmosClient!, s_testDatabaseId, TestContainerId);
|
||||
var runId1 = Guid.NewGuid().ToString();
|
||||
var runId2 = Guid.NewGuid().ToString();
|
||||
var sessionId1 = Guid.NewGuid().ToString();
|
||||
var sessionId2 = Guid.NewGuid().ToString();
|
||||
var checkpointValue = JsonSerializer.SerializeToElement(new { data = "test" }, s_jsonOptions);
|
||||
|
||||
// Act
|
||||
var checkpoint1 = await store.CreateCheckpointAsync(runId1, checkpointValue);
|
||||
var checkpoint2 = await store.CreateCheckpointAsync(runId2, checkpointValue);
|
||||
var checkpoint1 = await store.CreateCheckpointAsync(sessionId1, checkpointValue);
|
||||
var checkpoint2 = await store.CreateCheckpointAsync(sessionId2, checkpointValue);
|
||||
|
||||
var index1 = (await store.RetrieveIndexAsync(runId1)).ToList();
|
||||
var index2 = (await store.RetrieveIndexAsync(runId2)).ToList();
|
||||
var index1 = (await store.RetrieveIndexAsync(sessionId1)).ToList();
|
||||
var index2 = (await store.RetrieveIndexAsync(sessionId2)).ToList();
|
||||
|
||||
// Assert
|
||||
Assert.Single(index1);
|
||||
@@ -362,7 +362,7 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
#region Error Handling Tests
|
||||
|
||||
[SkippableFact]
|
||||
public async Task CreateCheckpointAsync_WithNullRunId_ThrowsArgumentExceptionAsync()
|
||||
public async Task CreateCheckpointAsync_WithNullSessionId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
this.SkipIfEmulatorNotAvailable();
|
||||
|
||||
@@ -376,7 +376,7 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task CreateCheckpointAsync_WithEmptyRunId_ThrowsArgumentExceptionAsync()
|
||||
public async Task CreateCheckpointAsync_WithEmptySessionId_ThrowsArgumentExceptionAsync()
|
||||
{
|
||||
this.SkipIfEmulatorNotAvailable();
|
||||
|
||||
@@ -396,11 +396,11 @@ public class CosmosCheckpointStoreTests : IAsyncLifetime, IDisposable
|
||||
|
||||
// Arrange
|
||||
using var store = new CosmosCheckpointStore(this._cosmosClient!, s_testDatabaseId, TestContainerId);
|
||||
var runId = Guid.NewGuid().ToString();
|
||||
var sessionId = Guid.NewGuid().ToString();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentNullException>(() =>
|
||||
store.RetrieveCheckpointAsync(runId, null!).AsTask());
|
||||
store.RetrieveCheckpointAsync(sessionId, null!).AsTask());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
+2
-2
@@ -45,7 +45,7 @@ internal sealed class WorkflowHarness(Workflow workflow, string runId)
|
||||
public async Task<WorkflowEvents> RunWorkflowAsync<TInput>(TInput input, bool useJson = false) where TInput : notnull
|
||||
{
|
||||
Console.WriteLine("RUNNING WORKFLOW...");
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, input, this.GetCheckpointManager(useJson), runId);
|
||||
StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, input, this.GetCheckpointManager(useJson), runId);
|
||||
IReadOnlyList<WorkflowEvent> workflowEvents = await MonitorAndDisposeWorkflowRunAsync(run).ToArrayAsync();
|
||||
this._lastCheckpoint = workflowEvents.OfType<SuperStepCompletedEvent>().LastOrDefault()?.CompletionInfo?.Checkpoint;
|
||||
return new WorkflowEvents(workflowEvents);
|
||||
@@ -55,7 +55,7 @@ internal sealed class WorkflowHarness(Workflow workflow, string runId)
|
||||
{
|
||||
Console.WriteLine("\nRESUMING WORKFLOW...");
|
||||
Assert.NotNull(this._lastCheckpoint);
|
||||
StreamingRun run = await InProcessExecution.ResumeStreamAsync(workflow, this._lastCheckpoint, this.GetCheckpointManager());
|
||||
StreamingRun run = await InProcessExecution.ResumeStreamingAsync(workflow, this._lastCheckpoint, this.GetCheckpointManager());
|
||||
IReadOnlyList<WorkflowEvent> workflowEvents = await MonitorAndDisposeWorkflowRunAsync(run, response).ToArrayAsync();
|
||||
this._lastCheckpoint = workflowEvents.OfType<SuperStepCompletedEvent>().LastOrDefault()?.CompletionInfo?.Checkpoint;
|
||||
return new WorkflowEvents(workflowEvents);
|
||||
|
||||
+2
-2
@@ -272,7 +272,7 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
// Arrange
|
||||
const string WorkflowInput = "Test input message";
|
||||
Workflow workflow = this.CreateWorkflow(workflowPath, WorkflowInput);
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow: workflow, input: WorkflowInput);
|
||||
await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow: workflow, input: WorkflowInput);
|
||||
|
||||
// Act
|
||||
await foreach (WorkflowEvent workflowEvent in run.WatchStreamAsync())
|
||||
@@ -330,7 +330,7 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
private async Task RunWorkflowAsync<TInput>(string workflowPath, TInput workflowInput) where TInput : notnull
|
||||
{
|
||||
Workflow workflow = this.CreateWorkflow(workflowPath, workflowInput);
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, workflowInput);
|
||||
await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, workflowInput);
|
||||
|
||||
await foreach (WorkflowEvent workflowEvent in run.WatchStreamAsync())
|
||||
{
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ public abstract class WorkflowActionExecutorTest(ITestOutputHelper output) : Wor
|
||||
prevExecutor = executor;
|
||||
}
|
||||
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflowBuilder.Build(), this.State);
|
||||
await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflowBuilder.Build(), this.State);
|
||||
WorkflowEvent[] events = await run.WatchStreamAsync().ToArrayAsync();
|
||||
|
||||
if (isDiscrete)
|
||||
|
||||
@@ -25,7 +25,7 @@ public class AgentEventsTests
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, new List<ChatMessage> { new(ChatRole.User, "Hello") });
|
||||
await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, new List<ChatMessage> { new(ChatRole.User, "Hello") });
|
||||
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
|
||||
List<WorkflowOutputEvent> outputEvents = new();
|
||||
|
||||
@@ -642,7 +642,7 @@ public class AgentWorkflowBuilderTests
|
||||
StringBuilder sb = new();
|
||||
|
||||
InProcessExecutionEnvironment environment = executionEnvironment.ToWorkflowExecutionEnvironment();
|
||||
await using StreamingRun run = await environment.StreamAsync(workflow, input);
|
||||
await using StreamingRun run = await environment.RunStreamingAsync(workflow, input);
|
||||
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
|
||||
WorkflowOutputEvent? output = null;
|
||||
|
||||
@@ -33,7 +33,7 @@ public class CheckpointParentTests
|
||||
|
||||
// Act
|
||||
StreamingRun run =
|
||||
await env.WithCheckpointing(checkpointManager).StreamAsync(workflow, "Hello");
|
||||
await env.WithCheckpointing(checkpointManager).RunStreamingAsync(workflow, "Hello");
|
||||
|
||||
List<CheckpointInfo> checkpoints = [];
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
@@ -49,7 +49,7 @@ public class CheckpointParentTests
|
||||
|
||||
CheckpointInfo firstCheckpoint = checkpoints[0];
|
||||
Checkpoint storedFirst = await ((ICheckpointManager)checkpointManager)
|
||||
.LookupCheckpointAsync(firstCheckpoint.RunId, firstCheckpoint);
|
||||
.LookupCheckpointAsync(firstCheckpoint.SessionId, firstCheckpoint);
|
||||
storedFirst.Parent.Should().BeNull("the first checkpoint should have no parent");
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class CheckpointParentTests
|
||||
InProcessExecutionEnvironment env = environment.ToWorkflowExecutionEnvironment();
|
||||
|
||||
// Act
|
||||
await using StreamingRun run = await env.WithCheckpointing(checkpointManager).StreamAsync(workflow, "Hello");
|
||||
await using StreamingRun run = await env.WithCheckpointing(checkpointManager).RunStreamingAsync(workflow, "Hello");
|
||||
|
||||
List<CheckpointInfo> checkpoints = [];
|
||||
using CancellationTokenSource cts = new();
|
||||
@@ -94,16 +94,16 @@ public class CheckpointParentTests
|
||||
|
||||
// Verify the parent chain
|
||||
Checkpoint stored0 = await ((ICheckpointManager)checkpointManager)
|
||||
.LookupCheckpointAsync(checkpoints[0].RunId, checkpoints[0]);
|
||||
.LookupCheckpointAsync(checkpoints[0].SessionId, checkpoints[0]);
|
||||
stored0.Parent.Should().BeNull("the first checkpoint should have no parent");
|
||||
|
||||
Checkpoint stored1 = await ((ICheckpointManager)checkpointManager)
|
||||
.LookupCheckpointAsync(checkpoints[1].RunId, checkpoints[1]);
|
||||
.LookupCheckpointAsync(checkpoints[1].SessionId, checkpoints[1]);
|
||||
stored1.Parent.Should().NotBeNull("the second checkpoint should have a parent");
|
||||
stored1.Parent.Should().Be(checkpoints[0], "the second checkpoint's parent should be the first checkpoint");
|
||||
|
||||
Checkpoint stored2 = await ((ICheckpointManager)checkpointManager)
|
||||
.LookupCheckpointAsync(checkpoints[2].RunId, checkpoints[2]);
|
||||
.LookupCheckpointAsync(checkpoints[2].SessionId, checkpoints[2]);
|
||||
stored2.Parent.Should().NotBeNull("the third checkpoint should have a parent");
|
||||
stored2.Parent.Should().Be(checkpoints[1], "the third checkpoint's parent should be the second checkpoint");
|
||||
}
|
||||
@@ -126,7 +126,7 @@ public class CheckpointParentTests
|
||||
InProcessExecutionEnvironment env = environment.ToWorkflowExecutionEnvironment();
|
||||
|
||||
// First run: collect a checkpoint to resume from
|
||||
await using StreamingRun run = await env.WithCheckpointing(checkpointManager).StreamAsync(workflow, "Hello");
|
||||
await using StreamingRun run = await env.WithCheckpointing(checkpointManager).RunStreamingAsync(workflow, "Hello");
|
||||
|
||||
List<CheckpointInfo> firstRunCheckpoints = [];
|
||||
using CancellationTokenSource cts = new();
|
||||
@@ -149,7 +149,7 @@ public class CheckpointParentTests
|
||||
await run.DisposeAsync();
|
||||
|
||||
// Act: Resume from the first checkpoint
|
||||
StreamingRun resumed = await env.WithCheckpointing(checkpointManager).ResumeStreamAsync(workflow, resumePoint);
|
||||
StreamingRun resumed = await env.WithCheckpointing(checkpointManager).ResumeStreamingAsync(workflow, resumePoint);
|
||||
|
||||
List<CheckpointInfo> resumedCheckpoints = [];
|
||||
using CancellationTokenSource cts2 = new();
|
||||
@@ -168,7 +168,7 @@ public class CheckpointParentTests
|
||||
// Assert: The first checkpoint after resume should have the resume point as its parent.
|
||||
resumedCheckpoints.Should().NotBeEmpty();
|
||||
Checkpoint storedResumed = await ((ICheckpointManager)checkpointManager)
|
||||
.LookupCheckpointAsync(resumedCheckpoints[0].RunId, resumedCheckpoints[0]);
|
||||
.LookupCheckpointAsync(resumedCheckpoints[0].SessionId, resumedCheckpoints[0]);
|
||||
storedResumed.Parent.Should().NotBeNull("checkpoint created after resume should have a parent");
|
||||
storedResumed.Parent.Should().Be(resumePoint, "checkpoint after resume should reference the checkpoint we resumed from");
|
||||
}
|
||||
|
||||
@@ -9,35 +9,35 @@ namespace Microsoft.Agents.AI.Workflows.UnitTests;
|
||||
|
||||
internal sealed class InMemoryJsonStore : JsonCheckpointStore
|
||||
{
|
||||
private readonly Dictionary<string, RunCheckpointCache<JsonElement>> _store = [];
|
||||
private readonly Dictionary<string, SessionCheckpointCache<JsonElement>> _store = [];
|
||||
|
||||
private RunCheckpointCache<JsonElement> EnsureRunStore(string runId)
|
||||
private SessionCheckpointCache<JsonElement> EnsureSessionStore(string sessionId)
|
||||
{
|
||||
if (!this._store.TryGetValue(runId, out RunCheckpointCache<JsonElement>? runStore))
|
||||
if (!this._store.TryGetValue(sessionId, out SessionCheckpointCache<JsonElement>? runStore))
|
||||
{
|
||||
runStore = this._store[runId] = new();
|
||||
runStore = this._store[sessionId] = new();
|
||||
}
|
||||
|
||||
return runStore;
|
||||
}
|
||||
|
||||
public override ValueTask<CheckpointInfo> CreateCheckpointAsync(string runId, JsonElement value, CheckpointInfo? parent = null)
|
||||
public override ValueTask<CheckpointInfo> CreateCheckpointAsync(string sessionId, JsonElement value, CheckpointInfo? parent = null)
|
||||
{
|
||||
return new(this.EnsureRunStore(runId).Add(runId, value));
|
||||
return new(this.EnsureSessionStore(sessionId).Add(sessionId, value));
|
||||
}
|
||||
|
||||
public override ValueTask<JsonElement> RetrieveCheckpointAsync(string runId, CheckpointInfo key)
|
||||
public override ValueTask<JsonElement> RetrieveCheckpointAsync(string sessionId, CheckpointInfo key)
|
||||
{
|
||||
if (!this.EnsureRunStore(runId).TryGet(key, out JsonElement result))
|
||||
if (!this.EnsureSessionStore(sessionId).TryGet(key, out JsonElement result))
|
||||
{
|
||||
throw new KeyNotFoundException("Could not retrieve checkpoint with id {key.CheckpointId} for run {runId}");
|
||||
throw new KeyNotFoundException($"Could not retrieve checkpoint with id {key.CheckpointId} for session {sessionId}");
|
||||
}
|
||||
|
||||
return new(result);
|
||||
}
|
||||
|
||||
public override ValueTask<IEnumerable<CheckpointInfo>> RetrieveIndexAsync(string runId, CheckpointInfo? withParent = null)
|
||||
public override ValueTask<IEnumerable<CheckpointInfo>> RetrieveIndexAsync(string sessionId, CheckpointInfo? withParent = null)
|
||||
{
|
||||
return new(this.EnsureRunStore(runId).Index);
|
||||
return new(this.EnsureSessionStore(sessionId).Index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class InProcessExecutionTests
|
||||
var inputMessage = new ChatMessage(ChatRole.User, "Hello");
|
||||
|
||||
// Act: Execute using streaming version with TurnToken
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, new List<ChatMessage> { inputMessage });
|
||||
await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, new List<ChatMessage> { inputMessage });
|
||||
|
||||
// Send TurnToken to actually trigger execution (this is the key step)
|
||||
bool messageSent = await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
@@ -108,7 +108,7 @@ public class InProcessExecutionTests
|
||||
var nonStreamingEvents = nonStreamingRun.OutgoingEvents.ToList();
|
||||
|
||||
// Act 2: Execute using StreamAsync (streaming) with TurnToken
|
||||
await using StreamingRun streamingRun = await InProcessExecution.StreamAsync(workflow2, new List<ChatMessage> { inputMessage });
|
||||
await using StreamingRun streamingRun = await InProcessExecution.RunStreamingAsync(workflow2, new List<ChatMessage> { inputMessage });
|
||||
await streamingRun.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
|
||||
List<WorkflowEvent> streamingEvents = [];
|
||||
|
||||
@@ -47,7 +47,7 @@ public class RepresentationTests
|
||||
{
|
||||
ExecutorInfo info = binding.ToExecutorInfo();
|
||||
|
||||
info.IsMatch(await binding.CreateInstanceAsync(runId: string.Empty)).Should().BeTrue();
|
||||
info.IsMatch(await binding.CreateInstanceAsync(sessionId: string.Empty)).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
+1
-2
@@ -28,8 +28,7 @@ internal static class Step1EntryPoint
|
||||
|
||||
public static async ValueTask RunAsync(TextWriter writer, IWorkflowExecutionEnvironment environment)
|
||||
{
|
||||
// TODO: Potentially normalize terminology viz Agent.RunStreamingAsync
|
||||
StreamingRun run = await environment.StreamAsync(WorkflowInstance, input: "Hello, World!").ConfigureAwait(false);
|
||||
StreamingRun run = await environment.RunStreamingAsync(WorkflowInstance, input: "Hello, World!").ConfigureAwait(false);
|
||||
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
{
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ internal static class Step2EntryPoint
|
||||
|
||||
public static async ValueTask<string> RunAsync(TextWriter writer, IWorkflowExecutionEnvironment environment, string input = "This is a spam message.")
|
||||
{
|
||||
StreamingRun handle = await environment.StreamAsync(WorkflowInstance, input: input).ConfigureAwait(false);
|
||||
StreamingRun handle = await environment.RunStreamingAsync(WorkflowInstance, input: input).ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in handle.WatchStreamAsync().ConfigureAwait(false))
|
||||
{
|
||||
switch (evt)
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ internal static class Step3EntryPoint
|
||||
|
||||
public static async ValueTask<string> RunAsync(TextWriter writer, IWorkflowExecutionEnvironment environment)
|
||||
{
|
||||
StreamingRun run = await environment.StreamAsync(WorkflowInstance, NumberSignal.Init).ConfigureAwait(false);
|
||||
StreamingRun run = await environment.RunStreamingAsync(WorkflowInstance, NumberSignal.Init).ConfigureAwait(false);
|
||||
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
{
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ internal static class Step4EntryPoint
|
||||
string? prompt = UpdatePrompt(null, signal);
|
||||
|
||||
Workflow workflow = WorkflowInstance;
|
||||
StreamingRun handle = await environment.StreamAsync(workflow, NumberSignal.Init).ConfigureAwait(false);
|
||||
StreamingRun handle = await environment.RunStreamingAsync(workflow, NumberSignal.Init).ConfigureAwait(false);
|
||||
|
||||
List<ExternalRequest> requests = [];
|
||||
await foreach (WorkflowEvent evt in handle.WatchStreamAsync().ConfigureAwait(false))
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@ internal static class Step5EntryPoint
|
||||
|
||||
StreamingRun handle =
|
||||
await environment.WithCheckpointing(checkpointManager)
|
||||
.StreamAsync(workflow, NumberSignal.Init)
|
||||
.RunStreamingAsync(workflow, NumberSignal.Init)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
List<CheckpointInfo> checkpoints = [];
|
||||
@@ -38,13 +38,13 @@ internal static class Step5EntryPoint
|
||||
|
||||
CheckpointInfo targetCheckpoint = checkpoints[2];
|
||||
|
||||
Console.WriteLine($"Restoring to checkpoint {targetCheckpoint} from run {targetCheckpoint.RunId}");
|
||||
Console.WriteLine($"Restoring to checkpoint {targetCheckpoint} from session {targetCheckpoint.SessionId}");
|
||||
if (rehydrateToRestore)
|
||||
{
|
||||
await handle.DisposeAsync().ConfigureAwait(false);
|
||||
|
||||
handle = await environment.WithCheckpointing(checkpointManager)
|
||||
.ResumeStreamAsync(workflow, targetCheckpoint, CancellationToken.None)
|
||||
.ResumeStreamingAsync(workflow, targetCheckpoint, CancellationToken.None)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ internal static class Step6EntryPoint
|
||||
{
|
||||
Workflow workflow = CreateWorkflow(maxSteps);
|
||||
|
||||
StreamingRun run = await environment.StreamAsync(workflow, Array.Empty<ChatMessage>())
|
||||
StreamingRun run = await environment.RunStreamingAsync(workflow, Array.Empty<ChatMessage>())
|
||||
.ConfigureAwait(false);
|
||||
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ internal static class Step7EntryPoint
|
||||
{
|
||||
Workflow workflow = Step6EntryPoint.CreateWorkflow(maxSteps);
|
||||
|
||||
AIAgent agent = workflow.AsAgent("group-chat-agent", "Group Chat Agent");
|
||||
AIAgent agent = workflow.AsAIAgent("group-chat-agent", "Group Chat Agent");
|
||||
|
||||
for (int i = 0; i < numIterations; i++)
|
||||
{
|
||||
|
||||
+13
-13
@@ -69,10 +69,10 @@ internal static class Step9EntryPoint
|
||||
|
||||
var requestPort = RequestPort.Create<TRequest, TResponse>(id);
|
||||
|
||||
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>());
|
||||
return builder.ForwardMessage<ExternalRequest>(source, targets: [filter], condition: message => message.IsDataOfType<TRequest>())
|
||||
.ForwardMessage<ExternalRequest>(filter, targets: [requestPort], condition: message => message.IsDataOfType<TRequest>())
|
||||
.ForwardMessage<ExternalResponse>(requestPort, targets: [filter], condition: message => message.IsDataOfType<TResponse>())
|
||||
.ForwardMessage<ExternalResponse>(filter, targets: [source], condition: message => message.IsDataOfType<TResponse>());
|
||||
}
|
||||
|
||||
public static WorkflowBuilder AddExternalRequest<TRequest, TResponse>(this WorkflowBuilder builder, ExecutorBinding source, string? id = null)
|
||||
@@ -207,11 +207,11 @@ internal static class Step9EntryPoint
|
||||
}
|
||||
else if (evt is RequestInfoEvent requestInfoEvent)
|
||||
{
|
||||
if (requestInfoEvent.Request.DataIs<ResourceRequest>())
|
||||
if (requestInfoEvent.Request.IsDataOfType<ResourceRequest>())
|
||||
{
|
||||
resourceRequests.Add(requestInfoEvent.Request);
|
||||
}
|
||||
else if (requestInfoEvent.Request.DataIs<PolicyCheckRequest>())
|
||||
else if (requestInfoEvent.Request.IsDataOfType<PolicyCheckRequest>())
|
||||
{
|
||||
policyRequests.Add(requestInfoEvent.Request);
|
||||
}
|
||||
@@ -237,14 +237,14 @@ internal static class Step9EntryPoint
|
||||
|
||||
foreach (ExternalRequest request in resourceRequests)
|
||||
{
|
||||
ResourceRequest resourceRequest = request.DataAs<ResourceRequest>()!;
|
||||
ResourceRequest resourceRequest = request.Data.As<ResourceRequest>()!;
|
||||
resourceRequest.Id.Should().BeOneOf(ResourceMissIds);
|
||||
responses.Add(request.CreateResponse(Part2FinishedResponses[resourceRequest.Id].ResourceResponse!));
|
||||
}
|
||||
|
||||
foreach (ExternalRequest request in policyRequests)
|
||||
{
|
||||
PolicyCheckRequest policyRequest = request.DataAs<PolicyCheckRequest>()!;
|
||||
PolicyCheckRequest policyRequest = request.Data.As<PolicyCheckRequest>()!;
|
||||
policyRequest.Id.Should().BeOneOf(PolicyMissIds);
|
||||
responses.Add(request.CreateResponse(Part2FinishedResponses[policyRequest.Id].PolicyResponse!));
|
||||
}
|
||||
@@ -372,7 +372,7 @@ internal sealed class ResourceCache()
|
||||
|
||||
private async ValueTask UnwrapAndHandleRequestAsync(ExternalRequest request, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (request.DataIs(out ResourceRequest? resourceRequest))
|
||||
if (request.TryGetDataAs(out ResourceRequest? resourceRequest))
|
||||
{
|
||||
ResourceResponse? response = await this.TryHandleResourceRequestAsync(resourceRequest, context, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
@@ -421,7 +421,7 @@ internal sealed class ResourceCache()
|
||||
|
||||
private ValueTask CollectResultAsync(ExternalResponse response, IWorkflowContext context)
|
||||
{
|
||||
if (response.DataIs<ResourceResponse>())
|
||||
if (response.IsDataOfType<ResourceResponse>())
|
||||
{
|
||||
// Normally we'd update the cache according to whatever logic we want here.
|
||||
return context.SendMessageAsync(response);
|
||||
@@ -459,7 +459,7 @@ internal sealed class QuotaPolicyEngine()
|
||||
|
||||
private async ValueTask UnwrapAndHandleRequestAsync(ExternalRequest request, IWorkflowContext context)
|
||||
{
|
||||
if (request.DataIs(out PolicyCheckRequest? policyRquest))
|
||||
if (request.TryGetDataAs(out PolicyCheckRequest? policyRquest))
|
||||
{
|
||||
PolicyResponse? response = await this.TryHandlePolicyCheckRequestAsync(policyRquest, context)
|
||||
.ConfigureAwait(false);
|
||||
@@ -507,7 +507,7 @@ internal sealed class QuotaPolicyEngine()
|
||||
}
|
||||
private ValueTask CollectAndForwardAsync(ExternalResponse response, IWorkflowContext context)
|
||||
{
|
||||
if (response.DataIs<PolicyResponse>())
|
||||
if (response.IsDataOfType<PolicyResponse>())
|
||||
{
|
||||
return context.SendMessageAsync(response);
|
||||
}
|
||||
@@ -569,7 +569,7 @@ internal sealed class Coordinator() : Executor(nameof(Coordinator), declareCross
|
||||
|
||||
internal async ValueTask RunWorkflowHandleEventsAsync<TInput>(Workflow workflow, TInput input) where TInput : notnull
|
||||
{
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, input);
|
||||
StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, input);
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
switch (evt)
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ internal static class Step10EntryPoint
|
||||
|
||||
public static async ValueTask RunAsync(TextWriter writer, IWorkflowExecutionEnvironment executionEnvironment, IEnumerable<string> inputs)
|
||||
{
|
||||
AIAgent hostAgent = WorkflowInstance.AsAgent("echo-workflow", "EchoW", executionEnvironment: executionEnvironment);
|
||||
AIAgent hostAgent = WorkflowInstance.AsAIAgent("echo-workflow", "EchoW", executionEnvironment: executionEnvironment);
|
||||
|
||||
AgentSession session = await hostAgent.CreateSessionAsync();
|
||||
foreach (string input in inputs)
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ internal static class Step11EntryPoint
|
||||
|
||||
public static async ValueTask RunAsync(TextWriter writer, IWorkflowExecutionEnvironment executionEnvironment, IEnumerable<string> inputs)
|
||||
{
|
||||
AIAgent hostAgent = WorkflowInstance.AsAgent("echo-workflow", "EchoW", executionEnvironment: executionEnvironment);
|
||||
AIAgent hostAgent = WorkflowInstance.AsAIAgent("echo-workflow", "EchoW", executionEnvironment: executionEnvironment);
|
||||
|
||||
AgentSession session = await hostAgent.CreateSessionAsync();
|
||||
foreach (string input in inputs)
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ internal static class Step12EntryPoint
|
||||
|
||||
public static async ValueTask RunAsync(TextWriter writer, IWorkflowExecutionEnvironment executionEnvironment, IEnumerable<string> inputs)
|
||||
{
|
||||
AIAgent hostAgent = WorkflowInstance.AsAgent("echo-workflow", "EchoW", executionEnvironment: executionEnvironment);
|
||||
AIAgent hostAgent = WorkflowInstance.AsAIAgent("echo-workflow", "EchoW", executionEnvironment: executionEnvironment);
|
||||
|
||||
AgentSession session = await hostAgent.CreateSessionAsync();
|
||||
foreach (string input in inputs)
|
||||
|
||||
+3
-3
@@ -30,7 +30,7 @@ internal static class Step13EntryPoint
|
||||
|
||||
public static async ValueTask<AgentSession> RunAsAgentAsync(TextWriter writer, string input, IWorkflowExecutionEnvironment environment, AgentSession? session)
|
||||
{
|
||||
AIAgent hostAgent = WorkflowInstance.AsAgent("echo-workflow", "EchoW", executionEnvironment: environment, includeWorkflowOutputsInResponse: true);
|
||||
AIAgent hostAgent = WorkflowInstance.AsAIAgent("echo-workflow", "EchoW", executionEnvironment: environment, includeWorkflowOutputsInResponse: true);
|
||||
|
||||
session ??= await hostAgent.CreateSessionAsync();
|
||||
AgentResponse response;
|
||||
@@ -83,10 +83,10 @@ internal static class Step13EntryPoint
|
||||
{
|
||||
if (resumeFrom == null)
|
||||
{
|
||||
return await environment.StreamAsync(WorkflowInstance, input);
|
||||
return await environment.RunStreamingAsync(WorkflowInstance, input);
|
||||
}
|
||||
|
||||
StreamingRun run = await environment.ResumeStreamAsync(WorkflowInstance, resumeFrom);
|
||||
StreamingRun run = await environment.ResumeStreamingAsync(WorkflowInstance, resumeFrom);
|
||||
await run.TrySendMessageAsync(input);
|
||||
return run;
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ internal sealed class TestRequestAgent(TestAgentRequestType requestType, int unp
|
||||
|
||||
static TRequest AssertAndExtractRequestContent<TRequest>(ExternalRequest request)
|
||||
{
|
||||
request.DataIs(out TRequest? content).Should().BeTrue();
|
||||
request.TryGetDataAs(out TRequest? content).Should().BeTrue();
|
||||
return content!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class WorkflowHostSmokeTests
|
||||
Workflow workflow = CreateWorkflow(failByThrowing);
|
||||
|
||||
// Act
|
||||
List<AgentResponseUpdate> updates = await workflow.AsAgent("WorkflowAgent", includeExceptionDetails: includeExceptionDetails)
|
||||
List<AgentResponseUpdate> updates = await workflow.AsAIAgent("WorkflowAgent", includeExceptionDetails: includeExceptionDetails)
|
||||
.RunStreamingAsync(new ChatMessage(ChatRole.User, "Hello"))
|
||||
.ToListAsync();
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ 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([s1, s2], t) // AddFanInEdge(target, sources)
|
||||
.AddFanInBarrierEdge([s1, s2], t) // AddFanInBarrierEdge(target, sources)
|
||||
.Build();
|
||||
|
||||
var dotContent = workflow.ToDotString();
|
||||
@@ -202,7 +202,7 @@ public class WorkflowVisualizerTests
|
||||
var workflow = new WorkflowBuilder("start")
|
||||
.AddEdge<string>(start, a, Condition) // Conditional edge
|
||||
.AddFanOutEdge(a, [b, c]) // Fan-out
|
||||
.AddFanInEdge([b, c], end) // Fan-in - AddFanInEdge(target, sources)
|
||||
.AddFanInBarrierEdge([b, c], end) // Fan-in - AddFanInEdge(target, sources)
|
||||
.Build();
|
||||
|
||||
var dotContent = workflow.ToDotString();
|
||||
@@ -310,7 +310,7 @@ public class WorkflowVisualizerTests
|
||||
|
||||
var workflow = new WorkflowBuilder("start")
|
||||
.AddFanOutEdge(start, [s1, s2])
|
||||
.AddFanInEdge([s1, s2], t)
|
||||
.AddFanInBarrierEdge([s1, s2], t)
|
||||
.Build();
|
||||
|
||||
var mermaidContent = workflow.ToMermaidString();
|
||||
@@ -381,7 +381,7 @@ public class WorkflowVisualizerTests
|
||||
var workflow = new WorkflowBuilder("start")
|
||||
.AddEdge<string>(start, a, Condition) // Conditional edge
|
||||
.AddFanOutEdge(a, [b, c]) // Fan-out
|
||||
.AddFanInEdge([b, c], end) // Fan-in
|
||||
.AddFanInBarrierEdge([b, c], end) // Fan-in
|
||||
.Build();
|
||||
|
||||
var mermaidContent = workflow.ToMermaidString();
|
||||
|
||||
Reference in New Issue
Block a user