mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Remove reflection samples (#1460)
* Removing usage of ReflectingExecutor<T> from workflow samples. * Removing uneeded changes. * Clean up. * Update dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Workflows/_Foundational/01_ExecutorsAndEdges/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Workflows/_Foundational/02_Streaming/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Updates per PR feedback. * Undo changes to generated file. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
38e10eab81
commit
a4039134de
@@ -6,7 +6,6 @@ using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace WorkflowCustomAgentExecutorsSample;
|
||||
@@ -50,7 +49,7 @@ public static class Program
|
||||
|
||||
// Execute the workflow
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, "Create a slogan for a new electric SUV that is affordable and fun to drive.");
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is SloganGeneratedEvent or FeedbackEvent)
|
||||
{
|
||||
@@ -107,10 +106,7 @@ internal sealed class SloganGeneratedEvent(SloganResult sloganResult) : Workflow
|
||||
/// 1. HandleAsync(string message): Handles the initial task to create a slogan.
|
||||
/// 2. HandleAsync(Feedback message): Handles feedback to improve the slogan.
|
||||
/// </summary>
|
||||
internal sealed class SloganWriterExecutor
|
||||
: ReflectingExecutor<SloganWriterExecutor>,
|
||||
IMessageHandler<string, SloganResult>,
|
||||
IMessageHandler<FeedbackResult, SloganResult>
|
||||
internal sealed class SloganWriterExecutor : Executor
|
||||
{
|
||||
private readonly AIAgent _agent;
|
||||
private readonly AgentThread _thread;
|
||||
@@ -134,6 +130,10 @@ internal sealed class SloganWriterExecutor
|
||||
this._thread = this._agent.GetNewThread();
|
||||
}
|
||||
|
||||
protected override RouteBuilder ConfigureRoutes(RouteBuilder routeBuilder) =>
|
||||
routeBuilder.AddHandler<string, SloganResult>(this.HandleAsync)
|
||||
.AddHandler<FeedbackResult, SloganResult>(this.HandleAsync);
|
||||
|
||||
public async ValueTask<SloganResult> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var result = await this._agent.RunAsync(message, this._thread, cancellationToken: cancellationToken);
|
||||
@@ -175,7 +175,7 @@ internal sealed class FeedbackEvent(FeedbackResult feedbackResult) : WorkflowEve
|
||||
/// <summary>
|
||||
/// A custom executor that uses an AI agent to provide feedback on a slogan.
|
||||
/// </summary>
|
||||
internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, IMessageHandler<SloganResult>
|
||||
internal sealed class FeedbackExecutor : Executor<SloganResult>
|
||||
{
|
||||
private readonly AIAgent _agent;
|
||||
private readonly AgentThread _thread;
|
||||
@@ -205,7 +205,7 @@ internal sealed class FeedbackExecutor : ReflectingExecutor<FeedbackExecutor>, I
|
||||
this._thread = this._agent.GetNewThread();
|
||||
}
|
||||
|
||||
public async ValueTask HandleAsync(SloganResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(SloganResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var sloganMessage = $"""
|
||||
Here is a slogan for the task '{message.Task}':
|
||||
|
||||
@@ -43,7 +43,7 @@ public static class Program
|
||||
// The agents are wrapped as executors. When they receive messages,
|
||||
// they will cache the messages and only start processing when they receive a TurnToken.
|
||||
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is AgentRunUpdateEvent executorComplete)
|
||||
{
|
||||
|
||||
@@ -35,7 +35,7 @@ public static class Program
|
||||
var chatClient = new AzureOpenAIClient(new Uri(endpoint), new AzureCliCredential()).GetChatClient(deploymentName).AsIChatClient();
|
||||
|
||||
// Create the workflow and turn it into an agent
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync(chatClient).ConfigureAwait(false);
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync(chatClient);
|
||||
var agent = workflow.AsAgent("workflow-agent", "Workflow Agent");
|
||||
var thread = agent.GetNewThread();
|
||||
|
||||
@@ -59,7 +59,7 @@ public static class Program
|
||||
static async Task ProcessInputAsync(AIAgent agent, AgentThread thread, string input)
|
||||
{
|
||||
Dictionary<string, List<AgentRunResponseUpdate>> buffer = [];
|
||||
await foreach (AgentRunResponseUpdate update in agent.RunStreamingAsync(input, thread).ConfigureAwait(false))
|
||||
await foreach (AgentRunResponseUpdate update in agent.RunStreamingAsync(input, thread))
|
||||
{
|
||||
if (update.MessageId is null)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace WorkflowAsAnAgentsSample;
|
||||
@@ -43,8 +42,7 @@ internal static class WorkflowHelper
|
||||
/// Executor that starts the concurrent processing by sending messages to the agents.
|
||||
/// </summary>
|
||||
private sealed class ConcurrentStartExecutor() :
|
||||
ReflectingExecutor<ConcurrentStartExecutor>("ConcurrentStartExecutor"),
|
||||
IMessageHandler<List<ChatMessage>>
|
||||
Executor<List<ChatMessage>>("ConcurrentStartExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts the concurrent processing by sending messages to the agents.
|
||||
@@ -53,7 +51,7 @@ internal static class WorkflowHelper
|
||||
/// <param name="context">Workflow context for accessing workflow services and adding events</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
public async ValueTask HandleAsync(List<ChatMessage> message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(List<ChatMessage> message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Broadcast the message to all connected agents. Receiving agents will queue
|
||||
// the message but will not start processing until they receive a turn token.
|
||||
@@ -67,8 +65,7 @@ internal static class WorkflowHelper
|
||||
/// Executor that aggregates the results from the concurrent agents.
|
||||
/// </summary>
|
||||
private sealed class ConcurrentAggregationExecutor() :
|
||||
ReflectingExecutor<ConcurrentAggregationExecutor>("ConcurrentAggregationExecutor"),
|
||||
IMessageHandler<ChatMessage>
|
||||
Executor<ChatMessage>("ConcurrentAggregationExecutor")
|
||||
{
|
||||
private readonly List<ChatMessage> _messages = [];
|
||||
|
||||
@@ -79,7 +76,7 @@ internal static class WorkflowHelper
|
||||
/// <param name="context">Workflow context for accessing workflow services and adding events</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
public async ValueTask HandleAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this._messages.Add(message);
|
||||
|
||||
|
||||
+7
-8
@@ -25,7 +25,7 @@ public static class Program
|
||||
private static async Task Main()
|
||||
{
|
||||
// Create the workflow
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync().ConfigureAwait(false);
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync();
|
||||
|
||||
// Create checkpoint manager
|
||||
var checkpointManager = CheckpointManager.Default;
|
||||
@@ -33,9 +33,9 @@ public static class Program
|
||||
|
||||
// Execute the workflow and save checkpoints
|
||||
await using Checkpointed<StreamingRun> checkpointedRun = await InProcessExecution
|
||||
.StreamAsync(workflow, NumberSignal.Init, checkpointManager)
|
||||
.ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
.StreamAsync(workflow, NumberSignal.Init, checkpointManager);
|
||||
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is ExecutorCompletedEvent executorCompletedEvt)
|
||||
{
|
||||
@@ -67,16 +67,15 @@ public static class Program
|
||||
Console.WriteLine($"Number of checkpoints created: {checkpoints.Count}");
|
||||
|
||||
// Rehydrate a new workflow instance from a saved checkpoint and continue execution
|
||||
var newWorkflow = await WorkflowHelper.GetWorkflowAsync().ConfigureAwait(false);
|
||||
var newWorkflow = await WorkflowHelper.GetWorkflowAsync();
|
||||
const int CheckpointIndex = 5;
|
||||
Console.WriteLine($"\n\nHydrating a new workflow instance from the {CheckpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex];
|
||||
|
||||
await using Checkpointed<StreamingRun> newCheckpointedRun =
|
||||
await InProcessExecution.ResumeStreamAsync(newWorkflow, savedCheckpoint, checkpointManager, checkpointedRun.Run.RunId)
|
||||
.ConfigureAwait(false);
|
||||
await InProcessExecution.ResumeStreamAsync(newWorkflow, savedCheckpoint, checkpointManager, checkpointedRun.Run.RunId);
|
||||
|
||||
await foreach (WorkflowEvent evt in newCheckpointedRun.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent evt in newCheckpointedRun.Run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is ExecutorCompletedEvent executorCompletedEvt)
|
||||
{
|
||||
|
||||
+12
-13
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
|
||||
namespace WorkflowCheckpointAndRehydrateSample;
|
||||
|
||||
@@ -42,7 +41,7 @@ internal enum NumberSignal
|
||||
/// <summary>
|
||||
/// Executor that makes a guess based on the current bounds.
|
||||
/// </summary>
|
||||
internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExecutor>("Guess"), IMessageHandler<NumberSignal>
|
||||
internal sealed class GuessNumberExecutor() : Executor<NumberSignal>("Guess")
|
||||
{
|
||||
/// <summary>
|
||||
/// The lower bound of the guessing range.
|
||||
@@ -69,20 +68,20 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
|
||||
private int NextGuess => (this.LowerBound + this.UpperBound) / 2;
|
||||
|
||||
public async ValueTask HandleAsync(NumberSignal message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(NumberSignal message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case NumberSignal.Init:
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken);
|
||||
break;
|
||||
case NumberSignal.Above:
|
||||
this.UpperBound = this.NextGuess - 1;
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken);
|
||||
break;
|
||||
case NumberSignal.Below:
|
||||
this.LowerBound = this.NextGuess + 1;
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -99,13 +98,13 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
(this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
(this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executor that judges the guess and provides feedback.
|
||||
/// </summary>
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
internal sealed class JudgeExecutor() : Executor<int>("Judge")
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries;
|
||||
@@ -120,20 +119,20 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
this._targetNumber = targetNumber;
|
||||
}
|
||||
|
||||
public async ValueTask HandleAsync(int message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(int message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this._tries++;
|
||||
if (message == this._targetNumber)
|
||||
{
|
||||
await context.YieldOutputAsync($"{this._targetNumber} found in {this._tries} tries!", cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.YieldOutputAsync($"{this._targetNumber} found in {this._tries} tries!", cancellationToken: cancellationToken);
|
||||
}
|
||||
else if (message < this._targetNumber)
|
||||
{
|
||||
await context.SendMessageAsync(NumberSignal.Below, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(NumberSignal.Below, cancellationToken: cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.SendMessageAsync(NumberSignal.Above, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(NumberSignal.Above, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,5 +148,5 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ public static class Program
|
||||
private static async Task Main()
|
||||
{
|
||||
// Create the workflow
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync().ConfigureAwait(false);
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync();
|
||||
|
||||
// Create checkpoint manager
|
||||
var checkpointManager = CheckpointManager.Default;
|
||||
@@ -33,8 +33,8 @@ public static class Program
|
||||
// Execute the workflow and save checkpoints
|
||||
await using Checkpointed<StreamingRun> checkpointedRun = await InProcessExecution
|
||||
.StreamAsync(workflow, NumberSignal.Init, checkpointManager)
|
||||
.ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
;
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is ExecutorCompletedEvent executorCompletedEvt)
|
||||
{
|
||||
@@ -70,8 +70,8 @@ public static class Program
|
||||
Console.WriteLine($"\n\nRestoring from the {CheckpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex];
|
||||
// Note that we are restoring the state directly to the same run instance.
|
||||
await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is ExecutorCompletedEvent executorCompletedEvt)
|
||||
{
|
||||
|
||||
+12
-13
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
|
||||
namespace WorkflowCheckpointAndResumeSample;
|
||||
|
||||
@@ -42,7 +41,7 @@ internal enum NumberSignal
|
||||
/// <summary>
|
||||
/// Executor that makes a guess based on the current bounds.
|
||||
/// </summary>
|
||||
internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExecutor>("Guess"), IMessageHandler<NumberSignal>
|
||||
internal sealed class GuessNumberExecutor() : Executor<NumberSignal>("Guess")
|
||||
{
|
||||
/// <summary>
|
||||
/// The lower bound of the guessing range.
|
||||
@@ -69,20 +68,20 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
|
||||
private int NextGuess => (this.LowerBound + this.UpperBound) / 2;
|
||||
|
||||
public async ValueTask HandleAsync(NumberSignal message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(NumberSignal message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case NumberSignal.Init:
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken);
|
||||
break;
|
||||
case NumberSignal.Above:
|
||||
this.UpperBound = this.NextGuess - 1;
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken);
|
||||
break;
|
||||
case NumberSignal.Below:
|
||||
this.LowerBound = this.NextGuess + 1;
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -99,13 +98,13 @@ internal sealed class GuessNumberExecutor() : ReflectingExecutor<GuessNumberExec
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
(this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
(this.LowerBound, this.UpperBound) = await context.ReadStateAsync<(int, int)>(StateKey, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executor that judges the guess and provides feedback.
|
||||
/// </summary>
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
internal sealed class JudgeExecutor() : Executor<int>("Judge")
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries;
|
||||
@@ -120,20 +119,20 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
this._targetNumber = targetNumber;
|
||||
}
|
||||
|
||||
public async ValueTask HandleAsync(int message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(int message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this._tries++;
|
||||
if (message == this._targetNumber)
|
||||
{
|
||||
await context.YieldOutputAsync($"{this._targetNumber} found in {this._tries} tries!", cancellationToken).ConfigureAwait(false);
|
||||
await context.YieldOutputAsync($"{this._targetNumber} found in {this._tries} tries!", cancellationToken);
|
||||
}
|
||||
else if (message < this._targetNumber)
|
||||
{
|
||||
await context.SendMessageAsync(NumberSignal.Below, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(NumberSignal.Below, cancellationToken: cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.SendMessageAsync(NumberSignal.Above, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(NumberSignal.Above, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,5 +148,5 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
+7
-7
@@ -27,7 +27,7 @@ public static class Program
|
||||
private static async Task Main()
|
||||
{
|
||||
// Create the workflow
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync().ConfigureAwait(false);
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync();
|
||||
|
||||
// Create checkpoint manager
|
||||
var checkpointManager = CheckpointManager.Default;
|
||||
@@ -36,15 +36,15 @@ public static class Program
|
||||
// Execute the workflow and save checkpoints
|
||||
await using Checkpointed<StreamingRun> checkpointedRun = await InProcessExecution
|
||||
.StreamAsync(workflow, new SignalWithNumber(NumberSignal.Init), checkpointManager)
|
||||
.ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
;
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync())
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case RequestInfoEvent requestInputEvt:
|
||||
// Handle `RequestInfoEvent` from the workflow
|
||||
ExternalResponse response = HandleExternalRequest(requestInputEvt.Request);
|
||||
await checkpointedRun.Run.SendResponseAsync(response).ConfigureAwait(false);
|
||||
await checkpointedRun.Run.SendResponseAsync(response);
|
||||
break;
|
||||
case ExecutorCompletedEvent executorCompletedEvt:
|
||||
Console.WriteLine($"* Executor {executorCompletedEvt.ExecutorId} completed.");
|
||||
@@ -76,15 +76,15 @@ public static class Program
|
||||
Console.WriteLine($"\n\nRestoring from the {CheckpointIndex + 1}th checkpoint.");
|
||||
CheckpointInfo savedCheckpoint = checkpoints[CheckpointIndex];
|
||||
// Note that we are restoring the state directly to the same run instance.
|
||||
await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None).ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await checkpointedRun.RestoreCheckpointAsync(savedCheckpoint, CancellationToken.None);
|
||||
await foreach (WorkflowEvent evt in checkpointedRun.Run.WatchStreamAsync())
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case RequestInfoEvent requestInputEvt:
|
||||
// Handle `RequestInfoEvent` from the workflow
|
||||
ExternalResponse response = HandleExternalRequest(requestInputEvt.Request);
|
||||
await checkpointedRun.Run.SendResponseAsync(response).ConfigureAwait(false);
|
||||
await checkpointedRun.Run.SendResponseAsync(response);
|
||||
break;
|
||||
case ExecutorCompletedEvent executorCompletedEvt:
|
||||
Console.WriteLine($"* Executor {executorCompletedEvt.ExecutorId} completed.");
|
||||
|
||||
+6
-8
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
|
||||
namespace WorkflowCheckpointWithHumanInTheLoopSample;
|
||||
|
||||
@@ -54,7 +53,7 @@ internal sealed class SignalWithNumber
|
||||
/// <summary>
|
||||
/// Executor that judges the guess and provides feedback.
|
||||
/// </summary>
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
internal sealed class JudgeExecutor() : Executor<int>("Judge")
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries;
|
||||
@@ -69,21 +68,20 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
this._targetNumber = targetNumber;
|
||||
}
|
||||
|
||||
public async ValueTask HandleAsync(int message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(int message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this._tries++;
|
||||
if (message == this._targetNumber)
|
||||
{
|
||||
await context.YieldOutputAsync($"{this._targetNumber} found in {this._tries} tries!", cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
await context.YieldOutputAsync($"{this._targetNumber} found in {this._tries} tries!", cancellationToken);
|
||||
}
|
||||
else if (message < this._targetNumber)
|
||||
{
|
||||
await context.SendMessageAsync(new SignalWithNumber(NumberSignal.Below, message), cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(new SignalWithNumber(NumberSignal.Below, message), cancellationToken: cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.SendMessageAsync(new SignalWithNumber(NumberSignal.Above, message), cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(new SignalWithNumber(NumberSignal.Above, message), cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,5 +97,5 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
/// This must be overridden to restore any state that was saved during checkpointing.
|
||||
/// </summary>
|
||||
protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
this._tries = await context.ReadStateAsync<int>(StateKey, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace WorkflowConcurrentSample;
|
||||
@@ -60,7 +59,7 @@ public static class Program
|
||||
|
||||
// Execute the workflow in streaming mode
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, "What is temperature?");
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is WorkflowOutputEvent output)
|
||||
{
|
||||
@@ -74,8 +73,7 @@ public static class Program
|
||||
/// Executor that starts the concurrent processing by sending messages to the agents.
|
||||
/// </summary>
|
||||
internal sealed class ConcurrentStartExecutor() :
|
||||
ReflectingExecutor<ConcurrentStartExecutor>("ConcurrentStartExecutor"),
|
||||
IMessageHandler<string>
|
||||
Executor<string>("ConcurrentStartExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts the concurrent processing by sending messages to the agents.
|
||||
@@ -85,7 +83,7 @@ internal sealed class ConcurrentStartExecutor() :
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A task representing the asynchronous operation</returns>
|
||||
public async ValueTask HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Broadcast the message to all connected agents. Receiving agents will queue
|
||||
// the message but will not start processing until they receive a turn token.
|
||||
@@ -99,8 +97,7 @@ internal sealed class ConcurrentStartExecutor() :
|
||||
/// Executor that aggregates the results from the concurrent agents.
|
||||
/// </summary>
|
||||
internal sealed class ConcurrentAggregationExecutor() :
|
||||
ReflectingExecutor<ConcurrentAggregationExecutor>("ConcurrentAggregationExecutor"),
|
||||
IMessageHandler<ChatMessage>
|
||||
Executor<ChatMessage>("ConcurrentAggregationExecutor")
|
||||
{
|
||||
private readonly List<ChatMessage> _messages = [];
|
||||
|
||||
@@ -112,7 +109,7 @@ internal sealed class ConcurrentAggregationExecutor() :
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>A task representing the asynchronous operation</returns>
|
||||
public async ValueTask HandleAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this._messages.Add(message);
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
|
||||
namespace WorkflowMapReduceSample;
|
||||
|
||||
@@ -130,8 +129,7 @@ public static class Program
|
||||
/// Splits data into roughly equal chunks based on the number of mapper nodes.
|
||||
/// </summary>
|
||||
internal sealed class Split(string[] mapperIds, string id) :
|
||||
ReflectingExecutor<Split>(id),
|
||||
IMessageHandler<string>
|
||||
Executor<string>(id)
|
||||
{
|
||||
private readonly string[] _mapperIds = mapperIds;
|
||||
private static readonly string[] s_lineSeparators = ["\r\n", "\r", "\n"];
|
||||
@@ -139,7 +137,7 @@ internal sealed class Split(string[] mapperIds, string id) :
|
||||
/// <summary>
|
||||
/// Tokenize input and assign contiguous index ranges to each mapper via shared state.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Ensure temp directory exists
|
||||
Directory.CreateDirectory(MapReduceConstants.TempDir);
|
||||
@@ -188,12 +186,12 @@ internal sealed class Split(string[] mapperIds, string id) :
|
||||
/// <summary>
|
||||
/// Maps each token to a count of 1 and writes pairs to a per-mapper file.
|
||||
/// </summary>
|
||||
internal sealed class Mapper(string id) : ReflectingExecutor<Mapper>(id), IMessageHandler<SplitComplete>
|
||||
internal sealed class Mapper(string id) : Executor<SplitComplete>(id)
|
||||
{
|
||||
/// <summary>
|
||||
/// Read the assigned slice, emit (word, 1) pairs, and persist to disk.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(SplitComplete message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(SplitComplete message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var dataToProcess = await context.ReadStateAsync<string[]>(MapReduceConstants.DataToProcessKey, scopeName: MapReduceConstants.StateScope, cancellationToken);
|
||||
var chunk = await context.ReadStateAsync<(int start, int end)>(this.Id, scopeName: MapReduceConstants.StateScope, cancellationToken);
|
||||
@@ -215,8 +213,7 @@ internal sealed class Mapper(string id) : ReflectingExecutor<Mapper>(id), IMessa
|
||||
/// Groups intermediate pairs by key and partitions them across reducers.
|
||||
/// </summary>
|
||||
internal sealed class Shuffler(string[] reducerIds, string[] mapperIds, string id) :
|
||||
ReflectingExecutor<Shuffler>(id),
|
||||
IMessageHandler<MapComplete>
|
||||
Executor<MapComplete>(id)
|
||||
{
|
||||
private readonly string[] _reducerIds = reducerIds;
|
||||
private readonly string[] _mapperIds = mapperIds;
|
||||
@@ -225,7 +222,7 @@ internal sealed class Shuffler(string[] reducerIds, string[] mapperIds, string i
|
||||
/// <summary>
|
||||
/// Aggregate mapper outputs and write one partition file per reducer.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(MapComplete message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(MapComplete message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this._mapResults.Add(message);
|
||||
|
||||
@@ -314,12 +311,12 @@ internal sealed class Shuffler(string[] reducerIds, string[] mapperIds, string i
|
||||
/// <summary>
|
||||
/// Sums grouped counts per key for its assigned partition.
|
||||
/// </summary>
|
||||
internal sealed class Reducer(string id) : ReflectingExecutor<Reducer>(id), IMessageHandler<ShuffleComplete>
|
||||
internal sealed class Reducer(string id) : Executor<ShuffleComplete>(id)
|
||||
{
|
||||
/// <summary>
|
||||
/// Read one shuffle partition and reduce it to totals.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(ShuffleComplete message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(ShuffleComplete message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (message.ReducerId != this.Id)
|
||||
{
|
||||
@@ -356,13 +353,12 @@ internal sealed class Reducer(string id) : ReflectingExecutor<Reducer>(id), IMes
|
||||
/// Joins all reducer outputs and yields the final output.
|
||||
/// </summary>
|
||||
internal sealed class CompletionExecutor(string id) :
|
||||
ReflectingExecutor<CompletionExecutor>(id),
|
||||
IMessageHandler<List<ReduceComplete>>
|
||||
Executor<List<ReduceComplete>>(id)
|
||||
{
|
||||
/// <summary>
|
||||
/// Collect reducer output file paths and yield final output.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(List<ReduceComplete> message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(List<ReduceComplete> message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var filePaths = message.ConvertAll(r => r.FilePath);
|
||||
await context.YieldOutputAsync(filePaths, cancellationToken);
|
||||
|
||||
+9
-10
@@ -6,7 +6,6 @@ using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace WorkflowEdgeConditionSample;
|
||||
@@ -64,7 +63,7 @@ public static class Program
|
||||
// Execute the workflow
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, email));
|
||||
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is WorkflowOutputEvent outputEvent)
|
||||
{
|
||||
@@ -147,7 +146,7 @@ internal sealed class Email
|
||||
/// <summary>
|
||||
/// Executor that detects spam using an AI agent.
|
||||
/// </summary>
|
||||
internal sealed class SpamDetectionExecutor : ReflectingExecutor<SpamDetectionExecutor>, IMessageHandler<ChatMessage, DetectionResult>
|
||||
internal sealed class SpamDetectionExecutor : Executor<ChatMessage, DetectionResult>
|
||||
{
|
||||
private readonly AIAgent _spamDetectionAgent;
|
||||
|
||||
@@ -160,7 +159,7 @@ internal sealed class SpamDetectionExecutor : ReflectingExecutor<SpamDetectionEx
|
||||
this._spamDetectionAgent = spamDetectionAgent;
|
||||
}
|
||||
|
||||
public async ValueTask<DetectionResult> HandleAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<DetectionResult> HandleAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Generate a random email ID and store the email content to the shared state
|
||||
var newEmail = new Email
|
||||
@@ -192,7 +191,7 @@ public sealed class EmailResponse
|
||||
/// <summary>
|
||||
/// Executor that assists with email responses using an AI agent.
|
||||
/// </summary>
|
||||
internal sealed class EmailAssistantExecutor : ReflectingExecutor<EmailAssistantExecutor>, IMessageHandler<DetectionResult, EmailResponse>
|
||||
internal sealed class EmailAssistantExecutor : Executor<DetectionResult, EmailResponse>
|
||||
{
|
||||
private readonly AIAgent _emailAssistantAgent;
|
||||
|
||||
@@ -205,7 +204,7 @@ internal sealed class EmailAssistantExecutor : ReflectingExecutor<EmailAssistant
|
||||
this._emailAssistantAgent = emailAssistantAgent;
|
||||
}
|
||||
|
||||
public async ValueTask<EmailResponse> HandleAsync(DetectionResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<EmailResponse> HandleAsync(DetectionResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (message.IsSpam)
|
||||
{
|
||||
@@ -227,24 +226,24 @@ internal sealed class EmailAssistantExecutor : ReflectingExecutor<EmailAssistant
|
||||
/// <summary>
|
||||
/// Executor that sends emails.
|
||||
/// </summary>
|
||||
internal sealed class SendEmailExecutor() : ReflectingExecutor<SendEmailExecutor>("SendEmailExecutor"), IMessageHandler<EmailResponse>
|
||||
internal sealed class SendEmailExecutor() : Executor<EmailResponse>("SendEmailExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulate the sending of an email.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
public override async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
await context.YieldOutputAsync($"Email sent: {message.Response}", cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executor that handles spam messages.
|
||||
/// </summary>
|
||||
internal sealed class HandleSpamExecutor() : ReflectingExecutor<HandleSpamExecutor>("HandleSpamExecutor"), IMessageHandler<DetectionResult>
|
||||
internal sealed class HandleSpamExecutor() : Executor<DetectionResult>("HandleSpamExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulate the handling of a spam message.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(DetectionResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(DetectionResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (message.IsSpam)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,6 @@ using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace WorkflowSwitchCaseSample;
|
||||
@@ -80,7 +79,7 @@ public static class Program
|
||||
// Execute the workflow
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, email));
|
||||
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is WorkflowOutputEvent outputEvent)
|
||||
{
|
||||
@@ -172,7 +171,7 @@ internal sealed class Email
|
||||
/// <summary>
|
||||
/// Executor that detects spam using an AI agent.
|
||||
/// </summary>
|
||||
internal sealed class SpamDetectionExecutor : ReflectingExecutor<SpamDetectionExecutor>, IMessageHandler<ChatMessage, DetectionResult>
|
||||
internal sealed class SpamDetectionExecutor : Executor<ChatMessage, DetectionResult>
|
||||
{
|
||||
private readonly AIAgent _spamDetectionAgent;
|
||||
|
||||
@@ -185,7 +184,7 @@ internal sealed class SpamDetectionExecutor : ReflectingExecutor<SpamDetectionEx
|
||||
this._spamDetectionAgent = spamDetectionAgent;
|
||||
}
|
||||
|
||||
public async ValueTask<DetectionResult> HandleAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<DetectionResult> HandleAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Generate a random email ID and store the email content
|
||||
var newEmail = new Email
|
||||
@@ -217,7 +216,7 @@ public sealed class EmailResponse
|
||||
/// <summary>
|
||||
/// Executor that assists with email responses using an AI agent.
|
||||
/// </summary>
|
||||
internal sealed class EmailAssistantExecutor : ReflectingExecutor<EmailAssistantExecutor>, IMessageHandler<DetectionResult, EmailResponse>
|
||||
internal sealed class EmailAssistantExecutor : Executor<DetectionResult, EmailResponse>
|
||||
{
|
||||
private readonly AIAgent _emailAssistantAgent;
|
||||
|
||||
@@ -230,7 +229,7 @@ internal sealed class EmailAssistantExecutor : ReflectingExecutor<EmailAssistant
|
||||
this._emailAssistantAgent = emailAssistantAgent;
|
||||
}
|
||||
|
||||
public async ValueTask<EmailResponse> HandleAsync(DetectionResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<EmailResponse> HandleAsync(DetectionResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (message.spamDecision == SpamDecision.Spam)
|
||||
{
|
||||
@@ -251,28 +250,28 @@ internal sealed class EmailAssistantExecutor : ReflectingExecutor<EmailAssistant
|
||||
/// <summary>
|
||||
/// Executor that sends emails.
|
||||
/// </summary>
|
||||
internal sealed class SendEmailExecutor() : ReflectingExecutor<SendEmailExecutor>("SendEmailExecutor"), IMessageHandler<EmailResponse>
|
||||
internal sealed class SendEmailExecutor() : Executor<EmailResponse>("SendEmailExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulate the sending of an email.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
await context.YieldOutputAsync($"Email sent: {message.Response}", cancellationToken).ConfigureAwait(false);
|
||||
public override async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
await context.YieldOutputAsync($"Email sent: {message.Response}", cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executor that handles spam messages.
|
||||
/// </summary>
|
||||
internal sealed class HandleSpamExecutor() : ReflectingExecutor<HandleSpamExecutor>("HandleSpamExecutor"), IMessageHandler<DetectionResult>
|
||||
internal sealed class HandleSpamExecutor() : Executor<DetectionResult>("HandleSpamExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulate the handling of a spam message.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(DetectionResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(DetectionResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (message.spamDecision == SpamDecision.Spam)
|
||||
{
|
||||
await context.YieldOutputAsync($"Email marked as spam: {message.Reason}", cancellationToken).ConfigureAwait(false);
|
||||
await context.YieldOutputAsync($"Email marked as spam: {message.Reason}", cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -284,12 +283,12 @@ internal sealed class HandleSpamExecutor() : ReflectingExecutor<HandleSpamExecut
|
||||
/// <summary>
|
||||
/// Executor that handles uncertain emails.
|
||||
/// </summary>
|
||||
internal sealed class HandleUncertainExecutor() : ReflectingExecutor<HandleUncertainExecutor>("HandleUncertainExecutor"), IMessageHandler<DetectionResult>
|
||||
internal sealed class HandleUncertainExecutor() : Executor<DetectionResult>("HandleUncertainExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulate the handling of an uncertain spam decision.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(DetectionResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(DetectionResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (message.spamDecision == SpamDecision.Uncertain)
|
||||
{
|
||||
|
||||
+15
-16
@@ -6,7 +6,6 @@ using Azure.AI.OpenAI;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace WorkflowMultiSelectionSample;
|
||||
@@ -88,7 +87,7 @@ public static class Program
|
||||
// Execute the workflow
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, new ChatMessage(ChatRole.User, email));
|
||||
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is WorkflowOutputEvent outputEvent)
|
||||
{
|
||||
@@ -228,7 +227,7 @@ internal sealed class Email
|
||||
/// <summary>
|
||||
/// Executor that analyzes emails using an AI agent.
|
||||
/// </summary>
|
||||
internal sealed class EmailAnalysisExecutor : ReflectingExecutor<EmailAnalysisExecutor>, IMessageHandler<ChatMessage, AnalysisResult>
|
||||
internal sealed class EmailAnalysisExecutor : Executor<ChatMessage, AnalysisResult>
|
||||
{
|
||||
private readonly AIAgent _emailAnalysisAgent;
|
||||
|
||||
@@ -241,7 +240,7 @@ internal sealed class EmailAnalysisExecutor : ReflectingExecutor<EmailAnalysisEx
|
||||
this._emailAnalysisAgent = emailAnalysisAgent;
|
||||
}
|
||||
|
||||
public async ValueTask<AnalysisResult> HandleAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<AnalysisResult> HandleAsync(ChatMessage message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Generate a random email ID and store the email content
|
||||
var newEmail = new Email
|
||||
@@ -274,7 +273,7 @@ public sealed class EmailResponse
|
||||
/// <summary>
|
||||
/// Executor that assists with email responses using an AI agent.
|
||||
/// </summary>
|
||||
internal sealed class EmailAssistantExecutor : ReflectingExecutor<EmailAssistantExecutor>, IMessageHandler<AnalysisResult, EmailResponse>
|
||||
internal sealed class EmailAssistantExecutor : Executor<AnalysisResult, EmailResponse>
|
||||
{
|
||||
private readonly AIAgent _emailAssistantAgent;
|
||||
|
||||
@@ -287,7 +286,7 @@ internal sealed class EmailAssistantExecutor : ReflectingExecutor<EmailAssistant
|
||||
this._emailAssistantAgent = emailAssistantAgent;
|
||||
}
|
||||
|
||||
public async ValueTask<EmailResponse> HandleAsync(AnalysisResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<EmailResponse> HandleAsync(AnalysisResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (message.spamDecision == SpamDecision.Spam)
|
||||
{
|
||||
@@ -308,24 +307,24 @@ internal sealed class EmailAssistantExecutor : ReflectingExecutor<EmailAssistant
|
||||
/// <summary>
|
||||
/// Executor that sends emails.
|
||||
/// </summary>
|
||||
internal sealed class SendEmailExecutor() : ReflectingExecutor<SendEmailExecutor>("SendEmailExecutor"), IMessageHandler<EmailResponse>
|
||||
internal sealed class SendEmailExecutor() : Executor<EmailResponse>("SendEmailExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulate the sending of an email.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
public override async ValueTask HandleAsync(EmailResponse message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
await context.YieldOutputAsync($"Email sent: {message.Response}", cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executor that handles spam messages.
|
||||
/// </summary>
|
||||
internal sealed class HandleSpamExecutor() : ReflectingExecutor<HandleSpamExecutor>("HandleSpamExecutor"), IMessageHandler<AnalysisResult>
|
||||
internal sealed class HandleSpamExecutor() : Executor<AnalysisResult>("HandleSpamExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulate the handling of a spam message.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(AnalysisResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(AnalysisResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (message.spamDecision == SpamDecision.Spam)
|
||||
{
|
||||
@@ -341,12 +340,12 @@ internal sealed class HandleSpamExecutor() : ReflectingExecutor<HandleSpamExecut
|
||||
/// <summary>
|
||||
/// Executor that handles uncertain messages.
|
||||
/// </summary>
|
||||
internal sealed class HandleUncertainExecutor() : ReflectingExecutor<HandleUncertainExecutor>("HandleUncertainExecutor"), IMessageHandler<AnalysisResult>
|
||||
internal sealed class HandleUncertainExecutor() : Executor<AnalysisResult>("HandleUncertainExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Simulate the handling of an uncertain spam decision.
|
||||
/// </summary>
|
||||
public async ValueTask HandleAsync(AnalysisResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(AnalysisResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (message.spamDecision == SpamDecision.Uncertain)
|
||||
{
|
||||
@@ -372,7 +371,7 @@ public sealed class EmailSummary
|
||||
/// <summary>
|
||||
/// Executor that summarizes emails using an AI agent.
|
||||
/// </summary>
|
||||
internal sealed class EmailSummaryExecutor : ReflectingExecutor<EmailSummaryExecutor>, IMessageHandler<AnalysisResult, AnalysisResult>
|
||||
internal sealed class EmailSummaryExecutor : Executor<AnalysisResult, AnalysisResult>
|
||||
{
|
||||
private readonly AIAgent _emailSummaryAgent;
|
||||
|
||||
@@ -385,7 +384,7 @@ internal sealed class EmailSummaryExecutor : ReflectingExecutor<EmailSummaryExec
|
||||
this._emailSummaryAgent = emailSummaryAgent;
|
||||
}
|
||||
|
||||
public async ValueTask<AnalysisResult> HandleAsync(AnalysisResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<AnalysisResult> HandleAsync(AnalysisResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Read the email content from the shared states
|
||||
var email = await context.ReadStateAsync<Email>(message.EmailId, scopeName: EmailStateConstants.EmailStateScope, cancellationToken);
|
||||
@@ -408,9 +407,9 @@ internal sealed class DatabaseEvent(string message) : WorkflowEvent(message) { }
|
||||
/// <summary>
|
||||
/// Executor that handles database access.
|
||||
/// </summary>
|
||||
internal sealed class DatabaseAccessExecutor() : ReflectingExecutor<DatabaseAccessExecutor>("DatabaseAccessExecutor"), IMessageHandler<AnalysisResult>
|
||||
internal sealed class DatabaseAccessExecutor() : Executor<AnalysisResult>("DatabaseAccessExecutor")
|
||||
{
|
||||
public async ValueTask HandleAsync(AnalysisResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(AnalysisResult message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. Save the email content
|
||||
await context.ReadStateAsync<Email>(message.EmailId, scopeName: EmailStateConstants.EmailStateScope, cancellationToken);
|
||||
|
||||
@@ -52,22 +52,22 @@ public static class TestWorkflowProvider
|
||||
"FOUNDRY_AGENT_RESEARCHWEATHER").ConfigureAwait(false);
|
||||
|
||||
// Initialize variables
|
||||
await context.QueueStateUpdateAsync("AgentResponse", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("AgentResponseText", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("AvailableAgents", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("FinalResponse", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("InputTask", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("InternalConversationId", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("NextSpeaker", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("Plan", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("ProgressLedgerUpdate", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("RestartCount", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("SeedTask", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("StallCount", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("TaskFacts", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("TaskInstructions", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("TeamDescription", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("TypedProgressLedger", UnassignedValue.Instance, "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync("AgentResponse", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("AgentResponseText", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("AvailableAgents", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("FinalResponse", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("InputTask", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("InternalConversationId", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("NextSpeaker", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("Plan", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("ProgressLedgerUpdate", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("RestartCount", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("SeedTask", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("StallCount", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("TaskFacts", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("TaskInstructions", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("TeamDescription", UnassignedValue.Instance, "Local");
|
||||
await context.QueueStateUpdateAsync("TypedProgressLedger", UnassignedValue.Instance, "Local");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +97,8 @@ public static class TestWorkflowProvider
|
||||
agentid: Env.FOUNDRY_AGENT_RESEARCHWEB
|
||||
}
|
||||
]
|
||||
""").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "AvailableAgents", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
""");
|
||||
await context.QueueStateUpdateAsync(key: "AvailableAgents", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -115,8 +115,8 @@ public static class TestWorkflowProvider
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("""
|
||||
Concat(ForAll(Local.AvailableAgents, $"- " & name & $": " & description), Value, "
|
||||
")
|
||||
""").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "TeamDescription", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
""");
|
||||
await context.QueueStateUpdateAsync(key: "TeamDescription", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -130,8 +130,8 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("System.LastMessage.Text").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "InputTask", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("System.LastMessage.Text");
|
||||
await context.QueueStateUpdateAsync(key: "InputTask", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -145,8 +145,8 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("UserMessage(Local.InputTask)").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "SeedTask", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("UserMessage(Local.InputTask)");
|
||||
await context.QueueStateUpdateAsync(key: "SeedTask", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -167,7 +167,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -180,8 +180,8 @@ public static class TestWorkflowProvider
|
||||
{
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
string conversationId = await agentProvider.CreateConversationAsync(cancellationToken).ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "InternalConversationId", value: conversationId, scopeName: "Local").ConfigureAwait(false);
|
||||
string conversationId = await agentProvider.CreateConversationAsync(cancellationToken);
|
||||
await context.QueueStateUpdateAsync(key: "InternalConversationId", value: conversationId, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -195,14 +195,14 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHANALYST", scopeName: "Env").ConfigureAwait(false);
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHANALYST", scopeName: "Env");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(agentName))
|
||||
{
|
||||
throw new InvalidOperationException($"Agent name must be defined: {this.Id}");
|
||||
}
|
||||
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "InternalConversationId", scopeName: "Local").ConfigureAwait(false);
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "InternalConversationId", scopeName: "Local");
|
||||
bool autoSend = true;
|
||||
string additionalInstructions =
|
||||
await context.FormatTemplateAsync(
|
||||
@@ -226,7 +226,7 @@ public static class TestWorkflowProvider
|
||||
|
||||
DO NOT include any other headings or sections in your response. DO NOT list next steps or plans until asked to do so.
|
||||
""");
|
||||
IList<ChatMessage>? inputMessages = await context.EvaluateListAsync<ChatMessage>("UserMessage(Local.InputTask)").ConfigureAwait(false);
|
||||
IList<ChatMessage>? inputMessages = await context.EvaluateListAsync<ChatMessage>("UserMessage(Local.InputTask)");
|
||||
|
||||
AgentRunResponse agentResponse =
|
||||
await InvokeAgentAsync(
|
||||
@@ -236,14 +236,14 @@ public static class TestWorkflowProvider
|
||||
autoSend,
|
||||
additionalInstructions,
|
||||
inputMessages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
cancellationToken);
|
||||
|
||||
if (autoSend)
|
||||
{
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse));
|
||||
}
|
||||
|
||||
await context.QueueStateUpdateAsync(key: "TaskFacts", value: agentResponse.Messages, scopeName: "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "TaskFacts", value: agentResponse.Messages, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -264,7 +264,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -278,14 +278,14 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHMANAGER", scopeName: "Env").ConfigureAwait(false);
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHMANAGER", scopeName: "Env");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(agentName))
|
||||
{
|
||||
throw new InvalidOperationException($"Agent name must be defined: {this.Id}");
|
||||
}
|
||||
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "InternalConversationId", scopeName: "Local").ConfigureAwait(false);
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "InternalConversationId", scopeName: "Local");
|
||||
bool autoSend = true;
|
||||
string additionalInstructions =
|
||||
await context.FormatTemplateAsync(
|
||||
@@ -300,7 +300,7 @@ public static class TestWorkflowProvider
|
||||
|
||||
Remember, there is no requirement to involve the entire team -- only select team member's whose particular expertise is required for this task.
|
||||
""");
|
||||
IList<ChatMessage>? inputMessages = await context.EvaluateListAsync<ChatMessage>("UserMessage(Local.InputTask)").ConfigureAwait(false);
|
||||
IList<ChatMessage>? inputMessages = await context.EvaluateListAsync<ChatMessage>("UserMessage(Local.InputTask)");
|
||||
|
||||
AgentRunResponse agentResponse =
|
||||
await InvokeAgentAsync(
|
||||
@@ -310,14 +310,14 @@ public static class TestWorkflowProvider
|
||||
autoSend,
|
||||
additionalInstructions,
|
||||
inputMessages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
cancellationToken);
|
||||
|
||||
if (autoSend)
|
||||
{
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse));
|
||||
}
|
||||
|
||||
await context.QueueStateUpdateAsync(key: "Plan", value: agentResponse.Messages, scopeName: "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "Plan", value: agentResponse.Messages, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -354,8 +354,8 @@ public static class TestWorkflowProvider
|
||||
Here is the plan to follow as best as possible:
|
||||
|
||||
" & Last(Local.Plan).Text
|
||||
""").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "TaskInstructions", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
""");
|
||||
await context.QueueStateUpdateAsync(key: "TaskInstructions", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -376,7 +376,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -390,14 +390,14 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHMANAGER", scopeName: "Env").ConfigureAwait(false);
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHMANAGER", scopeName: "Env");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(agentName))
|
||||
{
|
||||
throw new InvalidOperationException($"Agent name must be defined: {this.Id}");
|
||||
}
|
||||
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "InternalConversationId", scopeName: "Local").ConfigureAwait(false);
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "InternalConversationId", scopeName: "Local");
|
||||
bool autoSend = true;
|
||||
string additionalInstructions =
|
||||
await context.FormatTemplateAsync(
|
||||
@@ -443,7 +443,7 @@ public static class TestWorkflowProvider
|
||||
}}
|
||||
}}
|
||||
""");
|
||||
IList<ChatMessage>? inputMessages = await context.EvaluateListAsync<ChatMessage>("UserMessage(Local.AgentResponseText)").ConfigureAwait(false);
|
||||
IList<ChatMessage>? inputMessages = await context.EvaluateListAsync<ChatMessage>("UserMessage(Local.AgentResponseText)");
|
||||
|
||||
AgentRunResponse agentResponse =
|
||||
await InvokeAgentAsync(
|
||||
@@ -453,14 +453,14 @@ public static class TestWorkflowProvider
|
||||
autoSend,
|
||||
additionalInstructions,
|
||||
inputMessages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
cancellationToken);
|
||||
|
||||
if (autoSend)
|
||||
{
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse));
|
||||
}
|
||||
|
||||
await context.QueueStateUpdateAsync(key: "ProgressLedgerUpdate", value: agentResponse.Messages, scopeName: "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "ProgressLedgerUpdate", value: agentResponse.Messages, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -496,8 +496,8 @@ public static class TestWorkflowProvider
|
||||
VariableType.Record(
|
||||
("reason", typeof(string)),
|
||||
("answer", typeof(string)))));
|
||||
object? parsedValue = await context.ConvertValueAsync(targetType, "Last(Local.ProgressLedgerUpdate).Text", cancellationToken).ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "TypedProgressLedger", value: parsedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
object? parsedValue = await context.ConvertValueAsync(targetType, "Last(Local.ProgressLedgerUpdate).Text", cancellationToken);
|
||||
await context.QueueStateUpdateAsync(key: "TypedProgressLedger", value: parsedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -511,13 +511,13 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
bool condition0 = await context.EvaluateValueAsync<bool>("Local.TypedProgressLedger.is_request_satisfied.answer").ConfigureAwait(false);
|
||||
bool condition0 = await context.EvaluateValueAsync<bool>("Local.TypedProgressLedger.is_request_satisfied.answer");
|
||||
if (condition0)
|
||||
{
|
||||
return "conditionItem_fj432c";
|
||||
}
|
||||
|
||||
bool condition1 = await context.EvaluateValueAsync<bool>("Local.TypedProgressLedger.is_in_loop.answer || Not(Local.TypedProgressLedger.is_progress_being_made.answer)").ConfigureAwait(false);
|
||||
bool condition1 = await context.EvaluateValueAsync<bool>("Local.TypedProgressLedger.is_in_loop.answer || Not(Local.TypedProgressLedger.is_progress_being_made.answer)");
|
||||
if (condition1)
|
||||
{
|
||||
return "conditionItem_yiqund";
|
||||
@@ -542,7 +542,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -556,14 +556,14 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHMANAGER", scopeName: "Env").ConfigureAwait(false);
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHMANAGER", scopeName: "Env");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(agentName))
|
||||
{
|
||||
throw new InvalidOperationException($"Agent name must be defined: {this.Id}");
|
||||
}
|
||||
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "ConversationId", scopeName: "System").ConfigureAwait(false);
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "ConversationId", scopeName: "System");
|
||||
bool autoSend = true;
|
||||
string additionalInstructions =
|
||||
await context.FormatTemplateAsync(
|
||||
@@ -572,7 +572,7 @@ public static class TestWorkflowProvider
|
||||
Based only on the conversation and without adding any new information, synthesize the result of the conversation as a complete response to the user task.
|
||||
The user will only every see this last response and not the entire conversation, so please ensure it is complete and self-contained.
|
||||
""");
|
||||
IList<ChatMessage>? inputMessages = await context.ReadListAsync<ChatMessage>(key: "SeedTask", scopeName: "Local").ConfigureAwait(false);
|
||||
IList<ChatMessage>? inputMessages = await context.ReadListAsync<ChatMessage>(key: "SeedTask", scopeName: "Local");
|
||||
|
||||
AgentRunResponse agentResponse =
|
||||
await InvokeAgentAsync(
|
||||
@@ -582,14 +582,14 @@ public static class TestWorkflowProvider
|
||||
autoSend,
|
||||
additionalInstructions,
|
||||
inputMessages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
cancellationToken);
|
||||
|
||||
if (autoSend)
|
||||
{
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse));
|
||||
}
|
||||
|
||||
await context.QueueStateUpdateAsync(key: "FinalResponse", value: agentResponse.Messages, scopeName: "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "FinalResponse", value: agentResponse.Messages, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -603,8 +603,8 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("Local.StallCount + 1").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "StallCount", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("Local.StallCount + 1");
|
||||
await context.QueueStateUpdateAsync(key: "StallCount", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -618,13 +618,13 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
bool condition0 = await context.EvaluateValueAsync<bool>(".TypedProgressLedger.is_in_loop.answer").ConfigureAwait(false);
|
||||
bool condition0 = await context.EvaluateValueAsync<bool>(".TypedProgressLedger.is_in_loop.answer");
|
||||
if (condition0)
|
||||
{
|
||||
return "conditionItem_fpaNL9";
|
||||
}
|
||||
|
||||
bool condition1 = await context.EvaluateValueAsync<bool>("Not(Local.TypedProgressLedger.is_progress_being_made.answer)").ConfigureAwait(false);
|
||||
bool condition1 = await context.EvaluateValueAsync<bool>("Not(Local.TypedProgressLedger.is_progress_being_made.answer)");
|
||||
if (condition1)
|
||||
{
|
||||
return "conditionItem_NnqvXh";
|
||||
@@ -649,7 +649,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -670,7 +670,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -684,7 +684,7 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
bool condition0 = await context.EvaluateValueAsync<bool>("Local.StallCount > 2").ConfigureAwait(false);
|
||||
bool condition0 = await context.EvaluateValueAsync<bool>("Local.StallCount > 2");
|
||||
if (condition0)
|
||||
{
|
||||
return "conditionItem_NlQTBv";
|
||||
@@ -709,7 +709,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -723,7 +723,7 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
bool condition0 = await context.EvaluateValueAsync<bool>("Local.RestartCount > 2").ConfigureAwait(false);
|
||||
bool condition0 = await context.EvaluateValueAsync<bool>("Local.RestartCount > 2");
|
||||
if (condition0)
|
||||
{
|
||||
return "conditionItem_EXAlhZ";
|
||||
@@ -748,7 +748,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -769,7 +769,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -783,14 +783,14 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHANALYST", scopeName: "Env").ConfigureAwait(false);
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHANALYST", scopeName: "Env");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(agentName))
|
||||
{
|
||||
throw new InvalidOperationException($"Agent name must be defined: {this.Id}");
|
||||
}
|
||||
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "InternalConversationId", scopeName: "Local").ConfigureAwait(false);
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "InternalConversationId", scopeName: "Local");
|
||||
bool autoSend = true;
|
||||
string additionalInstructions =
|
||||
await context.FormatTemplateAsync(
|
||||
@@ -810,7 +810,7 @@ public static class TestWorkflowProvider
|
||||
"As a reminder, we are working to solve the following task:
|
||||
|
||||
" & Local.InputTask)
|
||||
""").ConfigureAwait(false);
|
||||
""");
|
||||
|
||||
AgentRunResponse agentResponse =
|
||||
await InvokeAgentAsync(
|
||||
@@ -820,14 +820,14 @@ public static class TestWorkflowProvider
|
||||
autoSend,
|
||||
additionalInstructions,
|
||||
inputMessages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
cancellationToken);
|
||||
|
||||
if (autoSend)
|
||||
{
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse));
|
||||
}
|
||||
|
||||
await context.QueueStateUpdateAsync(key: "TaskFacts", value: agentResponse.Messages, scopeName: "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "TaskFacts", value: agentResponse.Messages, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -848,7 +848,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -862,14 +862,14 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHMANAGER", scopeName: "Env").ConfigureAwait(false);
|
||||
string? agentName = await context.ReadStateAsync<string>(key: "FOUNDRY_AGENT_RESEARCHMANAGER", scopeName: "Env");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(agentName))
|
||||
{
|
||||
throw new InvalidOperationException($"Agent name must be defined: {this.Id}");
|
||||
}
|
||||
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "InternalConversationId", scopeName: "Local").ConfigureAwait(false);
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "InternalConversationId", scopeName: "Local");
|
||||
bool autoSend = true;
|
||||
string additionalInstructions =
|
||||
await context.FormatTemplateAsync(
|
||||
@@ -891,14 +891,14 @@ public static class TestWorkflowProvider
|
||||
autoSend,
|
||||
additionalInstructions,
|
||||
inputMessages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
cancellationToken);
|
||||
|
||||
if (autoSend)
|
||||
{
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse));
|
||||
}
|
||||
|
||||
await context.QueueStateUpdateAsync(key: "Plan", value: agentResponse.Messages, scopeName: "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "Plan", value: agentResponse.Messages, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -935,8 +935,8 @@ public static class TestWorkflowProvider
|
||||
Here is the plan to follow as best as possible:
|
||||
|
||||
" & Local.Plan.Text
|
||||
""").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "TaskInstructions", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
""");
|
||||
await context.QueueStateUpdateAsync(key: "TaskInstructions", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -951,7 +951,7 @@ public static class TestWorkflowProvider
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
object? evaluatedValue = 0;
|
||||
await context.QueueStateUpdateAsync(key: "StallCount", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "StallCount", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -965,8 +965,8 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("Local.RestartCount + 1").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "RestartCount", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("Local.RestartCount + 1");
|
||||
await context.QueueStateUpdateAsync(key: "RestartCount", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -989,7 +989,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -1004,7 +1004,7 @@ public static class TestWorkflowProvider
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
object? evaluatedValue = 0;
|
||||
await context.QueueStateUpdateAsync(key: "StallCount", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "StallCount", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -1018,8 +1018,8 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("Search(Local.AvailableAgents, Local.TypedProgressLedger.next_speaker.answer, name)").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "NextSpeaker", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("Search(Local.AvailableAgents, Local.TypedProgressLedger.next_speaker.answer, name)");
|
||||
await context.QueueStateUpdateAsync(key: "NextSpeaker", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -1033,7 +1033,7 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
bool condition0 = await context.EvaluateValueAsync<bool>("CountRows(Local.NextSpeaker) = 1").ConfigureAwait(false);
|
||||
bool condition0 = await context.EvaluateValueAsync<bool>("CountRows(Local.NextSpeaker) = 1");
|
||||
if (condition0)
|
||||
{
|
||||
return "conditionItem_GmigcU";
|
||||
@@ -1051,21 +1051,21 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
string? agentName = await context.EvaluateValueAsync<string>("First(Local.NextSpeaker).agentid").ConfigureAwait(false);
|
||||
string? agentName = await context.EvaluateValueAsync<string>("First(Local.NextSpeaker).agentid");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(agentName))
|
||||
{
|
||||
throw new InvalidOperationException($"Agent name must be defined: {this.Id}");
|
||||
}
|
||||
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "ConversationId", scopeName: "System").ConfigureAwait(false);
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "ConversationId", scopeName: "System");
|
||||
bool autoSend = true;
|
||||
string additionalInstructions =
|
||||
await context.FormatTemplateAsync(
|
||||
"""
|
||||
{Local.TypedProgressLedger.instruction_or_question.answer}
|
||||
""");
|
||||
IList<ChatMessage>? inputMessages = await context.ReadListAsync<ChatMessage>(key: "SeedTask", scopeName: "Local").ConfigureAwait(false);
|
||||
IList<ChatMessage>? inputMessages = await context.ReadListAsync<ChatMessage>(key: "SeedTask", scopeName: "Local");
|
||||
|
||||
AgentRunResponse agentResponse =
|
||||
await InvokeAgentAsync(
|
||||
@@ -1075,14 +1075,14 @@ public static class TestWorkflowProvider
|
||||
autoSend,
|
||||
additionalInstructions,
|
||||
inputMessages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
cancellationToken);
|
||||
|
||||
if (autoSend)
|
||||
{
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse));
|
||||
}
|
||||
|
||||
await context.QueueStateUpdateAsync(key: "AgentResponse", value: agentResponse.Messages, scopeName: "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "AgentResponse", value: agentResponse.Messages, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -1096,8 +1096,8 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("Last(Local.AgentResponse).Text").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "AgentResponseText", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("Last(Local.AgentResponse).Text");
|
||||
await context.QueueStateUpdateAsync(key: "AgentResponseText", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -1111,7 +1111,7 @@ public static class TestWorkflowProvider
|
||||
{
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
await context.QueueStateUpdateAsync(key: "SeedTask", value: UnassignedValue.Instance, scopeName: "Local").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "SeedTask", value: UnassignedValue.Instance, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -1132,7 +1132,7 @@ public static class TestWorkflowProvider
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response));
|
||||
|
||||
return default;
|
||||
}
|
||||
@@ -1146,8 +1146,8 @@ public static class TestWorkflowProvider
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("Local.StallCount + 1").ConfigureAwait(false);
|
||||
await context.QueueStateUpdateAsync(key: "StallCount", value: evaluatedValue, scopeName: "Local").ConfigureAwait(false);
|
||||
object? evaluatedValue = await context.EvaluateValueAsync<object>("Local.StallCount + 1");
|
||||
await context.QueueStateUpdateAsync(key: "StallCount", value: evaluatedValue, scopeName: "Local");
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ internal sealed class Program
|
||||
|
||||
string? messageId = null;
|
||||
|
||||
await foreach (WorkflowEvent workflowEvent in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent workflowEvent in run.WatchStreamAsync())
|
||||
{
|
||||
switch (workflowEvent)
|
||||
{
|
||||
|
||||
@@ -150,7 +150,7 @@ internal sealed class Program
|
||||
|
||||
string? messageId = null;
|
||||
|
||||
await foreach (WorkflowEvent workflowEvent in run.Run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent workflowEvent in run.Run.WatchStreamAsync())
|
||||
{
|
||||
switch (workflowEvent)
|
||||
{
|
||||
@@ -187,12 +187,12 @@ internal sealed class Program
|
||||
if (response is not null)
|
||||
{
|
||||
ExternalResponse requestResponse = requestInfo.Request.CreateResponse(response);
|
||||
await run.Run.SendResponseAsync(requestResponse).ConfigureAwait(false);
|
||||
await run.Run.SendResponseAsync(requestResponse);
|
||||
response = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
await run.Run.DisposeAsync().ConfigureAwait(false);
|
||||
await run.Run.DisposeAsync();
|
||||
return requestInfo.Request;
|
||||
}
|
||||
break;
|
||||
|
||||
+4
-4
@@ -24,18 +24,18 @@ public static class Program
|
||||
private static async Task Main()
|
||||
{
|
||||
// Create the workflow
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync().ConfigureAwait(false);
|
||||
var workflow = await WorkflowHelper.GetWorkflowAsync();
|
||||
|
||||
// Execute the workflow
|
||||
await using StreamingRun handle = await InProcessExecution.StreamAsync(workflow, NumberSignal.Init).ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in handle.WatchStreamAsync().ConfigureAwait(false))
|
||||
await using StreamingRun handle = await InProcessExecution.StreamAsync(workflow, NumberSignal.Init);
|
||||
await foreach (WorkflowEvent evt in handle.WatchStreamAsync())
|
||||
{
|
||||
switch (evt)
|
||||
{
|
||||
case RequestInfoEvent requestInputEvt:
|
||||
// Handle `RequestInfoEvent` from the workflow
|
||||
ExternalResponse response = HandleExternalRequest(requestInputEvt.Request);
|
||||
await handle.SendResponseAsync(response).ConfigureAwait(false);
|
||||
await handle.SendResponseAsync(response);
|
||||
break;
|
||||
|
||||
case WorkflowOutputEvent outputEvt:
|
||||
|
||||
+5
-7
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
|
||||
namespace WorkflowHumanInTheLoopBasicSample;
|
||||
|
||||
@@ -39,7 +38,7 @@ internal enum NumberSignal
|
||||
/// <summary>
|
||||
/// Executor that judges the guess and provides feedback.
|
||||
/// </summary>
|
||||
internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge"), IMessageHandler<int>
|
||||
internal sealed class JudgeExecutor() : Executor<int>("Judge")
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries;
|
||||
@@ -53,21 +52,20 @@ internal sealed class JudgeExecutor() : ReflectingExecutor<JudgeExecutor>("Judge
|
||||
this._targetNumber = targetNumber;
|
||||
}
|
||||
|
||||
public async ValueTask HandleAsync(int message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(int message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this._tries++;
|
||||
if (message == this._targetNumber)
|
||||
{
|
||||
await context.YieldOutputAsync($"{this._targetNumber} found in {this._tries} tries!", cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
await context.YieldOutputAsync($"{this._targetNumber} found in {this._tries} tries!", cancellationToken);
|
||||
}
|
||||
else if (message < this._targetNumber)
|
||||
{
|
||||
await context.SendMessageAsync(NumberSignal.Below, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(NumberSignal.Below, cancellationToken: cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.SendMessageAsync(NumberSignal.Above, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(NumberSignal.Above, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
|
||||
namespace WorkflowLoopSample;
|
||||
|
||||
@@ -33,8 +32,8 @@ public static class Program
|
||||
.BuildAsync<NumberSignal>();
|
||||
|
||||
// Execute the workflow
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, NumberSignal.Init).ConfigureAwait(false);
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, NumberSignal.Init);
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is WorkflowOutputEvent outputEvent)
|
||||
{
|
||||
@@ -57,7 +56,7 @@ internal enum NumberSignal
|
||||
/// <summary>
|
||||
/// Executor that makes a guess based on the current bounds.
|
||||
/// </summary>
|
||||
internal sealed class GuessNumberExecutor : ReflectingExecutor<GuessNumberExecutor>, IMessageHandler<NumberSignal>
|
||||
internal sealed class GuessNumberExecutor : Executor<NumberSignal>
|
||||
{
|
||||
/// <summary>
|
||||
/// The lower bound of the guessing range.
|
||||
@@ -83,20 +82,20 @@ internal sealed class GuessNumberExecutor : ReflectingExecutor<GuessNumberExecut
|
||||
|
||||
private int NextGuess => (this.LowerBound + this.UpperBound) / 2;
|
||||
|
||||
public async ValueTask HandleAsync(NumberSignal message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(NumberSignal message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case NumberSignal.Init:
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken);
|
||||
break;
|
||||
case NumberSignal.Above:
|
||||
this.UpperBound = this.NextGuess - 1;
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken);
|
||||
break;
|
||||
case NumberSignal.Below:
|
||||
this.LowerBound = this.NextGuess + 1;
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(this.NextGuess, cancellationToken: cancellationToken);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -105,7 +104,7 @@ internal sealed class GuessNumberExecutor : ReflectingExecutor<GuessNumberExecut
|
||||
/// <summary>
|
||||
/// Executor that judges the guess and provides feedback.
|
||||
/// </summary>
|
||||
internal sealed class JudgeExecutor : ReflectingExecutor<JudgeExecutor>, IMessageHandler<int>
|
||||
internal sealed class JudgeExecutor : Executor<int>
|
||||
{
|
||||
private readonly int _targetNumber;
|
||||
private int _tries;
|
||||
@@ -120,21 +119,21 @@ internal sealed class JudgeExecutor : ReflectingExecutor<JudgeExecutor>, IMessag
|
||||
this._targetNumber = targetNumber;
|
||||
}
|
||||
|
||||
public async ValueTask HandleAsync(int message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(int message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this._tries++;
|
||||
if (message == this._targetNumber)
|
||||
{
|
||||
await context.YieldOutputAsync($"{this._targetNumber} found in {this._tries} tries!", cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
;
|
||||
}
|
||||
else if (message < this._targetNumber)
|
||||
{
|
||||
await context.SendMessageAsync(NumberSignal.Below, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(NumberSignal.Below, cancellationToken: cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.SendMessageAsync(NumberSignal.Above, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await context.SendMessageAsync(NumberSignal.Above, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -3,7 +3,6 @@
|
||||
using System.Diagnostics;
|
||||
using Azure.Monitor.OpenTelemetry.Exporter;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Resources;
|
||||
using OpenTelemetry.Trace;
|
||||
@@ -69,7 +68,7 @@ public static class Program
|
||||
/// <summary>
|
||||
/// First executor: converts input text to uppercase.
|
||||
/// </summary>
|
||||
internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor>("UppercaseExecutor"), IMessageHandler<string, string>
|
||||
internal sealed class UppercaseExecutor() : Executor<string, string>("UppercaseExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes the input message by converting it to uppercase.
|
||||
@@ -79,14 +78,14 @@ internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>The input text converted to uppercase</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
public override async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Second executor: reverses the input text and completes the workflow.
|
||||
/// </summary>
|
||||
internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExecutor>("ReverseTextExecutor"), IMessageHandler<string, string>
|
||||
internal sealed class ReverseTextExecutor() : Executor<string, string>("ReverseTextExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes the input message by reversing the text.
|
||||
@@ -96,6 +95,6 @@ internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExec
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>The input text reversed</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
=> new(message.Reverse().ToArray());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Logs;
|
||||
using OpenTelemetry.Metrics;
|
||||
@@ -71,7 +70,7 @@ public static class Program
|
||||
/// <summary>
|
||||
/// First executor: converts input text to uppercase.
|
||||
/// </summary>
|
||||
internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor>("UppercaseExecutor"), IMessageHandler<string, string>
|
||||
internal sealed class UppercaseExecutor() : Executor<string, string>("UppercaseExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes the input message by converting it to uppercase.
|
||||
@@ -81,14 +80,14 @@ internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>The input text converted to uppercase</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
public override async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Second executor: reverses the input text and completes the workflow.
|
||||
/// </summary>
|
||||
internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExecutor>("ReverseTextExecutor"), IMessageHandler<string, string>
|
||||
internal sealed class ReverseTextExecutor() : Executor<string, string>("ReverseTextExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes the input message by reversing the text.
|
||||
@@ -98,6 +97,6 @@ internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExec
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>The input text reversed</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
=> new(message.Reverse().ToArray());
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
|
||||
namespace WorkflowSharedStatesSample;
|
||||
|
||||
@@ -52,9 +51,9 @@ internal static class FileContentStateConstants
|
||||
public const string FileContentStateScope = "FileContentState";
|
||||
}
|
||||
|
||||
internal sealed class FileReadExecutor() : ReflectingExecutor<FileReadExecutor>("FileReadExecutor"), IMessageHandler<string, string>
|
||||
internal sealed class FileReadExecutor() : Executor<string, string>("FileReadExecutor")
|
||||
{
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Read file content from embedded resource
|
||||
string fileContent = Resources.Read(message);
|
||||
@@ -72,9 +71,9 @@ internal sealed class FileStats
|
||||
public int WordCount { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class WordCountingExecutor() : ReflectingExecutor<WordCountingExecutor>("WordCountingExecutor"), IMessageHandler<string, FileStats>
|
||||
internal sealed class WordCountingExecutor() : Executor<string, FileStats>("WordCountingExecutor")
|
||||
{
|
||||
public async ValueTask<FileStats> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<FileStats> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Retrieve the file content from the shared state
|
||||
var fileContent = await context.ReadStateAsync<string>(message, scopeName: FileContentStateConstants.FileContentStateScope, cancellationToken)
|
||||
@@ -86,10 +85,9 @@ internal sealed class WordCountingExecutor() : ReflectingExecutor<WordCountingEx
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ParagraphCountingExecutor() : ReflectingExecutor<ParagraphCountingExecutor>("ParagraphCountingExecutor"),
|
||||
IMessageHandler<string, FileStats>
|
||||
internal sealed class ParagraphCountingExecutor() : Executor<string, FileStats>("ParagraphCountingExecutor")
|
||||
{
|
||||
public async ValueTask<FileStats> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask<FileStats> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Retrieve the file content from the shared state
|
||||
var fileContent = await context.ReadStateAsync<string>(message, scopeName: FileContentStateConstants.FileContentStateScope, cancellationToken)
|
||||
@@ -101,11 +99,11 @@ internal sealed class ParagraphCountingExecutor() : ReflectingExecutor<Paragraph
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class AggregationExecutor() : ReflectingExecutor<AggregationExecutor>("AggregationExecutor"), IMessageHandler<FileStats>
|
||||
internal sealed class AggregationExecutor() : Executor<FileStats>("AggregationExecutor")
|
||||
{
|
||||
private readonly List<FileStats> _messages = [];
|
||||
|
||||
public async ValueTask HandleAsync(FileStats message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override async ValueTask HandleAsync(FileStats message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this._messages.Add(message);
|
||||
|
||||
|
||||
+6
-7
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
|
||||
namespace WorkflowExecutorsAndEdgesSample;
|
||||
|
||||
@@ -44,7 +43,7 @@ public static class Program
|
||||
/// <summary>
|
||||
/// First executor: converts input text to uppercase.
|
||||
/// </summary>
|
||||
internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor>("UppercaseExecutor"), IMessageHandler<string, string>
|
||||
internal sealed class UppercaseExecutor() : Executor<string, string>("UppercaseExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes the input message by converting it to uppercase.
|
||||
@@ -54,14 +53,14 @@ internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>The input text converted to uppercase</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors
|
||||
public override ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
ValueTask.FromResult(message.ToUpperInvariant()); // The return value will be sent as a message along an edge to subsequent executors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Second executor: reverses the input text and completes the workflow.
|
||||
/// </summary>
|
||||
internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExecutor>("ReverseTextExecutor"), IMessageHandler<string, string>
|
||||
internal sealed class ReverseTextExecutor() : Executor<string, string>("ReverseTextExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes the input message by reversing the text.
|
||||
@@ -71,9 +70,9 @@ internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExec
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>The input text reversed</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
public override ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Because we do not suppress it, the returned result will be yielded as an output from this executor.
|
||||
return string.Concat(message.Reverse());
|
||||
return ValueTask.FromResult(string.Concat(message.Reverse()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Reflection;
|
||||
|
||||
namespace WorkflowStreamingSample;
|
||||
|
||||
@@ -30,7 +29,7 @@ public static class Program
|
||||
|
||||
// Execute the workflow in streaming mode
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, "Hello, World!");
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is ExecutorCompletedEvent executorCompleted)
|
||||
{
|
||||
@@ -43,7 +42,7 @@ public static class Program
|
||||
/// <summary>
|
||||
/// First executor: converts input text to uppercase.
|
||||
/// </summary>
|
||||
internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor>("UppercaseExecutor"), IMessageHandler<string, string>
|
||||
internal sealed class UppercaseExecutor() : Executor<string, string>("UppercaseExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes the input message by converting it to uppercase.
|
||||
@@ -53,14 +52,14 @@ internal sealed class UppercaseExecutor() : ReflectingExecutor<UppercaseExecutor
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>The input text converted to uppercase</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
message.ToUpperInvariant(); // The return value will be sent as a message along an edge to subsequent executors
|
||||
public override ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default) =>
|
||||
ValueTask.FromResult(message.ToUpperInvariant()); // The return value will be sent as a message along an edge to subsequent executors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Second executor: reverses the input text and completes the workflow.
|
||||
/// </summary>
|
||||
internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExecutor>("ReverseTextExecutor"), IMessageHandler<string, string>
|
||||
internal sealed class ReverseTextExecutor() : Executor<string, string>("ReverseTextExecutor")
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes the input message by reversing the text.
|
||||
@@ -70,9 +69,9 @@ internal sealed class ReverseTextExecutor() : ReflectingExecutor<ReverseTextExec
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.
|
||||
/// The default is <see cref="CancellationToken.None"/>.</param>
|
||||
/// <returns>The input text reversed</returns>
|
||||
public async ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken)
|
||||
public override ValueTask<string> HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Because we do not suppress it, the returned result will be yielded as an output from this executor.
|
||||
return string.Concat(message.Reverse());
|
||||
return ValueTask.FromResult(string.Concat(message.Reverse()));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ public static class Program
|
||||
// The agents are wrapped as executors. When they receive messages,
|
||||
// they will cache the messages and only start processing when they receive a TurnToken.
|
||||
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is AgentRunUpdateEvent executorComplete)
|
||||
{
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ public static class Program
|
||||
|
||||
await using StreamingRun run = await InProcessExecution.StreamAsync(workflow, messages);
|
||||
await run.TrySendMessageAsync(new TurnToken(emitEvents: true));
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
||||
{
|
||||
if (evt is AgentRunUpdateEvent e)
|
||||
{
|
||||
|
||||
+1
-2
@@ -58,8 +58,7 @@ AIAgent reporter = new ChatClientAgent(anthropic,
|
||||
|
||||
// Build a sequential workflow: Researcher -> Fact-Checker -> Reporter
|
||||
AIAgent workflowAgent = await AgentWorkflowBuilder.BuildSequential(researcher, factChecker, reporter)
|
||||
.AsAgentAsync()
|
||||
.ConfigureAwait(false);
|
||||
.AsAgentAsync();
|
||||
|
||||
// Run the workflow, streaming the output as it arrives.
|
||||
string? lastAuthor = null;
|
||||
|
||||
Reference in New Issue
Block a user