mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET Workflows - Introduce support for Local variable scope (#944)
* Prepare for update * Checkpoint * Comments * All fixed * Mixed casre test added * Rollback nuget * Remove redundant restorable * Namespace
This commit is contained in:
@@ -90,9 +90,9 @@
|
||||
<!-- Identity -->
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.77.0" />
|
||||
<!-- Workflows -->
|
||||
<PackageVersion Include="Microsoft.Bot.ObjectModel" Version="1.2025.922-1" />
|
||||
<PackageVersion Include="Microsoft.Bot.ObjectModel.Json" Version="1.2025.922-1" />
|
||||
<PackageVersion Include="Microsoft.Bot.ObjectModel.PowerFx" Version="1.2025.922-1" />
|
||||
<PackageVersion Include="Microsoft.Bot.ObjectModel" Version="1.2025.926-1" />
|
||||
<PackageVersion Include="Microsoft.Bot.ObjectModel.Json" Version="1.2025.926-1" />
|
||||
<PackageVersion Include="Microsoft.Bot.ObjectModel.PowerFx" Version="1.2025.926-1" />
|
||||
<PackageVersion Include="Microsoft.PowerFx.Interpreter" Version="1.4.0-build.20250821-1001" />
|
||||
<!-- Test -->
|
||||
<PackageVersion Include="FluentAssertions" Version="8.6.0" />
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Microsoft.Agents.Workflows.Declarative;
|
||||
public static class DeclarativeWorkflowBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a process from the provided YAML definition of a CPS Topic ObjectModel.
|
||||
/// Builds a workflow from the provided YAML definition.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInput">The type of the input message</typeparam>
|
||||
/// <param name="workflowFile">The path to the workflow.</param>
|
||||
@@ -35,7 +35,7 @@ public static class DeclarativeWorkflowBuilder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a process from the provided YAML definition of a CPS Topic ObjectModel.
|
||||
/// Builds a workflow from the provided YAML definition.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInput">The type of the input message</typeparam>
|
||||
/// <param name="yamlReader">The reader that provides the workflow object model YAML.</param>
|
||||
@@ -71,7 +71,7 @@ public static class DeclarativeWorkflowBuilder
|
||||
return visitor.Complete();
|
||||
}
|
||||
|
||||
private static ChatMessage DefaultTransform(object message) =>
|
||||
internal static ChatMessage DefaultTransform(object message) =>
|
||||
message switch
|
||||
{
|
||||
ChatMessage chatMessage => chatMessage,
|
||||
|
||||
+4
-4
@@ -23,16 +23,16 @@ internal static class IWorkflowContextExtensions
|
||||
context.SendMessageAsync(new ExecutorResultMessage(id, result));
|
||||
|
||||
public static ValueTask QueueStateResetAsync(this IWorkflowContext context, PropertyPath variablePath) =>
|
||||
context.QueueStateUpdateAsync(Throw.IfNull(variablePath.VariableName), UnassignedValue.Instance, Throw.IfNull(variablePath.VariableScopeName));
|
||||
context.QueueStateUpdateAsync(Throw.IfNull(variablePath.VariableName), UnassignedValue.Instance, Throw.IfNull(variablePath.NamespaceAlias));
|
||||
|
||||
public static ValueTask QueueStateUpdateAsync<TValue>(this IWorkflowContext context, PropertyPath variablePath, TValue? value) =>
|
||||
context.QueueStateUpdateAsync(Throw.IfNull(variablePath.VariableName), value, Throw.IfNull(variablePath.VariableScopeName));
|
||||
context.QueueStateUpdateAsync(Throw.IfNull(variablePath.VariableName), value, Throw.IfNull(variablePath.NamespaceAlias));
|
||||
|
||||
public static ValueTask QueueSystemUpdateAsync<TValue>(this IWorkflowContext context, string key, TValue? value) =>
|
||||
DeclarativeContext(context).QueueSystemUpdateAsync(key, value);
|
||||
|
||||
public static FormulaValue ReadState(this IWorkflowContext context, PropertyPath variablePath) =>
|
||||
context.ReadState(Throw.IfNull(variablePath.VariableName), Throw.IfNull(variablePath.VariableScopeName));
|
||||
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);
|
||||
@@ -51,7 +51,7 @@ internal static class IWorkflowContextExtensions
|
||||
public static async ValueTask EnsureWorkflowConversationAsync(this IWorkflowContext context, WorkflowAgentProvider agentProvider, StringExpression expression, CancellationToken cancellationToken)
|
||||
{
|
||||
if (expression.IsVariableReference &&
|
||||
expression.VariableReference.IsVariableReferenceWithScope(VariableScopeNames.System, out string? variableName))
|
||||
expression.VariableReference.IsVariableReferenceWithScope(VariableNamespace.System, out string? variableName))
|
||||
{
|
||||
if (string.Equals(variableName, SystemScope.Names.Conversation, StringComparison.Ordinal) ||
|
||||
string.Equals(variableName, SystemScope.Names.ConversationId, StringComparison.Ordinal))
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ internal sealed class DeclarativeWorkflowContext : IWorkflowContext
|
||||
{
|
||||
public static readonly FrozenSet<string> ManagedScopes =
|
||||
[
|
||||
VariableScopeNames.Local,
|
||||
VariableScopeNames.Topic,
|
||||
VariableScopeNames.Global,
|
||||
];
|
||||
|
||||
+9
-2
@@ -211,6 +211,15 @@ internal sealed class WorkflowActionVisitor : DialogActionVisitor
|
||||
this.RestartAfter(item.Id.Value, endExecutor.ParentId);
|
||||
}
|
||||
|
||||
protected override void Visit(EndDialog item)
|
||||
{
|
||||
this.Trace(item);
|
||||
|
||||
DefaultActionExecutor endExecutor = new(item, this._workflowState);
|
||||
this.ContinueWith(endExecutor);
|
||||
this.RestartAfter(item.Id.Value, endExecutor.ParentId);
|
||||
}
|
||||
|
||||
protected override void Visit(Question item)
|
||||
{
|
||||
this.Trace(item);
|
||||
@@ -388,8 +397,6 @@ internal sealed class WorkflowActionVisitor : DialogActionVisitor
|
||||
|
||||
protected override void Visit(UnknownDialogAction item) => this.NotSupported(item);
|
||||
|
||||
protected override void Visit(EndDialog item) => this.NotSupported(item);
|
||||
|
||||
protected override void Visit(RepeatDialog item) => this.NotSupported(item);
|
||||
|
||||
protected override void Visit(ReplaceDialog item) => this.NotSupported(item);
|
||||
|
||||
@@ -6,11 +6,6 @@ namespace Microsoft.Agents.Workflows.Declarative.Interpreter;
|
||||
|
||||
internal sealed class WorkflowElementWalker : BotElementWalker
|
||||
{
|
||||
static WorkflowElementWalker()
|
||||
{
|
||||
ProductContext.SetContext(Product.Foundry);
|
||||
}
|
||||
|
||||
private readonly DialogActionVisitor _visitor;
|
||||
|
||||
public WorkflowElementWalker(DialogActionVisitor visitor)
|
||||
|
||||
+1
@@ -37,6 +37,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="Microsoft.Agents.Workflows.Declarative.UnitTests" />
|
||||
<InternalsVisibleTo Include="Microsoft.Agents.Workflows.Declarative.IntegrationTests" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -17,8 +17,9 @@ internal static class RecalcEngineFactory
|
||||
|
||||
foreach (string scopeName in VariableScopeNames.AllScopes)
|
||||
{
|
||||
engine.UpdateVariable(scopeName, RecordValue.Empty());
|
||||
engine.UpdateVariable(WorkflowFormulaState.GetScopeName(scopeName), RecordValue.Empty());
|
||||
}
|
||||
engine.UpdateVariable(VariableScopeNames.Topic, RecordValue.Empty());
|
||||
|
||||
return engine;
|
||||
|
||||
|
||||
@@ -59,13 +59,13 @@ internal static class WorkflowDiagnostics
|
||||
|
||||
FormulaValue defaultValue = variableDiagnostic.ConstantValue?.ToFormula() ?? variableDiagnostic.Type.NewBlank();
|
||||
|
||||
if (variableDiagnostic.Path.VariableScopeName?.Equals(VariableScopeNames.System, StringComparison.OrdinalIgnoreCase) is true &&
|
||||
if (variableDiagnostic.Path.NamespaceAlias?.Equals(VariableScopeNames.System, StringComparison.OrdinalIgnoreCase) is true &&
|
||||
!SystemScope.AllNames.Contains(variableDiagnostic.Path.VariableName))
|
||||
{
|
||||
throw new DeclarativeModelException($"Variable '{variableDiagnostic.Path.VariableName}' is not a supported system variable.");
|
||||
}
|
||||
|
||||
scopes.Set(variableDiagnostic.Path.VariableName, defaultValue, variableDiagnostic.Path.VariableScopeName ?? WorkflowFormulaState.DefaultScopeName);
|
||||
scopes.Set(variableDiagnostic.Path.VariableName, defaultValue, variableDiagnostic.Path.NamespaceAlias ?? WorkflowFormulaState.DefaultScopeName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,12 +17,11 @@ namespace Microsoft.Agents.Workflows.Declarative.PowerFx;
|
||||
/// </summary>
|
||||
internal sealed class WorkflowFormulaState
|
||||
{
|
||||
// ISSUE #488 - Update default scope for workflows to `Workflow` (instead of `Topic`)
|
||||
public const string DefaultScopeName = VariableScopeNames.Topic;
|
||||
public const string DefaultScopeName = VariableScopeNames.Local;
|
||||
|
||||
public static readonly FrozenSet<string> RestorableScopes =
|
||||
[
|
||||
VariableScopeNames.Topic,
|
||||
VariableScopeNames.Local,
|
||||
VariableScopeNames.Global,
|
||||
VariableScopeNames.System,
|
||||
];
|
||||
@@ -37,7 +36,8 @@ internal sealed class WorkflowFormulaState
|
||||
|
||||
public WorkflowFormulaState(RecalcEngine engine)
|
||||
{
|
||||
this._scopes = VariableScopeNames.AllScopes.ToDictionary(scopeName => scopeName, scopeName => new WorkflowScope());
|
||||
this._scopes = VariableScopeNames.AllScopes.ToDictionary(scopeName => GetScopeName(scopeName), _ => new WorkflowScope());
|
||||
|
||||
this.Engine = engine;
|
||||
this.Evaluator = new WorkflowExpressionEngine(engine);
|
||||
this.Bind();
|
||||
@@ -87,11 +87,15 @@ internal sealed class WorkflowFormulaState
|
||||
}
|
||||
}
|
||||
|
||||
public void Bind(string? targetScope = null)
|
||||
public void Bind(string? scopeNameToBind = null)
|
||||
{
|
||||
if (targetScope is not null)
|
||||
if (scopeNameToBind is not null)
|
||||
{
|
||||
Bind(targetScope);
|
||||
Bind(scopeNameToBind);
|
||||
if (VariableScopeNames.GetNamespaceFromName(scopeNameToBind) == VariableNamespace.Component)
|
||||
{
|
||||
Bind(scopeNameToBind, VariableScopeNames.Topic);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -99,26 +103,38 @@ internal sealed class WorkflowFormulaState
|
||||
{
|
||||
Bind(scopeName);
|
||||
}
|
||||
|
||||
Bind(DefaultScopeName, VariableScopeNames.Topic);
|
||||
}
|
||||
|
||||
void Bind(string scopeName)
|
||||
void Bind(string scopeName, string? targetScope = null)
|
||||
{
|
||||
targetScope = GetScopeName(targetScope ?? scopeName);
|
||||
RecordValue scopeRecord = this.GetScope(scopeName).ToRecord();
|
||||
this.Engine.DeleteFormula(scopeName);
|
||||
this.Engine.UpdateVariable(scopeName, scopeRecord);
|
||||
this.Engine.DeleteFormula(targetScope);
|
||||
this.Engine.UpdateVariable(targetScope, scopeRecord);
|
||||
}
|
||||
}
|
||||
|
||||
private WorkflowScope GetScope(string? scopeName)
|
||||
{
|
||||
scopeName ??= DefaultScopeName;
|
||||
private WorkflowScope GetScope(string? scopeName) => this._scopes[GetScopeName(scopeName)];
|
||||
|
||||
if (!VariableScopeNames.IsValidName(scopeName))
|
||||
public static string GetScopeName(string? scopeName)
|
||||
{
|
||||
if (!ProductContext.IsLocalScopeSupported())
|
||||
{
|
||||
throw new DeclarativeActionException($"Invalid variable scope name: '{scopeName}'.");
|
||||
ProductContext.SetContext(Product.Foundry);
|
||||
}
|
||||
|
||||
return this._scopes[scopeName];
|
||||
scopeName ??= DefaultScopeName;
|
||||
|
||||
return
|
||||
VariableScopeNames.GetNamespaceFromName(scopeName) switch
|
||||
{
|
||||
// Always alias component level scope as "Local"
|
||||
VariableNamespace.Component => DefaultScopeName,
|
||||
VariableNamespace.Unknown => throw new DeclarativeActionException($"Invalid variable scope name: '{scopeName}'."),
|
||||
_ => scopeName,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Agents.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
@@ -35,7 +35,7 @@ public abstract class WorkflowTest : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
internal static string FormatVariablePath(string variableName, string? scope = null) => $"{scope ?? VariableScopeNames.Topic}.{variableName}";
|
||||
internal static string FormatVariablePath(string variableName, string? scope = null) => $"{scope ?? WorkflowFormulaState.DefaultScopeName}.{variableName}";
|
||||
|
||||
protected static IConfigurationRoot InitializeConfig() =>
|
||||
new ConfigurationBuilder()
|
||||
|
||||
+9
-9
@@ -7,23 +7,23 @@ trigger:
|
||||
|
||||
- kind: CreateConversation
|
||||
id: conversation_create1
|
||||
conversationId: Topic.FirstConversationId
|
||||
conversationId: Local.FirstConversationId
|
||||
|
||||
- kind: CreateConversation
|
||||
id: conversation_create2
|
||||
conversationId: Topic.SecondConversationId
|
||||
conversationId: Local.SecondConversationId
|
||||
|
||||
- kind: SendActivity
|
||||
id: sendActivity_conversation
|
||||
activity: |-
|
||||
Conversation 1: {Topic.FirstConversationId}
|
||||
Conversation 2: {Topic.SecondConversationId}
|
||||
Conversation 1: {Local.FirstConversationId}
|
||||
Conversation 2: {Local.SecondConversationId}
|
||||
|
||||
- kind: AddConversationMessage
|
||||
id: add_message
|
||||
message: Topic.MyMessage1
|
||||
message: Local.MyMessage1
|
||||
role: User
|
||||
conversationId: =Topic.FirstConversationId
|
||||
conversationId: =Local.FirstConversationId
|
||||
content:
|
||||
- type: Text
|
||||
value: {System.LastMessage.Text}
|
||||
@@ -31,12 +31,12 @@ trigger:
|
||||
- kind: SendActivity
|
||||
id: sendActivity_message
|
||||
activity: |-
|
||||
Messsage 1: {Topic.MyMessage1}
|
||||
Messsage 1: {Local.MyMessage1}
|
||||
|
||||
- kind: CopyConversationMessages
|
||||
id: copy_messages
|
||||
conversationId: =Topic.SecondConversationId
|
||||
messages: =[Topic.MyMessage1]
|
||||
conversationId: =Local.SecondConversationId
|
||||
messages: =[Local.MyMessage1]
|
||||
|
||||
- kind: SendActivity
|
||||
id: sendActivity_copy
|
||||
|
||||
+2
-2
@@ -7,11 +7,11 @@ trigger:
|
||||
|
||||
- kind: RetrieveConversationMessage
|
||||
id: get_message
|
||||
message: Topic.MyMessage
|
||||
message: Local.MyMessage
|
||||
conversationId: thread_T8xIzNrNcPkUkoCEGzxg80Vt
|
||||
messageId: msg_J4x6YZTDUUWNs60FOUAucldy
|
||||
|
||||
- kind: SendActivity
|
||||
id: sendActivity_message
|
||||
activity: |-
|
||||
{Topic.MyMessage}
|
||||
{Local.MyMessage}
|
||||
|
||||
+2
-2
@@ -7,10 +7,10 @@ trigger:
|
||||
|
||||
- kind: RetrieveConversationMessages
|
||||
id: get_message
|
||||
messages: Topic.MyMessages
|
||||
messages: Local.MyMessages
|
||||
conversationId: thread_T8xIzNrNcPkUkoCEGzxg80Vt
|
||||
|
||||
- kind: SendActivity
|
||||
id: sendActivity_message
|
||||
activity: |-
|
||||
{Topic.MyMessages}
|
||||
{Local.MyMessages}
|
||||
|
||||
+1
-1
@@ -12,4 +12,4 @@ trigger:
|
||||
input:
|
||||
messages: =[UserMessage(System.LastMessageText)]
|
||||
output:
|
||||
messages: Topic.Answer
|
||||
messages: Local.Answer
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ trigger:
|
||||
# Capture input
|
||||
- kind: SetVariable
|
||||
id: set_user_input
|
||||
variable: Topic.UserInput
|
||||
variable: Local.UserInput
|
||||
value: =System.LastMessage.Text
|
||||
|
||||
# Capture environment variable
|
||||
@@ -22,4 +22,4 @@ trigger:
|
||||
id: send_result
|
||||
activity: |-
|
||||
Hello {Global.UserName},
|
||||
You said, "{Topic.UserInput}"
|
||||
You said, "{Local.UserInput}"
|
||||
|
||||
+9
-13
@@ -7,7 +7,6 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.Workflows.Declarative.Interpreter;
|
||||
using Microsoft.Agents.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Agents.Workflows.Reflection;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Moq;
|
||||
using Xunit.Abstractions;
|
||||
@@ -140,7 +139,8 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Single.yaml", 1, "end_all")]
|
||||
[InlineData("EndConversation.yaml", 1, "end_all")]
|
||||
[InlineData("EndDialog.yaml", 1, "end_all")]
|
||||
[InlineData("EditTable.yaml", 2, "edit_var")]
|
||||
[InlineData("EditTableV2.yaml", 2, "edit_var")]
|
||||
[InlineData("ParseValue.yaml", 1, "parse_var")]
|
||||
@@ -149,6 +149,7 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
[InlineData("SetTextVariable.yaml", 1, "set_text")]
|
||||
[InlineData("ClearAllVariables.yaml", 1, "clear_all")]
|
||||
[InlineData("ResetVariable.yaml", 2, "clear_var")]
|
||||
[InlineData("MixedScopes.yaml", 2, "activity_input")]
|
||||
public async Task ExecuteActionAsync(string workflowFile, int expectedCount, string expectedId)
|
||||
{
|
||||
await this.RunWorkflowAsync(workflowFile);
|
||||
@@ -168,7 +169,6 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
[InlineData(typeof(DisableTrigger.Builder))]
|
||||
[InlineData(typeof(DisconnectedNodeContainer.Builder))]
|
||||
[InlineData(typeof(EmitEvent.Builder))]
|
||||
[InlineData(typeof(EndDialog.Builder))]
|
||||
[InlineData(typeof(GetActivityMembers.Builder))]
|
||||
[InlineData(typeof(GetConversationMembers.Builder))]
|
||||
[InlineData(typeof(HttpRequestAction.Builder))]
|
||||
@@ -211,7 +211,7 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
WorkflowFormulaState state = new(RecalcEngineFactory.Create());
|
||||
Mock<WorkflowAgentProvider> mockAgentProvider = new(MockBehavior.Strict);
|
||||
DeclarativeWorkflowOptions options = new(mockAgentProvider.Object);
|
||||
WorkflowActionVisitor visitor = new(new RootExecutor(), state, options);
|
||||
WorkflowActionVisitor visitor = new(new DeclarativeWorkflowExecutor<string>(WorkflowActionVisitor.Steps.Root("anything"), state, (message) => DeclarativeWorkflowBuilder.DefaultTransform(message)), state, options);
|
||||
WorkflowElementWalker walker = new(visitor);
|
||||
walker.Visit(dialog);
|
||||
Assert.True(visitor.HasUnsupportedActions);
|
||||
@@ -244,7 +244,7 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
Assert.Contains(this.WorkflowEvents.OfType<MessageActivityEvent>(), e => string.Equals(e.Message.Trim(), message, StringComparison.Ordinal));
|
||||
|
||||
private Task RunWorkflowAsync(string workflowPath) =>
|
||||
this.RunWorkflowAsync(workflowPath, string.Empty);
|
||||
this.RunWorkflowAsync(workflowPath, "Test input message");
|
||||
|
||||
private async Task RunWorkflowAsync<TInput>(string workflowPath, TInput workflowInput) where TInput : notnull
|
||||
{
|
||||
@@ -272,6 +272,10 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
{
|
||||
this.Output.WriteLine($"ACTION EXIT: {actionCompleteEvent.ActionId}");
|
||||
}
|
||||
else if (workflowEvent is MessageActivityEvent activityEvent)
|
||||
{
|
||||
this.Output.WriteLine($"ACTIVITY: {activityEvent.Message}");
|
||||
}
|
||||
else if (workflowEvent is AgentRunResponseEvent messageEvent)
|
||||
{
|
||||
this.Output.WriteLine($"MESSAGE: {messageEvent.Response.Messages[0].Text.Trim()}");
|
||||
@@ -279,12 +283,4 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
}
|
||||
this.WorkflowEventCounts = this.WorkflowEvents.GroupBy(e => e.GetType()).ToDictionary(e => e.Key, e => e.Count());
|
||||
}
|
||||
|
||||
private sealed class RootExecutor() :
|
||||
ReflectingExecutor<RootExecutor>(WorkflowActionVisitor.Steps.Root("anything")),
|
||||
IMessageHandler<string>
|
||||
{
|
||||
public async ValueTask HandleAsync(string message, IWorkflowContext context) =>
|
||||
await context.SendMessageAsync($"{this.Id}: {DateTime.UtcNow:t}").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -42,7 +42,7 @@ public abstract class WorkflowActionExecutorTest(ITestOutputHelper output) : Wor
|
||||
Assert.Equal(model, action.Model);
|
||||
}
|
||||
|
||||
protected void VerifyState(string variableName, FormulaValue expectedValue) => this.VerifyState(variableName, VariableScopeNames.Topic, expectedValue);
|
||||
protected void VerifyState(string variableName, FormulaValue expectedValue) => this.VerifyState(variableName, WorkflowFormulaState.DefaultScopeName, expectedValue);
|
||||
|
||||
internal void VerifyState(string variableName, string scopeName, FormulaValue expectedValue)
|
||||
{
|
||||
@@ -50,9 +50,7 @@ public abstract class WorkflowActionExecutorTest(ITestOutputHelper output) : Wor
|
||||
Assert.Equal(expectedValue.Format(), actualValue.Format());
|
||||
}
|
||||
|
||||
protected void VerifyUndefined(string variableName) => this.VerifyUndefined(variableName, VariableScopeNames.Topic);
|
||||
|
||||
internal void VerifyUndefined(string variableName, string scopeName) =>
|
||||
internal void VerifyUndefined(string variableName, string? scopeName = null) =>
|
||||
Assert.IsType<BlankValue>(this.State.Get(variableName, scopeName));
|
||||
|
||||
protected static TAction AssignParent<TAction>(DialogAction.Builder actionBuilder) where TAction : DialogAction
|
||||
|
||||
+8
-8
@@ -34,14 +34,14 @@ public class WorkflowExpressionEngineTests : RecalcEngineTest
|
||||
: base(output)
|
||||
{
|
||||
this.State.Set(Variables.GlobalValue, FormulaValue.New(255), VariableScopeNames.Global);
|
||||
this.State.Set(Variables.BoolValue, FormulaValue.New(true), VariableScopeNames.Topic);
|
||||
this.State.Set(Variables.StringValue, FormulaValue.New("Hello World"), VariableScopeNames.Topic);
|
||||
this.State.Set(Variables.IntValue, FormulaValue.New(long.MaxValue), VariableScopeNames.Topic);
|
||||
this.State.Set(Variables.NumberValue, FormulaValue.New(33.3), VariableScopeNames.Topic);
|
||||
this.State.Set(Variables.EnumValue, FormulaValue.New(nameof(VariablesToClear.ConversationScopedVariables)), VariableScopeNames.Topic);
|
||||
this.State.Set(Variables.ObjectValue, ObjectData, VariableScopeNames.Topic);
|
||||
this.State.Set(Variables.ArrayValue, TableData, VariableScopeNames.Topic);
|
||||
this.State.Set(Variables.BlankValue, FormulaValue.NewBlank(), VariableScopeNames.Topic);
|
||||
this.State.Set(Variables.BoolValue, FormulaValue.New(true));
|
||||
this.State.Set(Variables.StringValue, FormulaValue.New("Hello World"));
|
||||
this.State.Set(Variables.IntValue, FormulaValue.New(long.MaxValue));
|
||||
this.State.Set(Variables.NumberValue, FormulaValue.New(33.3));
|
||||
this.State.Set(Variables.EnumValue, FormulaValue.New(nameof(VariablesToClear.ConversationScopedVariables)));
|
||||
this.State.Set(Variables.ObjectValue, ObjectData);
|
||||
this.State.Set(Variables.ArrayValue, TableData);
|
||||
this.State.Set(Variables.BlankValue, FormulaValue.NewBlank());
|
||||
this.State.Bind();
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -15,7 +15,7 @@ public class WorkflowFormulaStateTests
|
||||
{
|
||||
// Arrange
|
||||
FormulaValue testValue = FormulaValue.New("test");
|
||||
this.State.Set("key1", testValue, VariableScopeNames.Topic);
|
||||
this.State.Set("key1", testValue);
|
||||
|
||||
// Act
|
||||
FormulaValue result = this.State.Get("key1");
|
||||
@@ -48,7 +48,7 @@ public class WorkflowFormulaStateTests
|
||||
this.State.Set("key1", testValue);
|
||||
|
||||
// Assert
|
||||
FormulaValue result = this.State.Get("key1", VariableScopeNames.Topic);
|
||||
FormulaValue result = this.State.Get("key1");
|
||||
Assert.Equal(testValue, result);
|
||||
}
|
||||
|
||||
@@ -74,11 +74,11 @@ public class WorkflowFormulaStateTests
|
||||
FormulaValue newValue = FormulaValue.New("new");
|
||||
|
||||
// Act
|
||||
this.State.Set("key1", initialValue, VariableScopeNames.Topic);
|
||||
this.State.Set("key1", newValue, VariableScopeNames.Topic);
|
||||
this.State.Set("key1", initialValue);
|
||||
this.State.Set("key1", newValue);
|
||||
|
||||
// Assert
|
||||
FormulaValue result = this.State.Get("key1", VariableScopeNames.Topic);
|
||||
FormulaValue result = this.State.Get("key1");
|
||||
Assert.Equal(newValue, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Agents.Workflows.Declarative.PowerFx;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.Workflows.Declarative.UnitTests;
|
||||
@@ -36,5 +36,5 @@ public abstract class WorkflowTest : IDisposable
|
||||
internal static string? FormatOptionalPath(string? variableName, string? scope = null) =>
|
||||
variableName is null ? null : FormatVariablePath(variableName, scope);
|
||||
|
||||
internal static string FormatVariablePath(string variableName, string? scope = null) => $"{scope ?? VariableScopeNames.Topic}.{variableName}";
|
||||
internal static string FormatVariablePath(string variableName, string? scope = null) => $"{scope ?? WorkflowFormulaState.DefaultScopeName}.{variableName}";
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,21 +7,21 @@ trigger:
|
||||
|
||||
- kind: SetVariable
|
||||
id: setVariable_test
|
||||
variable: Topic.TestValue
|
||||
variable: Local.TestValue
|
||||
value: =Value(System.LastMessageText)
|
||||
|
||||
- kind: ConditionGroup
|
||||
id: conditionGroup_test
|
||||
conditions:
|
||||
- id: conditionItem_odd
|
||||
condition: =Mod(Topic.TestValue, 2) = 1
|
||||
condition: =Mod(Local.TestValue, 2) = 1
|
||||
actions:
|
||||
- kind: SendActivity
|
||||
id: sendActivity_odd
|
||||
activity: ODD
|
||||
|
||||
- id: conditionItem_even
|
||||
condition: =Mod(Topic.TestValue, 2) = 0
|
||||
condition: =Mod(Local.TestValue, 2) = 0
|
||||
actions:
|
||||
- kind: SendActivity
|
||||
id: sendActivity_even
|
||||
|
||||
+2
-2
@@ -7,14 +7,14 @@ trigger:
|
||||
|
||||
- kind: SetVariable
|
||||
id: setVariable_test
|
||||
variable: Topic.TestValue
|
||||
variable: Local.TestValue
|
||||
value: =Value(System.LastMessageText)
|
||||
|
||||
- kind: ConditionGroup
|
||||
id: conditionGroup_test
|
||||
conditions:
|
||||
- id: conditionItem_odd
|
||||
condition: =Mod(Topic.TestValue, 2) = 1
|
||||
condition: =Mod(Local.TestValue, 2) = 1
|
||||
actions:
|
||||
- kind: SendActivity
|
||||
id: sendActivity_odd
|
||||
|
||||
+2
-2
@@ -7,11 +7,11 @@ trigger:
|
||||
|
||||
- kind: SetVariable
|
||||
id: set_var
|
||||
variable: Topic.MyTable
|
||||
variable: Local.MyTable
|
||||
value: =[{id: 3}]
|
||||
|
||||
- kind: EditTable
|
||||
id: edit_var
|
||||
itemsVariable: Topic.MyTable
|
||||
itemsVariable: Local.MyTable
|
||||
changeType: Add
|
||||
value: ={id: 7}
|
||||
|
||||
+2
-2
@@ -7,12 +7,12 @@ trigger:
|
||||
|
||||
- kind: SetVariable
|
||||
id: set_var
|
||||
variable: Topic.MyTable
|
||||
variable: Local.MyTable
|
||||
value: =[{id: 3}]
|
||||
|
||||
- kind: EditTableV2
|
||||
id: edit_var
|
||||
itemsVariable: Topic.MyTable
|
||||
itemsVariable: Local.MyTable
|
||||
changeType:
|
||||
kind: AddItemOperation
|
||||
value: ={id: 7}
|
||||
|
||||
+5
-1
@@ -5,5 +5,9 @@ trigger:
|
||||
id: my_workflow
|
||||
actions:
|
||||
|
||||
- kind: EndConversation
|
||||
- kind: EndDialog
|
||||
id: end_all
|
||||
|
||||
- kind: SendActivity
|
||||
id: send_activity_1
|
||||
activity: NEVER 1!
|
||||
+6
-6
@@ -7,24 +7,24 @@ trigger:
|
||||
|
||||
- kind: SetVariable
|
||||
id: setVariable_count
|
||||
variable: Topic.Count
|
||||
variable: Local.Count
|
||||
value: =0
|
||||
|
||||
- kind: Foreach
|
||||
id: foreach_loop
|
||||
items: =["a", "b", "c", "d", "e", "f"]
|
||||
index: Topic.LoopIndex
|
||||
value: Topic.LoopValue
|
||||
index: Local.LoopIndex
|
||||
value: Local.LoopValue
|
||||
actions:
|
||||
- kind: BreakLoop
|
||||
id: breakLoop_now
|
||||
- kind: SetVariable
|
||||
id: setVariable_loop
|
||||
variable: Topic.Count
|
||||
value: =Topic.Count + 1
|
||||
variable: Local.Count
|
||||
value: =Local.Count + 1
|
||||
- kind: SendActivity
|
||||
id: sendActivity_loop
|
||||
activity: x{Topic.Count} - {Topic.LoopIndex}:{Topic.LoopValue}
|
||||
activity: x{Local.Count} - {Local.LoopIndex}:{Local.LoopValue}
|
||||
|
||||
- kind: EndConversation
|
||||
id: end_all
|
||||
|
||||
+6
-6
@@ -7,24 +7,24 @@ trigger:
|
||||
actions:
|
||||
- kind: SetVariable
|
||||
id: setVariable_count
|
||||
variable: Topic.Count
|
||||
variable: Local.Count
|
||||
value: =0
|
||||
|
||||
- kind: Foreach
|
||||
id: foreach_loop
|
||||
items: =["a", "b", "c", "d", "e", "f"]
|
||||
index: Topic.LoopIndex
|
||||
value: Topic.LoopValue
|
||||
index: Local.LoopIndex
|
||||
value: Local.LoopValue
|
||||
actions:
|
||||
- kind: ContinueLoop
|
||||
id: continueLoop_now
|
||||
- kind: SetVariable
|
||||
id: setVariable_loop
|
||||
variable: Topic.Count
|
||||
value: =Topic.Count + 1
|
||||
variable: Local.Count
|
||||
value: =Local.Count + 1
|
||||
- kind: SendActivity
|
||||
id: sendActivity_loop
|
||||
activity: x{Topic.Count} - {Topic.LoopIndex}:{Topic.LoopValue}
|
||||
activity: x{Local.Count} - {Local.LoopIndex}:{Local.LoopValue}
|
||||
|
||||
- kind: EndConversation
|
||||
id: end_all
|
||||
|
||||
+6
-6
@@ -7,22 +7,22 @@ trigger:
|
||||
actions:
|
||||
- kind: SetVariable
|
||||
id: setVariable_count
|
||||
variable: Topic.Count
|
||||
variable: Local.Count
|
||||
value: =0
|
||||
|
||||
- kind: Foreach
|
||||
id: foreach_loop
|
||||
items: =["a", "b", "c", "d", "e", "f"]
|
||||
index: Topic.LoopIndex
|
||||
value: Topic.LoopValue
|
||||
index: Local.LoopIndex
|
||||
value: Local.LoopValue
|
||||
actions:
|
||||
- kind: SetVariable
|
||||
id: setVariable_loop
|
||||
variable: Topic.Count
|
||||
value: =Topic.Count + 1
|
||||
variable: Local.Count
|
||||
value: =Local.Count + 1
|
||||
- kind: SendActivity
|
||||
id: sendActivity_loop
|
||||
activity: x{Topic.Count} - {Topic.LoopIndex}:{Topic.LoopValue}
|
||||
activity: x{Local.Count} - {Local.LoopIndex}:{Local.LoopValue}
|
||||
|
||||
- kind: EndConversation
|
||||
id: end_all
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
kind: Workflow
|
||||
trigger:
|
||||
|
||||
kind: OnConversationStart
|
||||
id: my_workflow
|
||||
actions:
|
||||
|
||||
- kind: SetVariable
|
||||
id: set_input
|
||||
variable: Topic.TestValue
|
||||
value: =System.LastMessageText
|
||||
|
||||
- kind: SendActivity
|
||||
id: activity_input
|
||||
activity: |-
|
||||
Input: "{Local.TestValue}"
|
||||
+1
-1
@@ -7,6 +7,6 @@ trigger:
|
||||
actions:
|
||||
- kind: ParseValue
|
||||
id: parse_var
|
||||
variable: Topic.MyVar
|
||||
variable: Local.MyVar
|
||||
value: "42"
|
||||
valueType: Number
|
||||
|
||||
+2
-2
@@ -7,9 +7,9 @@ trigger:
|
||||
|
||||
- kind: SetVariable
|
||||
id: set_var
|
||||
variable: Topic.MyVar
|
||||
variable: Local.MyVar
|
||||
value: 42
|
||||
- kind: ResetVariable
|
||||
id: clear_var
|
||||
variable: Topic.MyVar
|
||||
variable: Local.MyVar
|
||||
|
||||
|
||||
+2
-2
@@ -7,10 +7,10 @@ trigger:
|
||||
|
||||
- kind: SetVariable
|
||||
id: set_input
|
||||
variable: Topic.TestValue
|
||||
variable: Local.TestValue
|
||||
value: =System.LastMessageText
|
||||
|
||||
- kind: SendActivity
|
||||
id: activity_input
|
||||
activity: |-
|
||||
Input: {Topic.TestValue}
|
||||
Input: "{Local.TestValue}"
|
||||
|
||||
+1
-1
@@ -7,5 +7,5 @@ trigger:
|
||||
|
||||
- kind: SetTextVariable
|
||||
id: set_text
|
||||
variable: Topic.TestVar
|
||||
variable: Local.TestVar
|
||||
value: Test content
|
||||
|
||||
+1
-1
@@ -7,5 +7,5 @@ trigger:
|
||||
|
||||
- kind: SetVariable
|
||||
id: set_var
|
||||
variable: Topic.TestVar
|
||||
variable: Local.TestVar
|
||||
value: =3
|
||||
|
||||
Reference in New Issue
Block a user