// Copyright (c) Microsoft. All rights reserved. using System; using System.IO; 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.Bot.ObjectModel.Yaml; using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI.Workflows.Declarative; /// /// Builder for converting a Foundry workflow object-model YAML definition into a process. /// public static class DeclarativeWorkflowBuilder { /// /// Transforms the input message into a based on . /// Also performs pass-through for input. /// /// The input message to transform. /// The transformed message (as public static ChatMessage DefaultTransform(object message) => message switch { ChatMessage chatMessage => chatMessage, string stringMessage => new ChatMessage(ChatRole.User, stringMessage), _ => new(ChatRole.User, $"{message}") }; /// /// Builder for converting a Foundry workflow object-model YAML definition into a process. /// /// The type of the input message /// The path to the workflow. /// Configuration options for workflow execution. /// An optional function to transform the input message into a . /// public static Workflow Build( string workflowFile, DeclarativeWorkflowOptions options, Func? inputTransform = null) where TInput : notnull { using StreamReader yamlReader = File.OpenText(workflowFile); return Build(yamlReader, options, inputTransform); } /// /// Builds a workflow from the provided YAML definition. /// /// The type of the input message /// The reader that provides the workflow object model YAML. /// Configuration options for workflow execution. /// An optional function to transform the input message into a . /// The that corresponds with the YAML object model. public static Workflow Build( TextReader yamlReader, DeclarativeWorkflowOptions options, Func? inputTransform = null) where TInput : notnull { AdaptiveDialog workflowElement = ReadWorkflow(yamlReader); string rootId = WorkflowActionVisitor.Steps.Root(workflowElement); WorkflowFormulaState state = new(options.CreateRecalcEngine()); state.Initialize(workflowElement.WrapWithBot(), options.Configuration); DeclarativeWorkflowExecutor rootExecutor = new(rootId, options, state, message => inputTransform?.Invoke(message) ?? DefaultTransform(message)); WorkflowActionVisitor visitor = new(rootExecutor, state, options); WorkflowElementWalker walker = new(visitor); walker.Visit(workflowElement); return visitor.Complete(); } /// /// Generates source code (provider/executor scaffolding) for the workflow defined in the YAML file. /// /// The path to the workflow YAML file. /// The language to use for the generated code. /// Optional target namespace for the generated code. /// Optional prefix for generated workflow type. /// The generated source code representing the workflow. public static string Eject( string workflowFile, DeclarativeWorkflowLanguage workflowLanguage, string? workflowNamespace = null, string? workflowPrefix = null) { using StreamReader yamlReader = File.OpenText(workflowFile); return Eject(yamlReader, workflowLanguage, workflowNamespace, workflowPrefix); } /// /// Generates source code (provider/executor scaffolding) for the workflow defined in the provided YAML reader. /// /// The reader supplying the workflow YAML. /// The language to use for the generated code. /// Optional target namespace for the generated code. /// Optional prefix for generated workflow type. /// The generated source code representing the workflow. public static string Eject( TextReader yamlReader, DeclarativeWorkflowLanguage workflowLanguage, string? workflowNamespace = null, string? workflowPrefix = null) { if (workflowLanguage != DeclarativeWorkflowLanguage.CSharp) { throw new NotSupportedException($"Converting workflow to {workflowLanguage} is not currently supported."); } AdaptiveDialog workflowElement = ReadWorkflow(yamlReader); string rootId = WorkflowActionVisitor.Steps.Root(workflowElement); WorkflowTypeInfo typeInfo = workflowElement.WrapWithBot().Describe(); WorkflowTemplateVisitor visitor = new(rootId, typeInfo); WorkflowElementWalker walker = new(visitor); walker.Visit(workflowElement); return visitor.Complete(workflowNamespace, workflowPrefix); } private static AdaptiveDialog ReadWorkflow(TextReader yamlReader) { BotElement rootElement = YamlSerializer.Deserialize(yamlReader) ?? throw new DeclarativeModelException("Workflow undefined."); // "Workflow" is an alias for "AdaptiveDialog" if (rootElement is not AdaptiveDialog workflowElement) { throw new DeclarativeModelException($"Unsupported root element: {rootElement.GetType().Name}. Expected an {nameof(Workflow)}."); } return workflowElement; } }