Files
agent-framework/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/DeclarativeWorkflowBuilder.cs
T
Peter Ibekwe 5d8dd4ea4b .NET: [BREAKING] Remove Support for Code-Gen in Declarative Workflows (#6095)
* Removed

* Remove sample

* Remove orphaned code-gen related code path

* Remove remaining references to code gen.

---------

Co-authored-by: Chris Rickman <crickman@microsoft.com>
Co-authored-by: Chris <66376200+crickman@users.noreply.github.com>
2026-05-27 20:14:38 +00:00

103 lines
4.6 KiB
C#

// 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.Agents.ObjectModel;
using Microsoft.Agents.ObjectModel.Yaml;
using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI.Workflows.Declarative;
/// <summary>
/// Builder for converting a Foundry workflow object-model YAML definition into a process.
/// </summary>
public static class DeclarativeWorkflowBuilder
{
/// <summary>
/// Transforms the input message into a <see cref="ChatMessage"/> based on <see cref="object.ToString()"/>.
/// Also performs pass-through for <see cref="ChatMessage"/> input.
/// </summary>
/// <param name="message">The input message to transform.</param>
/// <returns>The transformed message (as <see cref="ChatMessage"/></returns>
public static ChatMessage DefaultTransform(object message) =>
message switch
{
ChatMessage chatMessage => chatMessage,
string stringMessage => new ChatMessage(ChatRole.User, stringMessage),
_ => new(ChatRole.User, $"{message}")
};
/// <summary>
/// Builder for converting a Foundry workflow object-model YAML definition into a process.
/// </summary>
/// <typeparam name="TInput">The type of the input message</typeparam>
/// <param name="workflowFile">The path to the workflow.</param>
/// <param name="options">Configuration options for workflow execution.</param>
/// <param name="inputTransform">An optional function to transform the input message into a <see cref="ChatMessage"/>.</param>
/// <returns></returns>
public static Workflow Build<TInput>(
string workflowFile,
DeclarativeWorkflowOptions options,
Func<TInput, ChatMessage>? inputTransform = null)
where TInput : notnull
{
using StreamReader yamlReader = File.OpenText(workflowFile);
return Build(yamlReader, options, inputTransform);
}
/// <summary>
/// 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>
/// <param name="options">Configuration options for workflow execution.</param>
/// <param name="inputTransform">An optional function to transform the input message into a <see cref="ChatMessage"/>.</param>
/// <returns>The <see cref="Workflow"/> that corresponds with the YAML object model.</returns>
/// <remarks>
/// The returned workflow's root executor accepts <typeparamref name="TInput"/>,
/// <see cref="ChatMessage"/>, <see cref="System.Collections.Generic.IEnumerable{T}"/> of
/// <see cref="ChatMessage"/>, <see cref="string"/>, and <see cref="TurnToken"/>. This
/// makes the workflow usable both for direct invocation and for hosting via
/// <see cref="WorkflowHostingExtensions.AsAIAgent(Workflow, string?, string?, string?, IWorkflowExecutionEnvironment?, bool, bool)"/>.
/// </remarks>
public static Workflow Build<TInput>(
TextReader yamlReader,
DeclarativeWorkflowOptions options,
Func<TInput, ChatMessage>? 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<TInput> 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();
}
private static AdaptiveDialog ReadWorkflow(TextReader yamlReader)
{
BotElement rootElement = YamlSerializer.Deserialize<BotElement>(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;
}
}