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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user