// Copyright (c) Microsoft. All rights reserved. using System.Collections.Generic; using System.Text; 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.PowerFx; using Microsoft.Agents.ObjectModel; using Microsoft.Agents.ObjectModel.Abstractions; namespace Microsoft.Agents.AI.Workflows.Declarative.Kit; /// /// Extension methods for that assist with /// Power Fx expression evaluation. /// public static class IWorkflowContextExtensions { /// /// Formats a template lines using the workflow's declarative state /// and evaluating any embedded expressions (e.g., Power Fx) contained within each line. /// /// The workflow execution context used to restore persisted state prior to formatting. /// The template line to format. /// A token that propagates notification when operation should be canceled. /// /// A single string containing the formatted results of all lines separated by newline characters. /// A trailing newline will be present if at least one line was processed. /// /// /// Example: /// var text = await context.FormatAsync("Hello @{User.Name}", "Count: @{Metrics.Count}"); /// public static ValueTask FormatTemplateAsync(this IWorkflowContext context, string line, CancellationToken cancellationToken = default) => context.FormatTemplateAsync([line], cancellationToken); /// /// Formats a template lines using the workflow's declarative state /// and evaluating any embedded expressions (e.g., Power Fx) contained within each line. /// /// The workflow execution context used to restore persisted state prior to formatting. /// The template lines to format. /// A token that propagates notification when operation should be canceled. /// /// A single string containing the formatted results of all lines separated by newline characters. /// A trailing newline will be present if at least one line was processed. /// /// /// Example: /// var text = await context.FormatAsync("Hello @{User.Name}", "Count: @{Metrics.Count}"); /// public static async ValueTask FormatTemplateAsync(this IWorkflowContext context, IEnumerable lines, CancellationToken cancellationToken = default) { WorkflowFormulaState state = await context.GetStateAsync(cancellationToken).ConfigureAwait(false); StringBuilder builder = new(); foreach (string line in lines) { builder.AppendLine(state.Engine.Format(TemplateLine.Parse(line))); } return builder.ToString(); } /// /// Evaluate an expression using the workflow's declarative state. /// /// The workflow execution context used to restore persisted state prior to formatting. /// The expression to evaluate. /// A token that propagates notification when operation should be canceled. /// The evaluated expression value public static ValueTask EvaluateValueAsync(this IWorkflowContext context, string expression, CancellationToken cancellationToken = default) => context.EvaluateValueAsync(expression, cancellationToken); /// /// Evaluate an expression using the workflow's declarative state. /// /// The workflow execution context used to restore persisted state prior to formatting. /// The expression to evaluate. /// A token that propagates notification when operation should be canceled. /// The evaluated expression value public static async ValueTask EvaluateValueAsync(this IWorkflowContext context, string expression, CancellationToken cancellationToken = default) { WorkflowFormulaState state = await context.GetStateAsync(cancellationToken).ConfigureAwait(false); EvaluationResult result = state.Evaluator.GetValue(ValueExpression.Expression(expression)); return (TValue?)result.Value.ToObject(); } /// /// Evaluate an expression using the workflow's declarative state. /// /// The type of the list element. /// The workflow execution context used to restore persisted state prior to formatting. /// The expression to evaluate. /// A token that propagates notification when operation should be canceled. /// The evaluated list expression public static async ValueTask?> EvaluateListAsync(this IWorkflowContext context, string expression, CancellationToken cancellationToken = default) { WorkflowFormulaState state = await context.GetStateAsync(cancellationToken).ConfigureAwait(false); EvaluationResult result = state.Evaluator.GetValue(ValueExpression.Expression(expression)); return result.Value.AsList(); } /// /// Convert the result of an expression to the specified target type. /// /// The workflow execution context used to restore persisted state prior to formatting. /// Describes the target type for the value conversion. /// The expression to evaluate. /// A token that propagates notification when operation should be canceled. /// The converted expression value public static async ValueTask ConvertValueAsync(this IWorkflowContext context, VariableType targetType, string expression, CancellationToken cancellationToken = default) { object? sourceValue = await context.EvaluateValueAsync(expression, cancellationToken).ConfigureAwait(false); return sourceValue.ConvertType(targetType); } /// /// Convert the variable value to the specified target type. /// /// The workflow execution context used to restore persisted state prior to formatting. /// Describes the target type for the value conversion. /// The key of the state value. /// An optional name that specifies the scope to read.If null, the default scope is used. /// A token that propagates notification when operation should be canceled. /// The converted value public static async ValueTask ConvertValueAsync(this IWorkflowContext context, VariableType targetType, string key, string? scopeName = null, CancellationToken cancellationToken = default) { object? sourceValue = await context.ReadStateAsync(key, scopeName, cancellationToken).ConfigureAwait(false); return sourceValue.ConvertType(targetType); } /// /// Evaluate an expression using the workflow's declarative state. /// /// The type of the list element. /// The workflow execution context used to restore persisted state prior to formatting. /// The key of the state value. /// An optional name that specifies the scope to read.If null, the default scope is used. /// A token that propagates notification when operation should be canceled. /// The evaluated list expression public static async ValueTask?> ReadListAsync(this IWorkflowContext context, string key, string? scopeName = null, CancellationToken cancellationToken = default) { object? value = await context.ReadStateAsync(key, scopeName, cancellationToken).ConfigureAwait(false); return value.AsList(); } private static async Task GetStateAsync(this IWorkflowContext context, CancellationToken cancellationToken) { if (context is DeclarativeWorkflowContext declarativeContext) { return declarativeContext.State; } WorkflowFormulaState state = new(RecalcEngineFactory.Create()); await state.RestoreAsync(context, cancellationToken).ConfigureAwait(false); return state; } }