.NET Workflow - Update declarative code patterns (#1313)

* It hurts

* Fixed

* Namespace

* Namespace
This commit is contained in:
Chris
2025-10-08 14:32:47 -07:00
committed by GitHub
Unverified
parent 74864f353d
commit 988623e7b8
10 changed files with 47 additions and 32 deletions
@@ -37,7 +37,13 @@ public sealed class AzureAgentProvider(string projectEndpoint, TokenCredential p
/// <inheritdoc/>
public override async Task<string> CreateConversationAsync(CancellationToken cancellationToken = default)
{
PersistentAgentThread conversation = await this.GetAgentsClient().Threads.CreateThreadAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
PersistentAgentThread conversation =
await this.GetAgentsClient().Threads.CreateThreadAsync(
messages: null,
toolResources: null,
metadata: null,
cancellationToken).ConfigureAwait(false);
return conversation.Id;
}
@@ -92,7 +98,7 @@ public sealed class AzureAgentProvider(string projectEndpoint, TokenCredential p
/// <inheritdoc/>
public override async Task<AIAgent> GetAgentAsync(string agentId, CancellationToken cancellationToken = default) =>
await this.GetAgentsClient().GetAIAgentAsync(agentId, chatOptions: null, cancellationToken: cancellationToken).ConfigureAwait(false);
await this.GetAgentsClient().GetAIAgentAsync(agentId, chatOptions: null, clientFactory: null, cancellationToken).ConfigureAwait(false);
/// <inheritdoc/>
public override async Task<ChatMessage> GetMessageAsync(string conversationId, string messageId, CancellationToken cancellationToken = default)
@@ -103,7 +103,7 @@ internal static class AgentProviderExtensions
{
conversationId = assignValue;
await context.QueueConversationUpdateAsync(conversationId, cancellationToken: cancellationToken).ConfigureAwait(false);
await context.QueueConversationUpdateAsync(conversationId, cancellationToken).ConfigureAwait(false);
}
}
}
@@ -20,8 +20,17 @@ internal static class IWorkflowContextExtensions
public static ValueTask RaiseCompletionEventAsync(this IWorkflowContext context, DialogAction action, CancellationToken cancellationToken = default) =>
context.AddEventAsync(new DeclarativeActionCompletedEvent(action), cancellationToken);
public static ValueTask SendResultMessageAsync(this IWorkflowContext context, string id, object? result = null, CancellationToken cancellationToken = default) =>
context.SendMessageAsync(new ActionExecutorResult(id, result), cancellationToken: cancellationToken);
public static FormulaValue ReadState(this IWorkflowContext context, PropertyPath variablePath) =>
context.ReadState(Throw.IfNull(variablePath.VariableName), Throw.IfNull(variablePath.NamespaceAlias));
public static FormulaValue ReadState(this IWorkflowContext context, string key, string? scopeName = null) =>
DeclarativeContext(context).State.Get(key, scopeName);
public static ValueTask SendResultMessageAsync(this IWorkflowContext context, string id, CancellationToken cancellationToken = default) =>
context.SendResultMessageAsync(id, result: null, cancellationToken);
public static ValueTask SendResultMessageAsync(this IWorkflowContext context, string id, object? result, CancellationToken cancellationToken = default) =>
context.SendMessageAsync(new ActionExecutorResult(id, result), targetId: null, cancellationToken);
public static ValueTask QueueStateResetAsync(this IWorkflowContext context, PropertyPath variablePath, CancellationToken cancellationToken = default) =>
context.QueueStateUpdateAsync(Throw.IfNull(variablePath.VariableName), UnassignedValue.Instance, Throw.IfNull(variablePath.NamespaceAlias), cancellationToken);
@@ -29,14 +38,22 @@ internal static class IWorkflowContextExtensions
public static ValueTask QueueStateUpdateAsync<TValue>(this IWorkflowContext context, PropertyPath variablePath, TValue? value, CancellationToken cancellationToken = default) =>
context.QueueStateUpdateAsync(Throw.IfNull(variablePath.VariableName), value, Throw.IfNull(variablePath.NamespaceAlias), cancellationToken);
public static ValueTask QueueSystemUpdateAsync<TValue>(this IWorkflowContext context, string key, TValue? value, CancellationToken cancellationToken = default) =>
DeclarativeContext(context).QueueSystemUpdateAsync(key, value, cancellationToken);
public static async ValueTask QueueEnvironmentUpdateAsync<TValue>(this IWorkflowContext context, string key, TValue? value, CancellationToken cancellationToken = default)
{
DeclarativeWorkflowContext declarativeContext = DeclarativeContext(context);
await declarativeContext.UpdateStateAsync(key, value, VariableScopeNames.Environment, allowSystem: true, cancellationToken).ConfigureAwait(false);
declarativeContext.State.Bind();
}
public static FormulaValue ReadState(this IWorkflowContext context, PropertyPath variablePath) =>
context.ReadState(Throw.IfNull(variablePath.VariableName), Throw.IfNull(variablePath.NamespaceAlias));
public static async ValueTask QueueSystemUpdateAsync<TValue>(this IWorkflowContext context, string key, TValue? value, CancellationToken cancellationToken = default)
{
DeclarativeWorkflowContext declarativeContext = DeclarativeContext(context);
await declarativeContext.UpdateStateAsync(key, value, VariableScopeNames.System, allowSystem: true, cancellationToken).ConfigureAwait(false);
declarativeContext.State.Bind();
}
public static FormulaValue ReadState(this IWorkflowContext context, string key, string? scopeName = null) =>
DeclarativeContext(context).State.Get(key, scopeName);
public static ValueTask QueueConversationUpdateAsync(this IWorkflowContext context, string conversationId, CancellationToken cancellationToken = default) =>
context.QueueConversationUpdateAsync(conversationId, isExternal: false, cancellationToken);
public static async ValueTask QueueConversationUpdateAsync(this IWorkflowContext context, string conversationId, bool isExternal = false, CancellationToken cancellationToken = default)
{
@@ -53,7 +53,7 @@ internal sealed class DeclarativeWorkflowContext : IWorkflowContext
// Copy keys to array to avoid modifying collection during enumeration.
foreach (string key in this.State.Keys(scopeName).ToArray())
{
await this.UpdateStateAsync(key, UnassignedValue.Instance, scopeName, cancellationToken: cancellationToken).ConfigureAwait(false);
await this.UpdateStateAsync(key, UnassignedValue.Instance, scopeName, allowSystem: false, cancellationToken).ConfigureAwait(false);
}
}
else
@@ -68,13 +68,7 @@ internal sealed class DeclarativeWorkflowContext : IWorkflowContext
/// <inheritdoc/>
public async ValueTask QueueStateUpdateAsync<T>(string key, T? value, string? scopeName = null, CancellationToken cancellationToken = default)
{
await this.UpdateStateAsync(key, value, scopeName, cancellationToken: cancellationToken).ConfigureAwait(false);
this.State.Bind();
}
public async ValueTask QueueSystemUpdateAsync<TValue>(string key, TValue? value, CancellationToken cancellationToken = default)
{
await this.UpdateStateAsync(key, value, VariableScopeNames.System, allowSystem: true, cancellationToken).ConfigureAwait(false);
await this.UpdateStateAsync(key, value, scopeName, allowSystem: false, cancellationToken).ConfigureAwait(false);
this.State.Bind();
}
@@ -105,7 +99,7 @@ internal sealed class DeclarativeWorkflowContext : IWorkflowContext
public ValueTask SendMessageAsync(object message, string? targetId = null, CancellationToken cancellationToken = default)
=> this.Source.SendMessageAsync(message, targetId, cancellationToken);
private ValueTask UpdateStateAsync<T>(string key, T? value, string? scopeName, bool allowSystem = true, CancellationToken cancellationToken = default)
public ValueTask UpdateStateAsync<T>(string key, T? value, string? scopeName, bool allowSystem, CancellationToken cancellationToken = default)
{
bool isManagedScope =
scopeName is not null && // null scope cannot be managed
@@ -43,6 +43,6 @@ internal sealed class DeclarativeWorkflowExecutor<TInput>(
await options.AgentProvider.CreateMessageAsync(conversationId, input, cancellationToken).ConfigureAwait(false);
await declarativeContext.SetLastMessageAsync(input).ConfigureAwait(false);
await context.SendResultMessageAsync(this.Id, cancellationToken: cancellationToken).ConfigureAwait(false);
await context.SendResultMessageAsync(this.Id, cancellationToken).ConfigureAwait(false);
}
}
@@ -49,7 +49,7 @@ internal class DelegateActionExecutor<TMessage> : Executor<TMessage>, IResettabl
if (this._emitResult)
{
await context.SendResultMessageAsync(this.Id, cancellationToken: cancellationToken).ConfigureAwait(false);
await context.SendResultMessageAsync(this.Id, cancellationToken).ConfigureAwait(false);
}
}
}
@@ -6,7 +6,6 @@ using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
using Microsoft.Bot.ObjectModel;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
@@ -71,7 +70,7 @@ public abstract class RootExecutor<TInput> : Executor<TInput>, IResettableExecut
await this._agentProvider.CreateMessageAsync(this._conversationId, input, cancellationToken).ConfigureAwait(false);
await declarativeContext.SetLastMessageAsync(input).ConfigureAwait(false);
await declarativeContext.SendMessageAsync(new ActionExecutorResult(this.Id), cancellationToken: cancellationToken).ConfigureAwait(false);
await declarativeContext.SendResultMessageAsync(this.Id, cancellationToken).ConfigureAwait(false);
}
/// <summary>
@@ -94,7 +93,7 @@ public abstract class RootExecutor<TInput> : Executor<TInput>, IResettableExecut
{
foreach (string variableName in variableNames)
{
await context.QueueStateUpdateAsync(variableName, GetEnvironmentVariable(variableName), VariableScopeNames.Environment).ConfigureAwait(false);
await context.QueueEnvironmentUpdateAsync(variableName, GetEnvironmentVariable(variableName)).ConfigureAwait(false);
}
string GetEnvironmentVariable(string name)
@@ -17,7 +17,7 @@ internal sealed class CreateConversationExecutor(CreateConversation model, Workf
{
string conversationId = await agentProvider.CreateConversationAsync(cancellationToken).ConfigureAwait(false);
await this.AssignAsync(this.Model.ConversationId?.Path, FormulaValue.New(conversationId), context).ConfigureAwait(false);
await context.QueueConversationUpdateAsync(conversationId, cancellationToken: cancellationToken).ConfigureAwait(false);
await context.QueueConversationUpdateAsync(conversationId, cancellationToken).ConfigureAwait(false);
return default;
}
@@ -65,7 +65,7 @@ internal sealed class QuestionExecutor(Question model, WorkflowFormulaState stat
}
else
{
await context.SendResultMessageAsync(this.Id, result: null, cancellationToken).ConfigureAwait(false);
await context.SendResultMessageAsync(this.Id, cancellationToken).ConfigureAwait(false);
}
return default;
@@ -75,7 +75,7 @@ internal sealed class QuestionExecutor(Question model, WorkflowFormulaState stat
{
int count = await this._promptCount.ReadAsync(context).ConfigureAwait(false);
InputRequest inputRequest = new(this.FormatPrompt(this.Model.Prompt));
await context.SendMessageAsync(inputRequest, cancellationToken: cancellationToken).ConfigureAwait(false);
await context.SendMessageAsync(inputRequest, targetId: null, cancellationToken).ConfigureAwait(false);
await this._promptCount.WriteAsync(context, count + 1).ConfigureAwait(false);
}
@@ -109,7 +109,7 @@ internal sealed class QuestionExecutor(Question model, WorkflowFormulaState stat
{
await this.AssignAsync(this.Model.Variable?.Path, extractedValue, context).ConfigureAwait(false);
await this._hasExecuted.WriteAsync(context, true).ConfigureAwait(false);
await context.SendResultMessageAsync(this.Id, result: null, cancellationToken).ConfigureAwait(false);
await context.SendResultMessageAsync(this.Id, cancellationToken).ConfigureAwait(false);
}
}
@@ -129,7 +129,7 @@ internal sealed class QuestionExecutor(Question model, WorkflowFormulaState stat
await this.AssignAsync(this.Model.Variable?.Path, defaultValue.ToFormula(), context).ConfigureAwait(false);
string defaultValueResponse = this.FormatPrompt(this.Model.DefaultValueResponse);
await context.AddEventAsync(new MessageActivityEvent(defaultValueResponse.Trim()), cancellationToken).ConfigureAwait(false);
await context.SendResultMessageAsync(this.Id, result: null, cancellationToken).ConfigureAwait(false);
await context.SendResultMessageAsync(this.Id, cancellationToken).ConfigureAwait(false);
}
else
{
@@ -6,7 +6,6 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
using Microsoft.Bot.ObjectModel;
using Microsoft.PowerFx.Types;
@@ -72,6 +71,6 @@ public abstract class WorkflowActionExecutorTest(ITestOutputHelper output) : Wor
internal sealed class TestWorkflowExecutor() : Executor<WorkflowFormulaState>("test_workflow")
{
public override async ValueTask HandleAsync(WorkflowFormulaState message, IWorkflowContext context, CancellationToken cancellationToken) =>
await context.SendMessageAsync(new ActionExecutorResult(this.Id), cancellationToken: cancellationToken).ConfigureAwait(false);
await context.SendResultMessageAsync(this.Id, cancellationToken).ConfigureAwait(false);
}
}