mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET Workflows - Code Generation for Declarative Workflow (#655)
* Notes * Readme typo * Update readme * Checkpoint * Namespace fix * Fix ID and namespace * Checkpoint * Verified * Comments * Isolate "Kit" * Address note: static * Checkpoint * Checkpoint "Executor<>" * Prefix and internal executors * Test passing * Cleanup * Rename "session" concept * Revert workflow debug * Fix template base / pragma * Tune system scope * Update dotnet/src/Microsoft.Agents.Workflows.Declarative/CodeGen/ResetVariableTemplate.tt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix empty template * Add validation for codegen ut * Fix test * Codegen baselines * Constant * Prep * Mark TODO * Fix * Namespace * One more * Update baselines * Checkpoint * Checkpoint * Checkpoint * fme * Checkpoint * Another step * Fixed up * Roslyn * Fix * More cleaning * Async * Fix * Enum checkpoint * Refine enum * Checkpoint * Sync templates * Checkpoint * Streamline * Pre-merge analyzer updates * Foreach * Placeholders * Checkpoint * Clean-up * Sample path resolution * Checkpoint * Checkpoint - Workflow Code Building * Validation * Test cleanup * Update test basline * Update test baseline * Fix DefaultTemplate usage * Validation checkpoint * Fix break/continue edges * Verify generated code builds * Fix merge * Fix build validation * Update template handling of literal string values. * Test for metadata case * Update baselines * Fix merge * Checkpoint * Checkpoint: Conditions * Invoke Agent Checkpoint * Namespace * Address code-analysis issues * Cross platform test support * Invoke agent checkpoint * Clean sample * Checkpoint: Agent Invoke Input Messages * Checkpoint - Passing * Checkpoint * Regenerate all template + port conversation fix * Checkpoint: Tests good * Fix test for unbuntu * Fix build command * Checkpoint - E2E * Test fix * Update integration tests * Fix merge * Update * Checkpoint !!! * Baby steps * Checkpoint * Checkpoint E2E !!! * So close... * Integrate test validation * Fix merge * Rebase tests * Namespace * Namespace * Test cleanup * Sample comment cleanup * Checkpoint: List conversion * Include these * CheckPoint: ParseValue * Namespace * Fix sampel * More namspace * Comments * Test updates * Test fix * Better build * Shared code * Sort solution * Fix build * Prune solution * One more * Conversion matrix * Final table conversion --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -107,7 +107,8 @@
|
||||
<!-- Symbols -->
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
<!-- Toolset -->
|
||||
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="4.12.0" />
|
||||
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="4.14.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -77,6 +77,8 @@
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/Workflows/Declarative/">
|
||||
<Project Path="samples/GettingStarted/Workflows/Declarative/DeclarativeWorkflow.csproj" />
|
||||
<Project Path="samples/GettingStarted/Workflows/DeclarativeCode/DeclarativeCode.csproj" />
|
||||
<Project Path="samples/GettingStarted/Workflows/DeclarativeEject/DeclarativeEject.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Samples/GettingStarted/Workflows/Declarative/Examples/">
|
||||
<File Path="../workflow-samples/DeepResearch.yaml" />
|
||||
@@ -271,7 +273,6 @@
|
||||
<Project Path="src/Microsoft.Agents.AI.Abstractions/Microsoft.Agents.AI.Abstractions.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.AzureAI/Microsoft.Agents.AI.AzureAI.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.CopilotStudio/Microsoft.Agents.AI.CopilotStudio.csproj" />
|
||||
|
||||
<Project Path="src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Hosting.A2A/Microsoft.Agents.AI.Hosting.A2A.csproj" />
|
||||
<Project Path="src/Microsoft.Agents.AI.Hosting/Microsoft.Agents.AI.Hosting.csproj" />
|
||||
|
||||
@@ -8,4 +8,7 @@
|
||||
<ItemGroup Condition="'$(InjectSharedIntegrationTestCode)' == 'true'">
|
||||
<Compile Include="$(MSBuildThisFileDirectory)\..\..\src\Shared\IntegrationTests\*.cs" LinkBase="Shared\IntegrationTests" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(InjectSharedBuildTestCode)' == 'true'">
|
||||
<Compile Include="$(MSBuildThisFileDirectory)\..\..\src\Shared\CodeTests\*.cs" LinkBase="Shared\CodeTests" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Workflows\Microsoft.Agents.AI.Workflows.csproj" />
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net9.0</TargetFrameworks>
|
||||
<ProjectsDebugTargetFrameworks>net9.0</ProjectsDebugTargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
|
||||
<NoWarn>$(NoWarn);CA1812</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<InjectSharedThrow>true</InjectSharedThrow>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,251 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Azure.AI.Agents.Persistent;
|
||||
using Azure.Identity;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Test.WorkflowProviders;
|
||||
|
||||
namespace Demo.DeclarativeCode;
|
||||
|
||||
/// <summary>
|
||||
/// HOW TO: Execute a declarative workflow that has been converted to code.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <b>Configuration</b>
|
||||
/// Define FOUNDRY_PROJECT_ENDPOINT as a user-secret or environment variable that
|
||||
/// points to your Foundry project endpoint.
|
||||
/// </remarks>
|
||||
internal sealed class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
Program program = new(args);
|
||||
await program.ExecuteAsync();
|
||||
}
|
||||
|
||||
private async Task ExecuteAsync()
|
||||
{
|
||||
// Use DeclarativeWorkflowBuilder to build a workflow based on a YAML file.
|
||||
DeclarativeWorkflowOptions options =
|
||||
new(new AzureAgentProvider(this.FoundryEndpoint, new AzureCliCredential()))
|
||||
{
|
||||
Configuration = this.Configuration
|
||||
};
|
||||
|
||||
// Use the generated provider to create a workflow instance.
|
||||
Workflow workflow = TestWorkflowProvider.CreateWorkflow<string>(options);
|
||||
|
||||
Notify("\nWORKFLOW: Starting...");
|
||||
|
||||
// Run the workflow, just like any other workflow
|
||||
string input = this.GetWorkflowInput();
|
||||
StreamingRun run = await InProcessExecution.StreamAsync(workflow, input);
|
||||
await this.MonitorWorkflowRunAsync(run);
|
||||
|
||||
Notify("\nWORKFLOW: Done!");
|
||||
}
|
||||
|
||||
private const string ConfigKeyFoundryEndpoint = "FOUNDRY_PROJECT_ENDPOINT";
|
||||
|
||||
private static readonly Dictionary<string, string> s_nameCache = [];
|
||||
private static readonly HashSet<string> s_fileCache = [];
|
||||
|
||||
private string? WorkflowInput { get; }
|
||||
private string FoundryEndpoint { get; }
|
||||
private PersistentAgentsClient FoundryClient { get; }
|
||||
private IConfiguration Configuration { get; }
|
||||
|
||||
private Program(string[] args)
|
||||
{
|
||||
this.WorkflowInput = ParseWorkflowInput(args);
|
||||
|
||||
this.Configuration = InitializeConfig();
|
||||
|
||||
this.FoundryEndpoint = this.Configuration[ConfigKeyFoundryEndpoint] ?? throw new InvalidOperationException($"Undefined configuration setting: {ConfigKeyFoundryEndpoint}");
|
||||
this.FoundryClient = new PersistentAgentsClient(this.FoundryEndpoint, new AzureCliCredential());
|
||||
}
|
||||
|
||||
private async Task MonitorWorkflowRunAsync(StreamingRun run)
|
||||
{
|
||||
string? messageId = null;
|
||||
|
||||
await foreach (WorkflowEvent evt in run.WatchStreamAsync().ConfigureAwait(false))
|
||||
{
|
||||
if (evt is ExecutorInvokedEvent executorInvoked)
|
||||
{
|
||||
Debug.WriteLine($"STEP ENTER #{executorInvoked.ExecutorId}");
|
||||
}
|
||||
else if (evt is ExecutorCompletedEvent executorComplete)
|
||||
{
|
||||
Debug.WriteLine($"STEP EXIT #{executorComplete.ExecutorId}");
|
||||
}
|
||||
else if (evt is ExecutorFailedEvent executorFailure)
|
||||
{
|
||||
Debug.WriteLine($"STEP ERROR #{executorFailure.ExecutorId}: {executorFailure.Data?.Message ?? "Unknown"}");
|
||||
}
|
||||
else if (evt is WorkflowErrorEvent workflowError)
|
||||
{
|
||||
Debug.WriteLine("WORKFLOW ERROR");
|
||||
}
|
||||
else if (evt is ConversationUpdateEvent invokeEvent)
|
||||
{
|
||||
Debug.WriteLine($"CONVERSATION: {invokeEvent.Data}");
|
||||
}
|
||||
else if (evt is AgentRunUpdateEvent streamEvent)
|
||||
{
|
||||
if (!string.Equals(messageId, streamEvent.Update.MessageId, StringComparison.Ordinal))
|
||||
{
|
||||
messageId = streamEvent.Update.MessageId;
|
||||
|
||||
if (messageId is not null)
|
||||
{
|
||||
string? agentId = streamEvent.Update.AuthorName;
|
||||
if (agentId is not null)
|
||||
{
|
||||
if (!s_nameCache.TryGetValue(agentId, out string? realName))
|
||||
{
|
||||
PersistentAgent agent = await this.FoundryClient.Administration.GetAgentAsync(agentId);
|
||||
s_nameCache[agentId] = agent.Name;
|
||||
realName = agent.Name;
|
||||
}
|
||||
agentId = realName;
|
||||
}
|
||||
agentId ??= nameof(ChatRole.Assistant);
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.Write($"\n{agentId.ToUpperInvariant()}:");
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
Console.WriteLine($" [{messageId}]");
|
||||
}
|
||||
}
|
||||
|
||||
ChatResponseUpdate? chatUpdate = streamEvent.Update.RawRepresentation as ChatResponseUpdate;
|
||||
switch (chatUpdate?.RawRepresentation)
|
||||
{
|
||||
case MessageContentUpdate messageUpdate:
|
||||
string? fileId = messageUpdate.ImageFileId ?? messageUpdate.TextAnnotation?.OutputFileId;
|
||||
if (fileId is not null && s_fileCache.Add(fileId))
|
||||
{
|
||||
BinaryData content = await this.FoundryClient.Files.GetFileContentAsync(fileId);
|
||||
await DownloadFileContentAsync(Path.GetFileName(messageUpdate.TextAnnotation?.TextToReplace ?? "response.png"), content);
|
||||
}
|
||||
break;
|
||||
}
|
||||
try
|
||||
{
|
||||
Console.ResetColor();
|
||||
Console.Write(streamEvent.Data);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
else if (evt is AgentRunResponseEvent messageEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine();
|
||||
if (messageEvent.Response.AgentId is null)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.WriteLine("ACTIVITY:");
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine(messageEvent.Response?.Text.Trim());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (messageEvent.Response.Usage is not null)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
Console.WriteLine($"[Tokens Total: {messageEvent.Response.Usage.TotalTokenCount}, Input: {messageEvent.Response.Usage.InputTokenCount}, Output: {messageEvent.Response.Usage.OutputTokenCount}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetWorkflowInput()
|
||||
{
|
||||
string? input = this.WorkflowInput;
|
||||
|
||||
try
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.DarkGreen;
|
||||
|
||||
Console.Write("\nINPUT: ");
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
Console.WriteLine(input);
|
||||
return input;
|
||||
}
|
||||
while (string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
input = Console.ReadLine();
|
||||
}
|
||||
|
||||
return input.Trim();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
private static string? ParseWorkflowInput(string[] args)
|
||||
{
|
||||
return args?.FirstOrDefault();
|
||||
}
|
||||
|
||||
// Load configuration from user-secrets
|
||||
private static IConfigurationRoot InitializeConfig() =>
|
||||
new ConfigurationBuilder()
|
||||
.AddUserSecrets(Assembly.GetExecutingAssembly())
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
private static void Notify(string message)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
try
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
|
||||
private static async ValueTask DownloadFileContentAsync(string filename, BinaryData content)
|
||||
{
|
||||
string filePath = Path.Combine(Path.GetTempPath(), Path.GetFileName(filename));
|
||||
filePath = Path.ChangeExtension(filePath, ".png");
|
||||
|
||||
await File.WriteAllBytesAsync(filePath, content.ToArray());
|
||||
|
||||
Process.Start(
|
||||
new ProcessStartInfo
|
||||
{
|
||||
FileName = "cmd.exe",
|
||||
Arguments = $"/C start {filePath}"
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net9.0</TargetFrameworks>
|
||||
<ProjectsDebugTargetFrameworks>net9.0</ProjectsDebugTargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
|
||||
<NoWarn>$(NoWarn);CA1812</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<InjectSharedThrow>true</InjectSharedThrow>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.Workflows.Declarative\Microsoft.Agents.AI.Workflows.Declarative.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative;
|
||||
|
||||
namespace Demo.DeclarativeEject;
|
||||
|
||||
/// <summary>
|
||||
/// HOW TO: Convert a workflow from a declartive (yaml based) definition to code.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <b>Usage</b>
|
||||
/// Provide the path to the workflow definition file as the first argument.
|
||||
/// All other arguments are intepreted as a queue of inputs.
|
||||
/// When no input is queued, interactive input is requested from the console.
|
||||
/// </remarks>
|
||||
internal sealed class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Program program = new(args);
|
||||
program.Execute();
|
||||
}
|
||||
|
||||
private void Execute()
|
||||
{
|
||||
// Read and parse the declarative workflow.
|
||||
Notify($"WORKFLOW: Parsing {Path.GetFullPath(this.WorkflowFile)}");
|
||||
|
||||
Stopwatch timer = Stopwatch.StartNew();
|
||||
|
||||
// Use DeclarativeWorkflowBuilder to generate code based on a YAML file.
|
||||
string code = DeclarativeWorkflowBuilder.Eject(this.WorkflowFile, DeclarativeWorkflowLanguage.CSharp, workflowNamespace: "Demo.DeclarativeCode", workflowPrefix: "Sample");
|
||||
|
||||
Notify($"\nWORKFLOW: Defined {timer.Elapsed}\n");
|
||||
|
||||
Console.WriteLine(code);
|
||||
}
|
||||
|
||||
private const string DefaultWorkflow = "HelloWorld.yaml";
|
||||
|
||||
private string WorkflowFile { get; }
|
||||
|
||||
private Program(string[] args)
|
||||
{
|
||||
this.WorkflowFile = ParseWorkflowFile(args);
|
||||
}
|
||||
|
||||
private static string ParseWorkflowFile(string[] args)
|
||||
{
|
||||
string workflowFile = args.FirstOrDefault() ?? DefaultWorkflow;
|
||||
|
||||
if (!File.Exists(workflowFile) && !Path.IsPathFullyQualified(workflowFile))
|
||||
{
|
||||
string? repoFolder = GetRepoFolder();
|
||||
if (repoFolder is not null)
|
||||
{
|
||||
workflowFile = Path.Combine(repoFolder, "workflow-samples", workflowFile);
|
||||
workflowFile = Path.ChangeExtension(workflowFile, ".yaml");
|
||||
}
|
||||
}
|
||||
|
||||
if (!File.Exists(workflowFile))
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to locate workflow: {Path.GetFullPath(workflowFile)}.");
|
||||
}
|
||||
|
||||
return workflowFile;
|
||||
|
||||
static string? GetRepoFolder()
|
||||
{
|
||||
DirectoryInfo? current = new(Directory.GetCurrentDirectory());
|
||||
|
||||
while (current is not null)
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(current.FullName, ".git")))
|
||||
{
|
||||
return current.FullName;
|
||||
}
|
||||
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Notify(string message)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
try
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal abstract class ActionTemplate : CodeTemplate, IModeledAction
|
||||
{
|
||||
public string Id { get; private set; } = string.Empty;
|
||||
|
||||
public string Name { get; private set; } = string.Empty;
|
||||
|
||||
public string ParentId { get; private set; } = string.Empty;
|
||||
|
||||
public bool UseAgentProvider { get; init; }
|
||||
|
||||
protected TAction Initialize<TAction>(TAction model) where TAction : DialogAction
|
||||
{
|
||||
this.Id = model.GetId();
|
||||
this.ParentId = model.GetParentId() ?? WorkflowActionVisitor.Steps.Root();
|
||||
this.Name = this.Id.FormatType();
|
||||
|
||||
return model;
|
||||
}
|
||||
}
|
||||
+2791
File diff suppressed because it is too large
Load Diff
+57
@@ -0,0 +1,57 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new message to the specified agent conversation
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
EvaluateStringExpression(this.Model.ConversationId, "conversationId", isNullable: true); #>
|
||||
ArgumentNullException.ThrowIfNull(conversationId, nameof(conversationId));
|
||||
ChatMessage newMessage = new(ChatRole.<#= FormatEnum(this.Model.Role, RoleMap) #>, [.. this.GetContentAsync(context).ToEnumerable()]) { AdditionalProperties = this.GetMetadata() };
|
||||
await agentProvider.CreateMessageAsync(conversationId, newMessage, cancellationToken).ConfigureAwait(false);<#
|
||||
AssignVariable(this.Message, "newMessage");
|
||||
#>
|
||||
return default;
|
||||
}
|
||||
|
||||
private async IAsyncEnumerable<AIContent> GetContentAsync(IWorkflowContext context)
|
||||
{<#
|
||||
int index = 0;
|
||||
foreach (AddConversationMessageContent content in this.Model.Content)
|
||||
{
|
||||
++index;
|
||||
EvaluateMessageTemplate(content.Value, $"contentValue{index}");
|
||||
AgentMessageContentType contentType = content.Type.Value;
|
||||
if (contentType == AgentMessageContentType.ImageUrl)
|
||||
{#>
|
||||
yield return new UriContent(contentValue, "image/*");<#
|
||||
}
|
||||
else if (contentType == AgentMessageContentType.ImageFile)
|
||||
{#>
|
||||
yield return new HostedFileContent(contentValue);<#
|
||||
}
|
||||
else
|
||||
{#>
|
||||
yield return new TextContent(contentValue<#= index #>);<#
|
||||
}
|
||||
}#>
|
||||
}
|
||||
|
||||
private AdditionalPropertiesDictionary? GetMetadata()
|
||||
{<#
|
||||
EvaluateRecordExpression<object>(this.Model.Metadata, "metadata"); #>
|
||||
|
||||
if (metadata is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AdditionalPropertiesDictionary(metadata);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Extensions.AI;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class AddConversationMessageTemplate
|
||||
{
|
||||
public AddConversationMessageTemplate(AddConversationMessage model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.Message = this.Model.Message?.Path;
|
||||
this.UseAgentProvider = true;
|
||||
}
|
||||
|
||||
public AddConversationMessage Model { get; }
|
||||
|
||||
public PropertyPath? Message { get; }
|
||||
|
||||
public const string DefaultRole = nameof(ChatRole.User);
|
||||
|
||||
public static readonly FrozenDictionary<AgentMessageRoleWrapper, string> RoleMap =
|
||||
new Dictionary<AgentMessageRoleWrapper, string>()
|
||||
{
|
||||
[AgentMessageRoleWrapper.Get(AgentMessageRole.User)] = nameof(ChatRole.User),
|
||||
[AgentMessageRoleWrapper.Get(AgentMessageRole.Agent)] = nameof(ChatRole.Assistant),
|
||||
}.ToFrozenDictionary();
|
||||
}
|
||||
+2711
File diff suppressed because it is too large
Load Diff
+18
@@ -0,0 +1,18 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Reset all the state for the targeted variable scope.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
EvaluateEnumExpression<VariablesToClearWrapper, string>(this.Model.Variables, "targetScopeName", ScopeMap, isNullable: true); #>
|
||||
await context.QueueClearScopeAsync(targetScopeName).ConfigureAwait(false);
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class ClearAllVariablesTemplate
|
||||
{
|
||||
public ClearAllVariablesTemplate(ClearAllVariables model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
}
|
||||
|
||||
public ClearAllVariables Model { get; }
|
||||
|
||||
public static readonly FrozenDictionary<VariablesToClearWrapper, string?> ScopeMap =
|
||||
new Dictionary<VariablesToClearWrapper, string?>()
|
||||
{
|
||||
[VariablesToClearWrapper.Get(VariablesToClear.AllGlobalVariables)] = VariableScopeNames.Global,
|
||||
[VariablesToClearWrapper.Get(VariablesToClear.ConversationScopedVariables)] = WorkflowFormulaState.DefaultScopeName,
|
||||
}.ToFrozenDictionary();
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal abstract class CodeTemplate
|
||||
{
|
||||
private StringBuilder? _generationEnvironmentField;
|
||||
private CompilerErrorCollection? _errorsField;
|
||||
private List<int>? _indentLengthsField;
|
||||
private bool _endsWithNewline;
|
||||
|
||||
private string CurrentIndentField { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public abstract string TransformText();
|
||||
|
||||
#region Object Model helpers
|
||||
|
||||
public static string VariableName(PropertyPath path) => Throw.IfNull(path.VariableName);
|
||||
public static string VariableScope(PropertyPath path) => Throw.IfNull(path.NamespaceAlias);
|
||||
|
||||
public static string FormatBoolValue(bool? value, bool defaultValue = false) =>
|
||||
value ?? defaultValue ? "true" : "false";
|
||||
|
||||
public static string FormatStringValue(string? value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return "null";
|
||||
}
|
||||
|
||||
if (value.Contains('\n') || value.Contains('\r'))
|
||||
{
|
||||
return @$"""""""{Environment.NewLine}{value}{Environment.NewLine}""""""";
|
||||
}
|
||||
|
||||
if (value.Contains('"') || value.Contains('\\'))
|
||||
{
|
||||
return @$"""""""{value}""""""";
|
||||
}
|
||||
|
||||
return @$"""{value}""";
|
||||
}
|
||||
|
||||
public static string FormatValue<TValue>(string? value)
|
||||
{
|
||||
if (typeof(TValue) == typeof(string))
|
||||
{
|
||||
return FormatStringValue(value);
|
||||
}
|
||||
|
||||
if (value is null)
|
||||
{
|
||||
return "null";
|
||||
}
|
||||
|
||||
if (typeof(TValue).IsEnum)
|
||||
{
|
||||
return $"{typeof(TValue).Name}.{value}";
|
||||
}
|
||||
|
||||
return $"{value}";
|
||||
}
|
||||
|
||||
public static string FormatDataValue(DataValue value) =>
|
||||
value switch
|
||||
{
|
||||
BlankDataValue => "null",
|
||||
BooleanDataValue booleanValue => FormatBoolValue(booleanValue.Value),
|
||||
FloatDataValue decimalValue => $"{decimalValue.Value}",
|
||||
NumberDataValue numberValue => $"{numberValue.Value}",
|
||||
DateDataValue dateValue => $"new DateTime({dateValue.Value.Ticks}, DateTimeKind.{dateValue.Value.Kind})",
|
||||
DateTimeDataValue datetimeValue => $"new DateTimeOffset({datetimeValue.Value.Ticks}, TimeSpan.FromTicks({datetimeValue.Value.Offset}))",
|
||||
TimeDataValue timeValue => $"TimeSpan.FromTicks({timeValue.Value.Ticks})",
|
||||
StringDataValue stringValue => FormatStringValue(stringValue.Value),
|
||||
OptionDataValue optionValue => @$"""{optionValue.Value}""",
|
||||
// Indenting is important here to make the generated code readable. Don't change it without testing the output.
|
||||
RecordDataValue recordValue =>
|
||||
$"""
|
||||
[
|
||||
{string.Join(",\n ", recordValue.Properties.Select(p => $"[\"{p.Key}\"] = {FormatDataValue(p.Value)}"))}
|
||||
]
|
||||
""",
|
||||
_ => throw new DeclarativeModelException($"Unable to format '{value.GetType().Name}'"),
|
||||
};
|
||||
|
||||
public static TTarget FormatEnum<TSource, TTarget>(TSource value, IDictionary<TSource, TTarget> map, TTarget? defaultValue = default)
|
||||
{
|
||||
if (map.TryGetValue(value, out TTarget? target))
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
if (defaultValue is null)
|
||||
{
|
||||
throw new DeclarativeModelException($"No default value suppied for '{typeof(TTarget).Name}'");
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static string GetTypeAlias<TValue>() => GetTypeAlias(typeof(TValue));
|
||||
|
||||
public static string GetTypeAlias(Type type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
Type t when t == typeof(bool) => "bool",
|
||||
Type t when t == typeof(byte) => "byte",
|
||||
Type t when t == typeof(sbyte) => "sbyte",
|
||||
Type t when t == typeof(char) => "char",
|
||||
Type t when t == typeof(decimal) => "decimal",
|
||||
Type t when t == typeof(double) => "double",
|
||||
Type t when t == typeof(float) => "float",
|
||||
Type t when t == typeof(int) => "int",
|
||||
Type t when t == typeof(uint) => "uint",
|
||||
Type t when t == typeof(long) => "long",
|
||||
Type t when t == typeof(ulong) => "ulong",
|
||||
Type t when t == typeof(nint) => "nint",
|
||||
Type t when t == typeof(nuint) => "nuint",
|
||||
Type t when t == typeof(short) => "short",
|
||||
Type t when t == typeof(ushort) => "ushort",
|
||||
Type t when t == typeof(string) => "string",
|
||||
Type t when t == typeof(object) => "object",
|
||||
_ => type.Name
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
/// <summary>
|
||||
/// The string builder that generation-time code is using to assemble generated output
|
||||
/// </summary>
|
||||
public StringBuilder GenerationEnvironment
|
||||
{
|
||||
get
|
||||
{
|
||||
return this._generationEnvironmentField ??= new StringBuilder();
|
||||
}
|
||||
set
|
||||
{
|
||||
this._generationEnvironmentField = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The error collection for the generation process
|
||||
/// </summary>
|
||||
public CompilerErrorCollection Errors => this._errorsField ??= [];
|
||||
|
||||
/// <summary>
|
||||
/// A list of the lengths of each indent that was added with PushIndent
|
||||
/// </summary>
|
||||
private List<int> indentLengths => this._indentLengthsField ??= [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current indent we use when adding lines to the output
|
||||
/// </summary>
|
||||
public string CurrentIndent
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.CurrentIndentField;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Current transformation session
|
||||
/// </summary>
|
||||
public virtual IDictionary<string, object>? Session { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Transform-time helpers
|
||||
|
||||
/// <summary>
|
||||
/// Write text directly into the generated output
|
||||
/// </summary>
|
||||
public void Write(string textToAppend)
|
||||
{
|
||||
if (string.IsNullOrEmpty(textToAppend))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// If we're starting off, or if the previous text ended with a newline,
|
||||
// we have to append the current indent first.
|
||||
if ((this.GenerationEnvironment.Length == 0)
|
||||
|| this._endsWithNewline)
|
||||
{
|
||||
this.GenerationEnvironment.Append(this.CurrentIndentField);
|
||||
this._endsWithNewline = false;
|
||||
}
|
||||
// Check if the current text ends with a newline
|
||||
if (textToAppend.EndsWith(Environment.NewLine, StringComparison.CurrentCulture))
|
||||
{
|
||||
this._endsWithNewline = true;
|
||||
}
|
||||
// This is an optimization. If the current indent is "", then we don't have to do any
|
||||
// of the more complex stuff further down.
|
||||
if (this.CurrentIndentField.Length == 0)
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend);
|
||||
return;
|
||||
}
|
||||
// Everywhere there is a newline in the text, add an indent after it
|
||||
textToAppend = textToAppend.Replace(Environment.NewLine, Environment.NewLine + this.CurrentIndentField);
|
||||
// If the text ends with a newline, then we should strip off the indent added at the very end
|
||||
// because the appropriate indent will be added when the next time Write() is called
|
||||
if (this._endsWithNewline)
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend, 0, textToAppend.Length - this.CurrentIndentField.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.GenerationEnvironment.Append(textToAppend);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write text directly into the generated output
|
||||
/// </summary>
|
||||
public void WriteLine(string textToAppend)
|
||||
{
|
||||
this.Write(textToAppend);
|
||||
this.GenerationEnvironment.AppendLine();
|
||||
this._endsWithNewline = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write formatted text directly into the generated output
|
||||
/// </summary>
|
||||
public void Write(string format, params object[] args)
|
||||
{
|
||||
this.Write(string.Format(CultureInfo.CurrentCulture, format, args));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write formatted text directly into the generated output
|
||||
/// </summary>
|
||||
public void WriteLine(string format, params object[] args)
|
||||
{
|
||||
this.WriteLine(string.Format(CultureInfo.CurrentCulture, format, args));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise an error
|
||||
/// </summary>
|
||||
public void Error(string message)
|
||||
{
|
||||
CompilerError error = new()
|
||||
{
|
||||
ErrorText = message
|
||||
};
|
||||
this.Errors.Add(error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise a warning
|
||||
/// </summary>
|
||||
public void Warning(string message)
|
||||
{
|
||||
CompilerError error = new()
|
||||
{
|
||||
ErrorText = message,
|
||||
IsWarning = true
|
||||
};
|
||||
error.ErrorText = message;
|
||||
error.IsWarning = true;
|
||||
this.Errors.Add(error);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increase the indent
|
||||
/// </summary>
|
||||
public void PushIndent(string indent)
|
||||
{
|
||||
if (indent is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(indent));
|
||||
}
|
||||
this.CurrentIndentField += indent;
|
||||
this.indentLengths.Add(indent.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the last indent that was added with PushIndent
|
||||
/// </summary>
|
||||
public string PopIndent()
|
||||
{
|
||||
string returnValue = string.Empty;
|
||||
if (this.indentLengths.Count > 0)
|
||||
{
|
||||
int indentLength = this.indentLengths[this.indentLengths.Count - 1];
|
||||
this.indentLengths.RemoveAt(this.indentLengths.Count - 1);
|
||||
if (indentLength > 0)
|
||||
{
|
||||
returnValue = this.CurrentIndentField.Substring(this.CurrentIndentField.Length - indentLength);
|
||||
this.CurrentIndentField = this.CurrentIndentField.Remove(this.CurrentIndentField.Length - indentLength);
|
||||
}
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove any indentation
|
||||
/// </summary>
|
||||
public void ClearIndent()
|
||||
{
|
||||
this.indentLengths.Clear();
|
||||
this.CurrentIndentField = string.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToString Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Utility class to produce culture-oriented representation of an object as a string.
|
||||
/// </summary>
|
||||
public sealed class ToStringInstanceHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// This is called from the compile/run appdomain to convert objects within an expression block to a string
|
||||
/// </summary>
|
||||
#pragma warning disable CA1822 // Required to be non-static for use in generated code
|
||||
public string ToStringWithCulture(object objectToConvert) => $"{objectToConvert}";
|
||||
#pragma warning restore CA1822
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to produce culture-oriented representation of an object as a string
|
||||
/// </summary>
|
||||
public ToStringInstanceHelper ToStringHelper { get; } = new();
|
||||
|
||||
#endregion
|
||||
}
|
||||
+2748
File diff suppressed because it is too large
Load Diff
+31
@@ -0,0 +1,31 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Conditional branching similar to an if / elseif / elseif / else chain.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
for (int index = 0; index < this.Model.Conditions.Length; ++index)
|
||||
{
|
||||
ConditionItem conditionItem = this.Model.Conditions[index];
|
||||
if (conditionItem.Condition is null)
|
||||
{
|
||||
continue; // Skip if no condition is defined
|
||||
}
|
||||
|
||||
EvaluateBoolExpression(conditionItem.Condition, $"condition{index}");#>
|
||||
if (condition<#= index #>)
|
||||
{
|
||||
return "<#= ConditionGroupExecutor.Steps.Item(this.Model, conditionItem)#>";
|
||||
}
|
||||
<#
|
||||
}
|
||||
#>
|
||||
return "<#= ConditionGroupExecutor.Steps.Else(this.Model)#>";
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class ConditionGroupTemplate
|
||||
{
|
||||
public ConditionGroupTemplate(ConditionGroup model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
}
|
||||
|
||||
public ConditionGroup Model { get; }
|
||||
}
|
||||
+2731
File diff suppressed because it is too large
Load Diff
+26
@@ -0,0 +1,26 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Copies one or more messages into the specified agent conversation.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
EvaluateStringExpression(this.Model.ConversationId, "conversationId", isNullable: true); #>
|
||||
ArgumentNullException.ThrowIfNull(conversationId, nameof(conversationId));<#
|
||||
EvaluateValueExpression<ChatMessage[]>(this.Model.Messages, "messages");
|
||||
#>
|
||||
if (messages is not null)
|
||||
{
|
||||
foreach (ChatMessage message in messages)
|
||||
{
|
||||
await agentProvider.CreateMessageAsync(conversationId, message, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class CopyConversationMessagesTemplate
|
||||
{
|
||||
public CopyConversationMessagesTemplate(CopyConversationMessages model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.UseAgentProvider = true;
|
||||
}
|
||||
|
||||
public CopyConversationMessages Model { get; }
|
||||
}
|
||||
+2721
File diff suppressed because it is too large
Load Diff
+17
@@ -0,0 +1,17 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Creates a new conversation and stores the identifier value to the "<#= this.Model.ConversationId #>" variable.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
string conversationId = await agentProvider.CreateConversationAsync(cancellationToken).ConfigureAwait(false);<#
|
||||
AssignVariable(this.ConversationId, "conversationId");
|
||||
#>
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class CreateConversationTemplate
|
||||
{
|
||||
public CreateConversationTemplate(CreateConversation model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.ConversationId = Throw.IfNull(this.Model.ConversationId);
|
||||
this.UseAgentProvider = true;
|
||||
}
|
||||
|
||||
public CreateConversation Model { get; }
|
||||
|
||||
public PropertyPath ConversationId { get; }
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version: 17.0.0.0
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
|
||||
{
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Class to produce the template output
|
||||
/// </summary>
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\DefaultTemplate.tt"
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")]
|
||||
internal partial class DefaultTemplate : ActionTemplate, IModeledAction
|
||||
{
|
||||
#line hidden
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public override string TransformText()
|
||||
{
|
||||
this.Write("\n");
|
||||
this.Write("\n");
|
||||
this.Write("\nDelegateExecutor ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\DefaultTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.InstanceVariable));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(" = new(id: \"");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\DefaultTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\", ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\DefaultTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootVariable));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(".Session");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\DefaultTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.Action is not null ? $", {this.Action}" : ""));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(");\n");
|
||||
return this.GenerationEnvironment.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate, IModeledAction" visibility="internal" #>
|
||||
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Interpreter" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
DelegateExecutor <#= this.InstanceVariable #> = new(id: "<#= this.Id #>", <#= this.RootVariable #>.Session<#= this.Action is not null ? $", {this.Action}" : "" #>);
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class DefaultTemplate
|
||||
{
|
||||
public DefaultTemplate(DialogAction model, string rootId, string? action = null)
|
||||
{
|
||||
this.Initialize(model);
|
||||
this.Action = action;
|
||||
this.InstanceVariable = this.Id.FormatName();
|
||||
this.RootVariable = rootId.FormatName();
|
||||
}
|
||||
|
||||
public string? Action { get; }
|
||||
public string InstanceVariable { get; }
|
||||
public string RootVariable { get; }
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version: 17.0.0.0
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Class to produce the template output
|
||||
/// </summary>
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EdgeTemplate.tt"
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")]
|
||||
internal partial class EdgeTemplate : CodeTemplate
|
||||
{
|
||||
#line hidden
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public override string TransformText()
|
||||
{
|
||||
this.Write("\n");
|
||||
this.Write("\n");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EdgeTemplate.tt"
|
||||
if (this.Condition is not null)
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n builder.AddEdge(");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EdgeTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.SourceId));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(", ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EdgeTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.TargetId));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(", (object? result) => ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EdgeTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.Condition));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(");");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EdgeTemplate.tt"
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n builder.AddEdge(");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EdgeTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.SourceId));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(", ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EdgeTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.TargetId));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(");");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EdgeTemplate.tt"
|
||||
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n");
|
||||
return this.GenerationEnvironment.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<#@ template language="C#" inherits="CodeTemplate" visibility="internal" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<# if (this.Condition is not null)
|
||||
{#>
|
||||
builder.AddEdge(<#= this.SourceId #>, <#= this.TargetId #>, (object? result) => <#= this.Condition #>);<#
|
||||
}
|
||||
else
|
||||
{#>
|
||||
builder.AddEdge(<#= this.SourceId #>, <#= this.TargetId #>);<#
|
||||
} #>
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class EdgeTemplate
|
||||
{
|
||||
public EdgeTemplate(string sourceId, string targetId, string? condition = null)
|
||||
{
|
||||
this.SourceId = sourceId.FormatName();
|
||||
this.TargetId = targetId.FormatName();
|
||||
this.Condition = condition;
|
||||
}
|
||||
|
||||
public string SourceId { get; }
|
||||
public string TargetId { get; }
|
||||
public string? Condition { get; }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Modify items in a list
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class EditTableV2Template
|
||||
{
|
||||
public EditTableV2Template(EditTableV2 model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
}
|
||||
|
||||
public EditTableV2 Model { get; }
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version: 17.0.0.0
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
|
||||
{
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Class to produce the template output
|
||||
/// </summary>
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EmptyTemplate.tt"
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")]
|
||||
internal partial class EmptyTemplate : CodeTemplate, IModeledAction
|
||||
{
|
||||
#line hidden
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public override string TransformText()
|
||||
{
|
||||
this.Write("\n");
|
||||
this.Write("\n");
|
||||
this.Write("\nDelegateExecutor ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EmptyTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.InstanceVariable));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(" = new(id: \"");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EmptyTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\", ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EmptyTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootVariable));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(".Session");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\EmptyTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.Action is not null ? $", {this.Action}" : ""));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(");\n");
|
||||
return this.GenerationEnvironment.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<#@ template language="C#" inherits="CodeTemplate, IModeledAction" visibility="internal" #>
|
||||
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Interpreter" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
DelegateExecutor <#= this.InstanceVariable #> = new(id: "<#= this.Id #>", <#= this.RootVariable #>.Session<#= this.Action is not null ? $", {this.Action}" : "" #>);
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class EmptyTemplate
|
||||
{
|
||||
public EmptyTemplate(string actionId, string rootId, string? action = null)
|
||||
{
|
||||
this.Id = actionId;
|
||||
this.Name = this.Id.FormatType();
|
||||
this.InstanceVariable = this.Id.FormatName();
|
||||
this.RootVariable = rootId.FormatName();
|
||||
this.Action = action;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string Name { get; }
|
||||
public string InstanceVariable { get; }
|
||||
public string RootVariable { get; }
|
||||
public string? Action { get; }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Loops over a list assignign the loop variable to "<#= this.Model.Value #>" variable.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
private int _index;
|
||||
private object[] _values = [];
|
||||
|
||||
public bool HasValue { get; private set; }
|
||||
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
this._index = 0;<#
|
||||
|
||||
EvaluateValueExpression(this.Model.Items, "evaluatedValue");#>
|
||||
|
||||
if (evaluatedValue == null)
|
||||
{
|
||||
this._values = [];
|
||||
this.HasValue = false;
|
||||
}
|
||||
else
|
||||
if (evaluatedValue is IEnumerable evaluatedList)
|
||||
{
|
||||
this._values = [.. evaluatedList];
|
||||
}
|
||||
else
|
||||
{
|
||||
this._values = [evaluatedValue];
|
||||
}
|
||||
|
||||
await this.ResetAsync(context, null, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
public async ValueTask TakeNextAsync(IWorkflowContext context, object? _, CancellationToken cancellationToken)
|
||||
{
|
||||
if (this.HasValue = this._index < this._values.Length)
|
||||
{
|
||||
object value = this._values[this._index];
|
||||
<#
|
||||
AssignVariable(this.Value, "value", tightFormat: true);
|
||||
|
||||
if (this.Index is not null)
|
||||
{
|
||||
AssignVariable(this.Index, "this._index", tightFormat: true);
|
||||
}
|
||||
#>
|
||||
|
||||
this._index++;
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask ResetAsync(IWorkflowContext context, object? _, CancellationToken cancellationToken)
|
||||
{<#
|
||||
AssignVariable(this.Value, "UnassignedValue.Instance", tightFormat: true);
|
||||
|
||||
if (this.Index is not null)
|
||||
{
|
||||
AssignVariable(this.Index, "UnassignedValue.Instance", tightFormat: true);
|
||||
}
|
||||
#>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class ForeachTemplate
|
||||
{
|
||||
public ForeachTemplate(Foreach model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.Index = this.Model.Index?.Path;
|
||||
this.Value = Throw.IfNull(this.Model.Value);
|
||||
}
|
||||
|
||||
public Foreach Model { get; }
|
||||
public PropertyPath? Index { get; }
|
||||
public PropertyPath Value { get; }
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version: 17.0.0.0
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
|
||||
{
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Class to produce the template output
|
||||
/// </summary>
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\InstanceTemplate.tt"
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")]
|
||||
internal partial class InstanceTemplate : CodeTemplate
|
||||
{
|
||||
#line hidden
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public override string TransformText()
|
||||
{
|
||||
this.Write("\n");
|
||||
this.Write("\n");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\InstanceTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.ExecutorType));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("Executor ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\InstanceTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.InstanceVariable));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(" = new(");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\InstanceTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootVariable));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(".Session");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\InstanceTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.HasProvider ? ", options.AgentProvider" : ""));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(");");
|
||||
return this.GenerationEnvironment.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<#@ template language="C#" inherits="CodeTemplate" visibility="internal" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#= this.ExecutorType #>Executor <#= this.InstanceVariable #> = new(<#= this.RootVariable #>.Session<#= this.HasProvider ? ", options.AgentProvider" : "" #>);
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class InstanceTemplate
|
||||
{
|
||||
public InstanceTemplate(string executorId, string rootId, bool hasProvider = false)
|
||||
{
|
||||
this.InstanceVariable = executorId.FormatName();
|
||||
this.ExecutorType = executorId.FormatType();
|
||||
this.RootVariable = rootId.FormatName();
|
||||
this.HasProvider = hasProvider;
|
||||
}
|
||||
|
||||
public string InstanceVariable { get; }
|
||||
public string ExecutorType { get; }
|
||||
public string RootVariable { get; }
|
||||
public bool HasProvider { get; }
|
||||
}
|
||||
+2748
File diff suppressed because it is too large
Load Diff
+43
@@ -0,0 +1,43 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Invokes an agent to process messages and return a response within a conversation context.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session, WorkflowAgentProvider agentProvider) : AgentExecutor(id: "<#= this.Id #>", session, agentProvider)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
EvaluateStringExpression(this.Model.Agent.Name, "agentName", isNullable: true);#>
|
||||
|
||||
if (string.IsNullOrWhiteSpace(agentName))
|
||||
{
|
||||
throw new InvalidOperationException($"Agent name must be defined: {this.Id}");
|
||||
}
|
||||
<#
|
||||
EvaluateStringExpression(this.Model.ConversationId, "conversationId", isNullable: true);
|
||||
EvaluateBoolExpression(this.Model.Output?.AutoSend, "autoSend", defaultValue: true);
|
||||
EvaluateMessageTemplate(this.Model.Input?.AdditionalInstructions, "additionalInstructions");
|
||||
EvaluateListExpression<ChatMessage>(this.Model.Input?.Messages, "inputMessages");#>
|
||||
|
||||
AgentRunResponse agentResponse =
|
||||
await InvokeAgentAsync(
|
||||
context,
|
||||
agentName,
|
||||
conversationId,
|
||||
autoSend,
|
||||
additionalInstructions,
|
||||
inputMessages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (autoSend)
|
||||
{
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, agentResponse)).ConfigureAwait(false);
|
||||
}
|
||||
<#
|
||||
AssignVariable(this.Messages, "agentResponse.Messages"); #>
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class InvokeAzureAgentTemplate
|
||||
{
|
||||
public InvokeAzureAgentTemplate(InvokeAzureAgent model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.Messages = this.Model.Output?.Messages?.Path;
|
||||
this.UseAgentProvider = true;
|
||||
}
|
||||
|
||||
public InvokeAzureAgent Model { get; }
|
||||
|
||||
public PropertyPath? Messages { get; }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Parses a string or untyped value to the provided data type. When the input is a string, it will be treated as JSON.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
VariableType targetType = <#= this.GetVariableType() #>;<#
|
||||
if (this.Model.Value.IsVariableReference && this.Model.Value.VariableReference.SegmentCount == 2)
|
||||
{#>
|
||||
object? parsedValue = await context.ConvertValueAsync(targetType, key: "<#= this.Model.Value.VariableReference.VariableName #>", scopeName: "<#= this.Model.Value.VariableReference.NamespaceAlias #>", cancellationToken).ConfigureAwait(false);<#
|
||||
}
|
||||
else if (this.Model.Value.IsVariableReference)
|
||||
{#>
|
||||
object? parsedValue = await context.ConvertValueAsync(targetType, <#= FormatStringValue(this.Model.Value.VariableReference.ToString()) #>, cancellationToken).ConfigureAwait(false);<#
|
||||
}
|
||||
else
|
||||
{#>
|
||||
object? parsedValue = await context.ConvertValueAsync(targetType, <#= FormatStringValue(this.Model.Value.ExpressionText) #>, cancellationToken).ConfigureAwait(false);<#
|
||||
}
|
||||
AssignVariable(this.Variable, "parsedValue"); #>
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class ParseValueTemplate
|
||||
{
|
||||
public ParseValueTemplate(ParseValue model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.Variable = Throw.IfNull(this.Model.Variable);
|
||||
}
|
||||
|
||||
public ParseValue Model { get; }
|
||||
public PropertyPath Variable { get; }
|
||||
|
||||
private string GetVariableType()
|
||||
{
|
||||
return GetVariableType(this.Model.ValueType);
|
||||
|
||||
static string GetVariableType(DataType? dataType) =>
|
||||
dataType switch
|
||||
{
|
||||
null => "null",
|
||||
StringDataType => "typeof(string)",
|
||||
BooleanDataType => "typeof(bool)",
|
||||
FloatDataType => "typeof(double)",
|
||||
NumberDataType => "typeof(decimal)",
|
||||
DateTimeDataType => "typeof(DateTime)",
|
||||
DateDataType => "typeof(DateTime)",
|
||||
TimeDataType => "typeof(TimeSpan)",
|
||||
RecordDataType recordType => $"\nVariableType.Record(\n{string.Join(",\n ", recordType.Properties.Select(property => @$"( ""{property.Key}"", {GetVariableType(property.Value.Type)} )"))})",
|
||||
TableDataType tableType => $"\nVariableType.Record(\n{string.Join(",\n ", tableType.Properties.Select(property => @$"( ""{property.Key}"", {GetVariableType(property.Value.Type)} )"))})",
|
||||
_ => throw new DeclarativeModelException($"Unsupported data type: {dataType}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version: 17.0.0.0
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
|
||||
{
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Class to produce the template output
|
||||
/// </summary>
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")]
|
||||
internal partial class ProviderTemplate : CodeTemplate
|
||||
{
|
||||
#line hidden
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public override string TransformText()
|
||||
{
|
||||
this.Write("\n");
|
||||
this.Write("\n");
|
||||
this.Write(@"
|
||||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
#nullable enable
|
||||
#pragma warning disable IDE0005 // Extra using directive is ok.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Extensions.AI;
|
||||
");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
|
||||
if (this.Namespace is not null)
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\nnamespace ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.Namespace));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(";\n");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(@"
|
||||
/// <summary>
|
||||
/// This class provides a factory method to create a <see cref=""Workflow"" /> instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The workflow defined here was generated from a declarative workflow definition.
|
||||
/// Declarative workflows utilize Power FX for defining conditions and expressions.
|
||||
/// To learn more about Power FX, see:
|
||||
/// https://learn.microsoft.com/power-platform/power-fx/formula-reference-copilot-studio
|
||||
/// </remarks>
|
||||
public static class ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.Prefix ?? string.Empty));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("WorkflowProvider\n{");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
|
||||
foreach (string executor in ByLine(this.Executors, formatGroup: true))
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(executor));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(@"
|
||||
public static Workflow CreateWorkflow<TInput>(
|
||||
DeclarativeWorkflowOptions options,
|
||||
Func<TInput, ChatMessage>? inputTransform = null)
|
||||
where TInput : notnull
|
||||
{
|
||||
// Create root executor to initialize the workflow.
|
||||
inputTransform ??= (message) => DeclarativeWorkflowBuilder.DefaultTransform(message);
|
||||
");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootExecutorType));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("Executor<TInput> ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootInstance));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(" = new(options, inputTransform);");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
|
||||
|
||||
// Create executor instances
|
||||
foreach (string instance in ByLine(this.Instances))
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(instance));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n\n // Define the workflow builder\n WorkflowBuilder builder = new(");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.RootInstance));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(");\n\n // Connect executors");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
|
||||
foreach (string edge in ByLine(this.Edges))
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(edge));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\ProviderTemplate.tt"
|
||||
|
||||
}
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n\n // Build the workflow\n return builder.Build();\n }\n}\n");
|
||||
return this.GenerationEnvironment.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<#@ template language="C#" inherits="CodeTemplate" visibility="internal" #>
|
||||
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Extensions" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
#nullable enable
|
||||
#pragma warning disable IDE0005 // Extra using directive is ok.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI;
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Extensions.AI;
|
||||
<#
|
||||
if (this.Namespace is not null)
|
||||
{#>
|
||||
namespace <#= this.Namespace #>;
|
||||
<#
|
||||
}
|
||||
#>
|
||||
/// <summary>
|
||||
/// This class provides a factory method to create a <see cref="Workflow" /> instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The workflow defined here was generated from a declarative workflow definition.
|
||||
/// Declarative workflows utilize Power FX for defining conditions and expressions.
|
||||
/// To learn more about Power FX, see:
|
||||
/// https://learn.microsoft.com/power-platform/power-fx/formula-reference-copilot-studio
|
||||
/// </remarks>
|
||||
public static class <#= this.Prefix ?? string.Empty #>WorkflowProvider
|
||||
{<#
|
||||
foreach (string executor in ByLine(this.Executors, formatGroup: true))
|
||||
{ #>
|
||||
<#= executor #><#
|
||||
}
|
||||
#>
|
||||
public static Workflow CreateWorkflow<TInput>(
|
||||
DeclarativeWorkflowOptions options,
|
||||
Func<TInput, ChatMessage>? inputTransform = null)
|
||||
where TInput : notnull
|
||||
{
|
||||
// Create root executor to initialize the workflow.
|
||||
inputTransform ??= (message) => DeclarativeWorkflowBuilder.DefaultTransform(message);
|
||||
<#= this.RootExecutorType #>Executor<TInput> <#= this.RootInstance #> = new(options, inputTransform);<#
|
||||
|
||||
// Create executor instances
|
||||
foreach (string instance in ByLine(this.Instances))
|
||||
{ #>
|
||||
<#= instance #><#
|
||||
}#>
|
||||
|
||||
// Define the workflow builder
|
||||
WorkflowBuilder builder = new(<#= this.RootInstance #>);
|
||||
|
||||
// Connect executors<#
|
||||
foreach (string edge in ByLine(this.Edges))
|
||||
{ #>
|
||||
<#= edge #><#
|
||||
}
|
||||
#>
|
||||
|
||||
// Build the workflow
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class ProviderTemplate
|
||||
{
|
||||
public ProviderTemplate(
|
||||
string workflowId,
|
||||
IEnumerable<string> executors,
|
||||
IEnumerable<string> instances,
|
||||
IEnumerable<string> edges)
|
||||
{
|
||||
this.Executors = executors;
|
||||
this.Instances = instances;
|
||||
this.Edges = edges;
|
||||
this.RootInstance = workflowId.FormatName();
|
||||
this.RootExecutorType = workflowId.FormatType();
|
||||
}
|
||||
|
||||
public string? Namespace { get; init; }
|
||||
public string? Prefix { get; init; }
|
||||
|
||||
public string RootInstance { get; }
|
||||
public string RootExecutorType { get; }
|
||||
|
||||
public IEnumerable<string> Executors { get; }
|
||||
public IEnumerable<string> Instances { get; }
|
||||
public IEnumerable<string> Edges { get; }
|
||||
|
||||
public static IEnumerable<string> ByLine(IEnumerable<string> templates, bool formatGroup = false)
|
||||
{
|
||||
foreach (string template in templates)
|
||||
{
|
||||
foreach (string line in template.ByLine())
|
||||
{
|
||||
yield return line;
|
||||
}
|
||||
|
||||
if (formatGroup)
|
||||
{
|
||||
yield return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Request input.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class QuestionTemplate
|
||||
{
|
||||
public QuestionTemplate(Question model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
}
|
||||
|
||||
public Question Model { get; }
|
||||
}
|
||||
+2717
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Resets the value of the "<#= this.Model.Variable #>" variable, potentially causing re-evaluation
|
||||
/// of the default value, question or action that provides the value to this variable.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
AssignVariable(this.Variable, "UnassignedValue.Instance"); #>
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class ResetVariableTemplate
|
||||
{
|
||||
public ResetVariableTemplate(ResetVariable model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.Variable = Throw.IfNull(this.Model.Variable);
|
||||
}
|
||||
|
||||
public ResetVariable Model { get; }
|
||||
|
||||
public PropertyPath Variable { get; }
|
||||
}
|
||||
+2722
File diff suppressed because it is too large
Load Diff
+20
@@ -0,0 +1,20 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Retrieves a list of messages from an agent conversation.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
EvaluateStringExpression(this.Model.ConversationId, "conversationId");
|
||||
EvaluateStringExpression(this.Model.MessageId, "messageId"); #>
|
||||
ChatMessage message = await agentProvider.GetMessageAsync(conversationId, messageId, cancellationToken).ConfigureAwait(false);<#
|
||||
AssignVariable(this.Model.Message, "message");
|
||||
#>
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class RetrieveConversationMessageTemplate
|
||||
{
|
||||
public RetrieveConversationMessageTemplate(RetrieveConversationMessage model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.UseAgentProvider = true;
|
||||
}
|
||||
|
||||
public RetrieveConversationMessage Model { get; }
|
||||
}
|
||||
+2732
File diff suppressed because it is too large
Load Diff
+30
@@ -0,0 +1,30 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>f
|
||||
/// <summary>
|
||||
/// Retrieves a specific message from an agent conversation.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
EvaluateStringExpression(this.Model.ConversationId, "conversationId");
|
||||
EvaluateIntExpression(this.Model.Limit, "limit");
|
||||
EvaluateStringExpression(this.Model.MessageAfter, "after", isNullable: true);
|
||||
EvaluateStringExpression(this.Model.MessageBefore, "before", isNullable: true);
|
||||
EvaluateEnumExpression<AgentMessageSortOrderWrapper, bool>(this.Model.SortOrder, "newestFirst", SortMap, defaultValue: DefaultSort); #>
|
||||
ChatMessage messages =
|
||||
await agentProvider.GetMessageAsync(
|
||||
converationId,
|
||||
limit,
|
||||
after,
|
||||
before,
|
||||
newestFirst,
|
||||
cancellationToken).ConfigureAwait(false);<#
|
||||
AssignVariable(this.Model.Messages, "messages");
|
||||
#>
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class RetrieveConversationMessagesTemplate
|
||||
{
|
||||
public RetrieveConversationMessagesTemplate(RetrieveConversationMessages model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.UseAgentProvider = true;
|
||||
}
|
||||
|
||||
public RetrieveConversationMessages Model { get; }
|
||||
|
||||
public const string DefaultSort = "false";
|
||||
|
||||
public static readonly FrozenDictionary<AgentMessageSortOrderWrapper, string> SortMap =
|
||||
new Dictionary<AgentMessageSortOrderWrapper, string>()
|
||||
{
|
||||
[AgentMessageSortOrderWrapper.Get(AgentMessageSortOrder.NewestFirst)] = "true",
|
||||
[AgentMessageSortOrderWrapper.Get(AgentMessageSortOrder.OldestFirst)] = "false",
|
||||
}.ToFrozenDictionary();
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version: 17.0.0.0
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
// ------------------------------------------------------------------------------
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen
|
||||
{
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Class to produce the template output
|
||||
/// </summary>
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")]
|
||||
internal partial class RootTemplate : CodeTemplate, IModeledAction
|
||||
{
|
||||
#line hidden
|
||||
/// <summary>
|
||||
/// Create the template output
|
||||
/// </summary>
|
||||
public override string TransformText()
|
||||
{
|
||||
this.Write("\n");
|
||||
this.Write("\n");
|
||||
this.Write("\n");
|
||||
this.Write("\n");
|
||||
this.Write("\n/// <summary>\n/// The root executor for a declarative workflow.\n/// </summary>\ni" +
|
||||
"nternal sealed class ");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.TypeName));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("Executor<TInput>(\n DeclarativeWorkflowOptions options,\n Func<TInput, ChatMe" +
|
||||
"ssage> inputTransform) :\n RootExecutor<TInput>(\"");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(this.Id));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\", options, inputTransform)\n where TInput : notnull\n{\n protected override a" +
|
||||
"sync ValueTask ExecuteAsync(TInput message, IWorkflowContext context, Cancellati" +
|
||||
"onToken cancellationToken)\n {");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
|
||||
if (this.TypeInfo.EnvironmentVariables.Count > 0)
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n // Set environment variables\n await this.InitializeEnvironmentAsy" +
|
||||
"nc(\n context,");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
|
||||
int index = this.TypeInfo.EnvironmentVariables.Count - 1;
|
||||
foreach (string variableName in this.TypeInfo.EnvironmentVariables)
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n \"");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(variableName));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\"");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(index > 0 ? "," : ""));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
|
||||
--index;
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write(").ConfigureAwait(false);\n");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
}
|
||||
|
||||
if (this.TypeInfo.UserVariables.Count > 0)
|
||||
{
|
||||
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n // Initialize variables");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
|
||||
foreach (VariableInformationDiagnostic variableInfo in this.TypeInfo.UserVariables)
|
||||
{
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n await context.QueueStateUpdateAsync(\"");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(variableInfo.Path.VariableName));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\", UnassignedValue.Instance, \"");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
this.Write(this.ToStringHelper.ToStringWithCulture(variableInfo.Path.NamespaceAlias));
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\").ConfigureAwait(false);");
|
||||
|
||||
#line 1 "C:\Users\crickman\source\repos\af5\dotnet\src\Microsoft.Agents.AI.Workflows.Declarative\CodeGen\RootTemplate.tt"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
this.Write("\n }\n}\n");
|
||||
return this.GenerationEnvironment.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<#@ template language="C#" inherits="CodeTemplate, IModeledAction" visibility="internal" #>
|
||||
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Extensions" #>
|
||||
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Interpreter" #>
|
||||
<#@ import namespace="Microsoft.Bot.ObjectModel" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
/// <summary>
|
||||
/// The root executor for a declarative workflow.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.TypeName #>Executor<TInput>(
|
||||
DeclarativeWorkflowOptions options,
|
||||
Func<TInput, ChatMessage> inputTransform) :
|
||||
RootExecutor<TInput>("<#= this.Id #>", options, inputTransform)
|
||||
where TInput : notnull
|
||||
{
|
||||
protected override async ValueTask ExecuteAsync(TInput message, IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
if (this.TypeInfo.EnvironmentVariables.Count > 0)
|
||||
{ #>
|
||||
// Set environment variables
|
||||
await this.InitializeEnvironmentAsync(
|
||||
context,<#
|
||||
int index = this.TypeInfo.EnvironmentVariables.Count - 1;
|
||||
foreach (string variableName in this.TypeInfo.EnvironmentVariables)
|
||||
{#>
|
||||
"<#= variableName #>"<#= index > 0 ? "," : "" #><#
|
||||
--index;
|
||||
}#>).ConfigureAwait(false);
|
||||
<#}
|
||||
|
||||
if (this.TypeInfo.UserVariables.Count > 0)
|
||||
{
|
||||
#>
|
||||
// Initialize variables<#
|
||||
foreach (VariableInformationDiagnostic variableInfo in this.TypeInfo.UserVariables)
|
||||
{#>
|
||||
await context.QueueStateUpdateAsync("<#= variableInfo.Path.VariableName #>", UnassignedValue.Instance, "<#= variableInfo.Path.NamespaceAlias #>").ConfigureAwait(false);<#
|
||||
}
|
||||
}#>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class RootTemplate
|
||||
{
|
||||
internal RootTemplate(
|
||||
string workflowId,
|
||||
WorkflowTypeInfo typeInfo)
|
||||
{
|
||||
this.Id = workflowId;
|
||||
this.TypeInfo = typeInfo;
|
||||
this.TypeName = workflowId.FormatType();
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public WorkflowTypeInfo TypeInfo { get; }
|
||||
public string TypeName { get; }
|
||||
}
|
||||
+2762
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Formats a message template and sends an activity event.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{ <#
|
||||
if (this.Model.Activity is MessageActivityTemplate messageActivity)
|
||||
{ #>
|
||||
string activityText =
|
||||
await context.FormatTemplateAsync( <#
|
||||
foreach (TemplateLine line in messageActivity.Text)
|
||||
{ #>
|
||||
"""<#
|
||||
foreach (string text in line.ToTemplateString().ByLine())
|
||||
{ #>
|
||||
<#= text #><#
|
||||
} #>
|
||||
"""<#
|
||||
}
|
||||
#>
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);<#
|
||||
} #>
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class SendActivityTemplate
|
||||
{
|
||||
public SendActivityTemplate(SendActivity model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
}
|
||||
|
||||
public SendActivity Model { get; }
|
||||
}
|
||||
+2727
File diff suppressed because it is too large
Load Diff
+27
@@ -0,0 +1,27 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Assigns an evaluated expression, other variable, or literal value to one or more variables.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<# int index = 0;
|
||||
foreach (var assignment in this.Model.Assignments)
|
||||
{
|
||||
// Separate assigments with a blank line for readability
|
||||
if (index > 0)
|
||||
{#>
|
||||
<#
|
||||
}
|
||||
++index;
|
||||
EvaluateValueExpression(assignment.Value, $"evaluatedValue{index}");
|
||||
AssignVariable(assignment.Variable, $"evaluatedValue{index}");
|
||||
}
|
||||
#>
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class SetMultipleVariablesTemplate
|
||||
{
|
||||
public SetMultipleVariablesTemplate(SetMultipleVariables model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
}
|
||||
|
||||
public SetMultipleVariables Model { get; }
|
||||
}
|
||||
+2716
File diff suppressed because it is too large
Load Diff
+16
@@ -0,0 +1,16 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Assigns an evaluated message template to the "<#= this.Model.Variable #>" variable.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
EvaluateMessageTemplate(this.Model.Value, "textValue");
|
||||
AssignVariable(this.Variable, "textValue"); #>
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class SetTextVariableTemplate
|
||||
{
|
||||
public SetTextVariableTemplate(SetTextVariable model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.Variable = Throw.IfNull(this.Model.Variable);
|
||||
}
|
||||
|
||||
public SetTextVariable Model { get; }
|
||||
|
||||
public PropertyPath Variable { get; }
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
<#@ template language="C#" inherits="ActionTemplate" visibility="internal" #>
|
||||
<#@ output extension=".cs" #>
|
||||
<#@ assembly name="System.Core" #>
|
||||
<#@ include file="Snippets/Index.tt" once="true" #>
|
||||
/// <summary>
|
||||
/// Assigns an evaluated expression, other variable, or literal value to the "<#= this.Model.Variable #>" variable.
|
||||
/// </summary>
|
||||
internal sealed class <#= this.Name #>Executor(FormulaSession session) : ActionExecutor(id: "<#= this.Id #>", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{<#
|
||||
EvaluateValueExpression(this.Model.Value, "evaluatedValue");
|
||||
AssignVariable(this.Variable, "evaluatedValue"); #>
|
||||
return default;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen;
|
||||
|
||||
internal partial class SetVariableTemplate
|
||||
{
|
||||
internal SetVariableTemplate(SetVariable model)
|
||||
{
|
||||
this.Model = this.Initialize(model);
|
||||
this.Variable = Throw.IfNull(this.Model.Variable);
|
||||
}
|
||||
|
||||
public SetVariable Model { get; }
|
||||
public PropertyPath Variable { get; }
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<#+
|
||||
void AssignVariable(PropertyPath targetVariable, string valueVariable, bool tightFormat = false)
|
||||
{
|
||||
if (targetVariable is not null)
|
||||
{#>
|
||||
await context.QueueStateUpdateAsync(key: "<#= VariableName(targetVariable) #>", value: <#= valueVariable #>, scopeName: "<#= VariableScope(targetVariable) #>").ConfigureAwait(false);<#+
|
||||
if (!tightFormat)
|
||||
{#>
|
||||
<#+}
|
||||
}
|
||||
}
|
||||
#>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<#+
|
||||
void EvaluateBoolExpression(BoolExpression expression, string targetVariable, bool defaultValue = false)
|
||||
{
|
||||
if (expression is null)
|
||||
{#>
|
||||
bool <#= targetVariable #> = <#= FormatBoolValue(defaultValue) #>;<#+
|
||||
}
|
||||
else if (expression.IsLiteral)
|
||||
{#>
|
||||
bool <#= targetVariable #> = <#= FormatBoolValue(expression.LiteralValue) #>;<#+
|
||||
}
|
||||
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
|
||||
{#>
|
||||
bool <#= targetVariable #> = await context.ReadStateAsync<bool>(key: "<#= expression.VariableReference.VariableName #>", scopeName: "<#= expression.VariableReference.NamespaceAlias #>").ConfigureAwait(false);<#+
|
||||
}
|
||||
else if (expression.IsVariableReference)
|
||||
{#>
|
||||
bool <#= targetVariable #> = await context.EvaluateValueAsync<bool>>(<#= FormatStringValue(expression.VariableReference.ToString()) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
else
|
||||
{#>
|
||||
bool <#= targetVariable #> = await context.EvaluateValueAsync<bool>(<#= FormatStringValue(expression.ExpressionText) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
}
|
||||
#>
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<#+
|
||||
void EvaluateEnumExpression<TWrapper, TValue>(
|
||||
EnumExpression<TWrapper> expression,
|
||||
string targetVariable,
|
||||
IDictionary<TWrapper, string> resultMap,
|
||||
string defaultValue = null,
|
||||
bool qualifyResult = false,
|
||||
bool isNullable = false)
|
||||
where TWrapper : EnumWrapper
|
||||
{
|
||||
string resultType = $"{GetTypeAlias<TValue>()}{(isNullable ? "?" : "")}";
|
||||
if (expression is null)
|
||||
{#>
|
||||
<#= resultType #> <#= targetVariable #> = <#= FormatValue<TValue>(defaultValue) #>;<#+
|
||||
}
|
||||
else if (expression.IsLiteral)
|
||||
{
|
||||
resultMap.TryGetValue(expression.LiteralValue, out string resultValue);
|
||||
if (qualifyResult)
|
||||
{#>
|
||||
<#= resultType #> <#= targetVariable #> = <#= GetTypeAlias<TValue>() #>.<#= resultValue #>;<#+
|
||||
}
|
||||
else
|
||||
{#>
|
||||
<#= resultType #> <#= targetVariable #> = <#= FormatValue<TValue>(resultValue) #>;<#+
|
||||
}
|
||||
}
|
||||
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
|
||||
{#>
|
||||
<#= resultType #> <#= targetVariable #> = await context.ReadStateAsync<<#= resultType #>>(key: "<#= expression.VariableReference.VariableName #>", scopeName: "<#= expression.VariableReference.NamespaceAlias #>").ConfigureAwait(false);<#+
|
||||
}
|
||||
else if (expression.IsVariableReference)
|
||||
{#>
|
||||
<#= resultType #>? <#= targetVariable #> = await context.EvaluateValueAsync<<#= resultType #>>(<#= FormatStringValue(expression.VariableReference.ToString()) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
else
|
||||
{#>
|
||||
<#= resultType #> <#= targetVariable #> = await context.EvaluateValueAsync<<#= resultType #>>(<#= FormatStringValue(expression.ExpressionText) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
}
|
||||
#>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<#+
|
||||
void EvaluateIntExpression(IntExpression expression, string targetVariable, bool isNullable = false)
|
||||
{
|
||||
string typeName = isNullable ? "int?" : "int";
|
||||
if (expression is null)
|
||||
{#>
|
||||
<#= typeName #> <#= targetVariable #> = <#= isNullable ? "null" : "0" #>;<#+
|
||||
}
|
||||
else if (expression.IsLiteral)
|
||||
{#>
|
||||
<#= typeName #> <#= targetVariable #> = <#= expression.LiteralValue #>;<#+
|
||||
}
|
||||
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
|
||||
{#>
|
||||
<#= typeName #> <#= targetVariable #> = await context.ReadStateAsync<int>(key: "<#= expression.VariableReference.VariableName #>", scopeName: "<#= expression.VariableReference.NamespaceAlias #>").ConfigureAwait(false);<#+
|
||||
}
|
||||
else if (expression.IsVariableReference)
|
||||
{#>
|
||||
<#= typeName #>? <#= targetVariable #> = await context.EvaluateValueAsync<<#= typeName #>>(<#= FormatStringValue(expression.VariableReference.ToString()) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
else
|
||||
{#>
|
||||
<#= typeName #> <#= targetVariable #> = await context.EvaluateValueAsync<<#= typeName #>>(<#= FormatStringValue(expression.ExpressionText) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
}
|
||||
#>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<#+
|
||||
void EvaluateListExpression<TElement>(ValueExpression expression, string targetVariable)
|
||||
{
|
||||
string typeName = GetTypeAlias<TElement>();
|
||||
if (expression is null)
|
||||
{#>
|
||||
IList<<#= typeName #>>? <#= targetVariable #> = null;<#+
|
||||
}
|
||||
else if (expression.IsLiteral)
|
||||
{#>
|
||||
IList<<#= typeName #>>? <#= targetVariable #> = <#= FormatDataValue(expression.LiteralValue) #>;<#+
|
||||
}
|
||||
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
|
||||
{#>
|
||||
IList<<#= typeName #>>? <#= targetVariable #> = await context.ReadListAsync<<#= GetTypeAlias<TElement>() #>>(key: "<#= expression.VariableReference.VariableName #>", scopeName: "<#= expression.VariableReference.NamespaceAlias #>").ConfigureAwait(false);<#+
|
||||
}
|
||||
else if (expression.IsVariableReference)
|
||||
{#>
|
||||
IList<<#= typeName #>>? <#= targetVariable #>> = await context.EvaluateListAsync<<#= typeName #>>(<#= FormatStringValue(expression.VariableReference.ToString()) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
else
|
||||
{#>
|
||||
IList<<#= typeName #>>? <#= targetVariable #> = await context.EvaluateListAsync<<#= typeName #>>(<#= FormatStringValue(expression.ExpressionText) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
}
|
||||
#>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<#+
|
||||
void EvaluateRecordExpression<TValue>(ObjectExpression<RecordDataValue> expression, string targetVariable)
|
||||
{
|
||||
string resultTypeName = $"Dictionary<string, {GetTypeAlias<TValue>()}?>?";
|
||||
if (expression is null)
|
||||
{#>
|
||||
<#= resultTypeName #> <#= targetVariable #> = null;<#+
|
||||
}
|
||||
else if (expression.IsLiteral)
|
||||
{#>
|
||||
<#= resultTypeName #> <#= targetVariable #> =
|
||||
<#= FormatDataValue(expression.LiteralValue) #>;<#+
|
||||
}
|
||||
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
|
||||
{#>
|
||||
<#= resultTypeName #> <#= targetVariable #> = await context.ReadStateAsync<<#= resultTypeName #>>(key: "<#= expression.VariableReference.VariableName #>", scopeName: "<#= expression.VariableReference.NamespaceAlias #>").ConfigureAwait(false);<#+
|
||||
}
|
||||
else if (expression.IsVariableReference)
|
||||
{#>
|
||||
<#= resultTypeName #>? <#= targetVariable #> = await context.EvaluateExpressionAsync<<#= resultTypeName #>>(<#= FormatStringValue(expression.VariableReference.ToString()) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
else
|
||||
{#>
|
||||
<#= resultTypeName #> <#= targetVariable #> = await context.EvaluateExpressionAsync<<#= resultTypeName #>>(<#= FormatStringValue(expression.ExpressionText) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
}
|
||||
#>
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<#+
|
||||
void EvaluateStringExpression(StringExpression expression, string targetVariable, bool isNullable = false)
|
||||
{
|
||||
string typeName = isNullable ? "string?" : "string";
|
||||
if (expression is null)
|
||||
{#>
|
||||
<#= typeName #> <#= targetVariable #> = <#= isNullable ? "null" : "string.Empty" #>;<#+
|
||||
}
|
||||
else if (expression.IsLiteral)
|
||||
{
|
||||
if (expression.LiteralValue.Contains("\n"))
|
||||
{#>
|
||||
<#= typeName #> <#= targetVariable #> =
|
||||
"""
|
||||
<#= expression.LiteralValue #>
|
||||
""";<#+
|
||||
}
|
||||
else
|
||||
{#>
|
||||
<#= typeName #> <#= targetVariable #> = <#= FormatStringValue(expression.LiteralValue) #>;<#+
|
||||
}
|
||||
}
|
||||
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
|
||||
{#>
|
||||
<#= typeName #> <#= targetVariable #> = await context.ReadStateAsync<string>(key: "<#= expression.VariableReference.VariableName #>", scopeName: "<#= expression.VariableReference.NamespaceAlias #>").ConfigureAwait(false);<#+
|
||||
}
|
||||
else if (expression.IsVariableReference)
|
||||
{#>
|
||||
<#= typeName #> <#= targetVariable #> = await context.EvaluateValueAsync<string>(<#= FormatStringValue(expression.VariableReference.ToString()) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
else
|
||||
{#>
|
||||
<#= typeName #> <#= targetVariable #> = await context.EvaluateValueAsync<string>(<#= FormatStringValue(expression.ExpressionText) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
}
|
||||
#>
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<#+
|
||||
void EvaluateValueExpression(ValueExpression expression, string targetVariable) =>
|
||||
EvaluateValueExpression<object>(expression, targetVariable);
|
||||
|
||||
void EvaluateValueExpression<TValue>(ValueExpression expression, string targetVariable)
|
||||
{
|
||||
if (expression is null)
|
||||
{#>
|
||||
<#= GetTypeAlias<TValue>() #>? <#= targetVariable #> = null;<#+
|
||||
}
|
||||
else if (expression.IsLiteral)
|
||||
{#>
|
||||
<#= GetTypeAlias<TValue>() #>? <#= targetVariable #> = <#= FormatDataValue(expression.LiteralValue) #>;<#+
|
||||
}
|
||||
else if (expression.IsVariableReference && expression.VariableReference.SegmentCount == 2)
|
||||
{#>
|
||||
<#= GetTypeAlias<TValue>() #>? <#= targetVariable #> = await context.ReadStateAsync<<#= GetTypeAlias<TValue>() #>>(key: "<#= expression.VariableReference.VariableName #>", scopeName: "<#= expression.VariableReference.NamespaceAlias #>").ConfigureAwait(false);<#+
|
||||
}
|
||||
else if (expression.IsVariableReference)
|
||||
{#>
|
||||
<#= GetTypeAlias<TValue>() #>? <#= targetVariable #> = await context.EvaluateValueAsync<<#= GetTypeAlias<TValue>() #>>(<#= FormatStringValue(expression.VariableReference.ToString()) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
else
|
||||
{#>
|
||||
<#= GetTypeAlias<TValue>() #>? <#= targetVariable #> = await context.EvaluateValueAsync<<#= GetTypeAlias<TValue>() #>>(<#= FormatStringValue(expression.ExpressionText) #>).ConfigureAwait(false);<#+
|
||||
}
|
||||
}
|
||||
#>
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<#+
|
||||
void EvaluateMessageTemplate(TemplateLine templateLine, string variableName)
|
||||
{
|
||||
if (templateLine is not null)
|
||||
{#>
|
||||
string <#= variableName #> =
|
||||
await context.FormatTemplateAsync(
|
||||
"""<#+
|
||||
FormatMessageTemplate(templateLine); #>
|
||||
""");<#+
|
||||
}
|
||||
else
|
||||
{#>
|
||||
string? <#= variableName #> = null;<#+
|
||||
}
|
||||
}
|
||||
|
||||
void FormatMessageTemplate(TemplateLine line)
|
||||
{
|
||||
foreach (string text in line.ToTemplateString().ByLine())
|
||||
{ #>
|
||||
<#= text #><#+
|
||||
}
|
||||
}
|
||||
#>
|
||||
@@ -0,0 +1,14 @@
|
||||
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.Extensions" #>
|
||||
<#@ import namespace="Microsoft.Agents.AI.Workflows.Declarative.ObjectModel" #>
|
||||
<#@ import namespace="Microsoft.Bot.ObjectModel" #>
|
||||
<#@ import namespace="Microsoft.Extensions.AI" #>
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#@ include file="AssignVariableTemplate.tt" once="true" #>
|
||||
<#@ include file="EvaluateBoolExpressionTemplate.tt" once="true" #>
|
||||
<#@ include file="EvaluateEnumExpressionTemplate.tt" once="true" #>
|
||||
<#@ include file="EvaluateIntExpressionTemplate.tt" once="true" #>
|
||||
<#@ include file="EvaluateListExpressionTemplate.tt" once="true" #>
|
||||
<#@ include file="EvaluateRecordExpressionTemplate.tt" once="true" #>
|
||||
<#@ include file="EvaluateStringExpressionTemplate.tt" once="true" #>
|
||||
<#@ include file="EvaluateValueExpressionTemplate.tt" once="true" #>
|
||||
<#@ include file="FormatMessageTemplate.tt" once="true" #>
|
||||
+76
-14
@@ -16,6 +16,20 @@ namespace Microsoft.Agents.AI.Workflows.Declarative;
|
||||
/// </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>
|
||||
@@ -48,13 +62,7 @@ public static class DeclarativeWorkflowBuilder
|
||||
Func<TInput, ChatMessage>? inputTransform = null)
|
||||
where TInput : notnull
|
||||
{
|
||||
BotElement rootElement = YamlSerializer.Deserialize<BotElement>(yamlReader) ?? throw new DeclarativeModelException("Workflow undefined.");
|
||||
|
||||
if (rootElement is not AdaptiveDialog workflowElement)
|
||||
{
|
||||
throw new DeclarativeModelException($"Unsupported root element: {rootElement.GetType().Name}. Expected an {nameof(Workflow)}.");
|
||||
}
|
||||
|
||||
AdaptiveDialog workflowElement = ReadWorkflow(yamlReader);
|
||||
string rootId = WorkflowActionVisitor.Steps.Root(workflowElement);
|
||||
|
||||
WorkflowFormulaState state = new(options.CreateRecalcEngine());
|
||||
@@ -72,11 +80,65 @@ public static class DeclarativeWorkflowBuilder
|
||||
return visitor.Complete();
|
||||
}
|
||||
|
||||
internal static ChatMessage DefaultTransform(object message) =>
|
||||
message switch
|
||||
{
|
||||
ChatMessage chatMessage => chatMessage,
|
||||
string stringMessage => new ChatMessage(ChatRole.User, stringMessage),
|
||||
_ => new(ChatRole.User, $"{message}")
|
||||
};
|
||||
/// <summary>
|
||||
/// Generates source code (provider/executor scaffolding) for the workflow defined in the YAML file.
|
||||
/// </summary>
|
||||
/// <param name="workflowFile">The path to the workflow YAML file.</param>
|
||||
/// <param name="workflowLanguage">The language to use for the generated code.</param>
|
||||
/// <param name="workflowNamespace">Optional target namespace for the generated code.</param>
|
||||
/// <param name="workflowPrefix">Optional prefix for generated workflow type.</param>
|
||||
/// <returns>The generated source code representing the workflow.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates source code (provider/executor scaffolding) for the workflow defined in the provided YAML reader.
|
||||
/// </summary>
|
||||
/// <param name="yamlReader">The reader supplying the workflow YAML.</param>
|
||||
/// <param name="workflowLanguage">The language to use for the generated code.</param>
|
||||
/// <param name="workflowNamespace">Optional target namespace for the generated code.</param>
|
||||
/// <param name="workflowPrefix">Optional prefix for generated workflow type.</param>
|
||||
/// <returns>The generated source code representing the workflow.</returns>
|
||||
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<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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative;
|
||||
|
||||
/// <summary>
|
||||
/// Defines programming language for workflow ejection.
|
||||
/// </summary>
|
||||
public enum DeclarativeWorkflowLanguage
|
||||
{
|
||||
/// <summary>
|
||||
/// Python programming language.
|
||||
/// </summary>
|
||||
Python,
|
||||
|
||||
/// <summary>
|
||||
/// C# programming language.
|
||||
/// </summary>
|
||||
CSharp,
|
||||
|
||||
/// <summary>
|
||||
/// JavaScript programming language.
|
||||
/// </summary>
|
||||
JavaScript,
|
||||
}
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx.Functions;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
+154
-2
@@ -1,14 +1,43 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
internal static class DataValueExtensions
|
||||
{
|
||||
public static DataValue ToDataValue(this object? value) =>
|
||||
value switch
|
||||
{
|
||||
null => DataValue.Blank(),
|
||||
UnassignedValue => DataValue.Blank(),
|
||||
FormulaValue formulaValue => formulaValue.ToDataValue(),
|
||||
DataValue dataValue => dataValue,
|
||||
bool booleanValue => BooleanDataValue.Create(booleanValue),
|
||||
int decimalValue => NumberDataValue.Create(decimalValue),
|
||||
long decimalValue => NumberDataValue.Create(decimalValue),
|
||||
float decimalValue => FloatDataValue.Create(decimalValue),
|
||||
decimal decimalValue => NumberDataValue.Create(decimalValue),
|
||||
double numberValue => FloatDataValue.Create(numberValue),
|
||||
string stringValue => StringDataValue.Create(stringValue),
|
||||
DateTime dateonlyValue when dateonlyValue.TimeOfDay == TimeSpan.Zero => DateDataValue.Create(dateonlyValue),
|
||||
DateTime datetimeValue => DateTimeDataValue.Create(datetimeValue),
|
||||
TimeSpan timeValue => TimeDataValue.Create(timeValue),
|
||||
object when value is IDictionary dictionaryValue => dictionaryValue.ToRecordValue(),
|
||||
object when value is IEnumerable tableValue => tableValue.ToTableValue(),
|
||||
_ => throw new DeclarativeModelException($"Unsupported variable type: {value.GetType().Name}"),
|
||||
};
|
||||
|
||||
public static FormulaValue ToFormula(this DataValue? value) =>
|
||||
value switch
|
||||
{
|
||||
@@ -65,12 +94,37 @@ internal static class DataValueExtensions
|
||||
DateTimeDataValue dateTimeValue => dateTimeValue.Value.DateTime,
|
||||
DateDataValue dateValue => dateValue.Value,
|
||||
TimeDataValue timeValue => timeValue.Value,
|
||||
TableDataValue tableValue => tableValue.Values.Select(value => value.ToDictionary()).ToArray(),
|
||||
RecordDataValue recordValue => recordValue.ToDictionary(),
|
||||
TableDataValue tableValue => tableValue.ToObject(),
|
||||
RecordDataValue recordValue => recordValue.ToObject(),
|
||||
OptionDataValue optionValue => optionValue.Value.Value,
|
||||
_ => throw new DeclarativeModelException($"Unsupported {nameof(DataValue)} type: {value.GetType().Name}"),
|
||||
};
|
||||
|
||||
public static Type ToClrType(this DataType type) =>
|
||||
type switch
|
||||
{
|
||||
BooleanDataType => typeof(bool),
|
||||
NumberDataType => typeof(decimal),
|
||||
FloatDataType => typeof(double),
|
||||
StringDataType => typeof(string),
|
||||
DateTimeDataType => typeof(DateTime),
|
||||
DateDataType => typeof(DateTime),
|
||||
TimeDataType => typeof(TimeSpan),
|
||||
TableDataType tableType => VariableType.ListType,
|
||||
RecordDataType recordValue => VariableType.RecordType,
|
||||
_ => throw new DeclarativeModelException($"Unsupported {nameof(DataValue)} type: {type.GetType().Name}"),
|
||||
};
|
||||
|
||||
public static IList<TElement>? AsList<TElement>(this DataValue? value)
|
||||
{
|
||||
if (value is null || value is BlankDataValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.ToObject().AsList<TElement>();
|
||||
}
|
||||
|
||||
public static FormulaValue NewBlank(this DataType? type) => FormulaValue.NewBlank(type?.ToFormulaType() ?? FormulaType.Blank);
|
||||
|
||||
public static RecordValue ToRecordValue(this RecordDataValue recordDataValue) =>
|
||||
@@ -88,6 +142,53 @@ internal static class DataValueExtensions
|
||||
return recordType;
|
||||
}
|
||||
|
||||
public static RecordDataValue ToRecordValue(this IDictionary value)
|
||||
{
|
||||
return DataValue.RecordFromFields(GetFields());
|
||||
|
||||
IEnumerable<KeyValuePair<string, DataValue>> GetFields()
|
||||
{
|
||||
yield return new KeyValuePair<string, DataValue>(TypeSchema.Discriminator, nameof(ExpandoObject).ToDataValue());
|
||||
|
||||
foreach (string key in value.Keys)
|
||||
{
|
||||
yield return new KeyValuePair<string, DataValue>(key, value[key].ToDataValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TableDataValue ToTableValue(this IEnumerable values)
|
||||
{
|
||||
IEnumerator enumerator = values.GetEnumerator();
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
return DataValue.EmptyTable;
|
||||
}
|
||||
|
||||
if (enumerator.Current is IDictionary)
|
||||
{
|
||||
DataValue.TableFromRecords(GetFields().ToImmutableArray());
|
||||
}
|
||||
|
||||
return DataValue.TableFromValues(GetValues().ToImmutableArray());
|
||||
|
||||
IEnumerable<RecordDataValue> GetFields()
|
||||
{
|
||||
foreach (IDictionary value in values)
|
||||
{
|
||||
yield return value.ToRecordValue();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<DataValue> GetValues()
|
||||
{
|
||||
foreach (object value in values)
|
||||
{
|
||||
yield return value.ToDataValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RecordType ParseRecordType(this RecordDataValue record)
|
||||
{
|
||||
RecordType recordType = RecordType.Empty();
|
||||
@@ -98,9 +199,60 @@ internal static class DataValueExtensions
|
||||
return recordType;
|
||||
}
|
||||
|
||||
private static object ToObject(this TableDataValue table)
|
||||
{
|
||||
DataValue? firstElement = table.Values.FirstOrDefault();
|
||||
if (firstElement is null)
|
||||
{
|
||||
return Array.Empty<object>();
|
||||
}
|
||||
|
||||
if (firstElement is RecordDataValue record)
|
||||
{
|
||||
if (record.Properties.Count == 1 && record.Properties.TryGetValue("Value", out DataValue? singleColumn))
|
||||
{
|
||||
record = singleColumn as RecordDataValue ?? record;
|
||||
}
|
||||
|
||||
if (record.Properties.TryGetValue(TypeSchema.Discriminator, out DataValue? value) && value is StringDataValue typeValue)
|
||||
{
|
||||
if (string.Equals(nameof(ChatMessage), typeValue.Value, StringComparison.Ordinal))
|
||||
{
|
||||
return table.ToChatMessages().ToArray();
|
||||
}
|
||||
|
||||
if (string.Equals(nameof(ExpandoObject), typeValue.Value, StringComparison.Ordinal))
|
||||
{
|
||||
return table.Values.Select(dataValue => dataValue.ToDictionary()).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return table.Values.Select(value => value.ToObject()).ToArray();
|
||||
}
|
||||
|
||||
private static object ToObject(this RecordDataValue record)
|
||||
{
|
||||
if (record.Properties.TryGetValue(TypeSchema.Discriminator, out DataValue? value) && value is StringDataValue typeValue)
|
||||
{
|
||||
if (string.Equals(nameof(ChatMessage), typeValue.Value, StringComparison.Ordinal))
|
||||
{
|
||||
return record.ToChatMessage();
|
||||
}
|
||||
|
||||
if (string.Equals(nameof(ExpandoObject), typeValue.Value, StringComparison.Ordinal))
|
||||
{
|
||||
return record.ToDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
return record.ToDictionary();
|
||||
}
|
||||
|
||||
private static Dictionary<string, object?> ToDictionary(this RecordDataValue record)
|
||||
{
|
||||
Dictionary<string, object?> result = [];
|
||||
result[TypeSchema.Discriminator] = nameof(ExpandoObject);
|
||||
foreach (KeyValuePair<string, DataValue> property in record.Properties)
|
||||
{
|
||||
result[property.Key] = property.Value.ToObject();
|
||||
|
||||
+1
-1
@@ -8,8 +8,8 @@ using System.Dynamic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx.Functions;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Interpreter;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.PowerFx;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
internal static class JsonDocumentExtensions
|
||||
{
|
||||
public static FrozenDictionary<string, object?> ParseRecord(this JsonDocument jsonDocument, VariableType recordType) => jsonDocument.RootElement.ParseRecord(recordType);
|
||||
|
||||
public static RecordValue ParseRecord(this JsonDocument jsonDocument, RecordDataType recordType) => jsonDocument.RootElement.ParseRecord(recordType);
|
||||
|
||||
private static FrozenDictionary<string, object?> ParseRecord(this JsonElement currentElement, VariableType recordType)
|
||||
{
|
||||
if (!recordType.IsRecord || recordType.Schema is null)
|
||||
{
|
||||
throw new DeclarativeActionException($"Unable to parse JSON element as {recordType.Type.Name}.");
|
||||
}
|
||||
|
||||
return ParseValues().ToFrozenDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||
|
||||
IEnumerable<KeyValuePair<string, object?>> ParseValues()
|
||||
{
|
||||
foreach (KeyValuePair<string, VariableType?> property in recordType.Schema)
|
||||
{
|
||||
JsonElement propertyElement = currentElement.GetProperty(property.Key);
|
||||
object? parsedValue =
|
||||
property.Value?.Type switch
|
||||
{
|
||||
null => null,
|
||||
_ when property.Value.Type == typeof(string) => propertyElement.GetString(),
|
||||
_ when property.Value.Type == typeof(int) => propertyElement.GetInt32(),
|
||||
_ when property.Value.Type == typeof(long) => propertyElement.GetInt64(),
|
||||
_ when property.Value.Type == typeof(decimal) => propertyElement.GetDecimal(),
|
||||
_ when property.Value.Type == typeof(double) => propertyElement.GetDouble(),
|
||||
_ when property.Value.Type == typeof(bool) => propertyElement.GetBoolean(),
|
||||
_ when property.Value.Type == typeof(DateTime) => propertyElement.GetDateTime(),
|
||||
_ when property.Value.Type == typeof(TimeSpan) => propertyElement.GetDateTimeOffset().TimeOfDay,
|
||||
_ when property.Value.IsRecord => propertyElement.ParseRecord(property.Value),
|
||||
//TableDataType tableType => ParseTable(tableType, propertyElement),
|
||||
_ => throw new InvalidOperationException($"Unsupported data type '{property.Value.Type}' for property '{property.Key}'"),
|
||||
};
|
||||
yield return new KeyValuePair<string, object?>(property.Key, parsedValue);
|
||||
}
|
||||
|
||||
//static TableValue ParseTable(TableDataType tableType, JsonElement propertyElement)
|
||||
//{
|
||||
// RecordDataType recordType = tableType.ToRecord();
|
||||
// return
|
||||
// FormulaValue.NewTable(
|
||||
// recordType.ToRecordType(),
|
||||
// propertyElement.EnumerateArray().Select(tableElement => tableElement.ParseRecord(recordType)));
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private static RecordValue ParseRecord(this JsonElement currentElement, RecordDataType recordType)
|
||||
{
|
||||
return FormulaValue.NewRecordFromFields(ParseValues());
|
||||
|
||||
IEnumerable<NamedValue> ParseValues()
|
||||
{
|
||||
foreach (KeyValuePair<string, PropertyInfo> property in recordType.Properties)
|
||||
{
|
||||
JsonElement propertyElement = currentElement.GetProperty(property.Key);
|
||||
FormulaValue? parsedValue =
|
||||
property.Value.Type switch
|
||||
{
|
||||
StringDataType => FormulaValue.New(propertyElement.GetString()),
|
||||
NumberDataType => FormulaValue.New(propertyElement.GetDecimal()),
|
||||
BooleanDataType => FormulaValue.New(propertyElement.GetBoolean()),
|
||||
DateTimeDataType => FormulaValue.New(propertyElement.GetDateTime()),
|
||||
DateDataType => FormulaValue.New(propertyElement.GetDateTime()),
|
||||
TimeDataType => FormulaValue.New(propertyElement.GetDateTimeOffset().TimeOfDay),
|
||||
RecordDataType recordType => propertyElement.ParseRecord(recordType),
|
||||
TableDataType tableType => ParseTable(tableType, propertyElement),
|
||||
_ => throw new InvalidOperationException($"Unsupported data type '{property.Value.Type}' for property '{property.Key}'"),
|
||||
};
|
||||
yield return new NamedValue(property.Key, parsedValue);
|
||||
}
|
||||
|
||||
static TableValue ParseTable(TableDataType tableType, JsonElement propertyElement)
|
||||
{
|
||||
RecordDataType recordType = tableType.ToRecord();
|
||||
return
|
||||
FormulaValue.NewTable(
|
||||
recordType.ToRecordType(),
|
||||
propertyElement.EnumerateArray().Select(tableElement => tableElement.ParseRecord(recordType)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Kit;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
internal static class ObjectExtensions
|
||||
{
|
||||
public static IList<TElement>? AsList<TElement>(this object? value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
null => null,
|
||||
UnassignedValue => null,
|
||||
BlankValue => null,
|
||||
BlankDataValue => null,
|
||||
IList<TElement> list => list,
|
||||
IEnumerable<TElement> enumerable => enumerable.ToList(),
|
||||
TElement element => [element],
|
||||
_ => TypedElements().ToList(),
|
||||
};
|
||||
|
||||
IEnumerable<TElement> TypedElements()
|
||||
{
|
||||
if (value is not IEnumerable enumerable)
|
||||
{
|
||||
throw new DeclarativeActionException($"Value '{value.GetType().Name}' is not '{nameof(IEnumerable)}'.");
|
||||
}
|
||||
|
||||
foreach (var item in enumerable)
|
||||
{
|
||||
if (item is not TElement element)
|
||||
{
|
||||
throw new DeclarativeActionException($"Item '{item.GetType().Name}' is not of type '{typeof(TElement).Name}'");
|
||||
}
|
||||
|
||||
yield return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static object? ConvertType(this object? sourceValue, VariableType targetType)
|
||||
{
|
||||
if (!targetType.IsValid())
|
||||
{
|
||||
throw new DeclarativeActionException($"Unsupported type: '{targetType.Type.Name}'.");
|
||||
}
|
||||
|
||||
if (sourceValue != null && targetType.Type.IsAssignableFrom(sourceValue.GetType()))
|
||||
{
|
||||
return sourceValue;
|
||||
}
|
||||
|
||||
return targetType switch
|
||||
{
|
||||
_ when typeof(string).IsAssignableFrom(targetType.Type) => ConvertToString(),
|
||||
_ when typeof(bool).IsAssignableFrom(targetType.Type) => ConvertToBool(),
|
||||
_ when targetType.IsRecord => ConvertToRecord(),
|
||||
_ when targetType.IsList => ConvertToList(),
|
||||
_ when typeof(int).IsAssignableFrom(targetType.Type) => ConvertToInt(),
|
||||
_ when typeof(long).IsAssignableFrom(targetType.Type) => ConvertToLong(),
|
||||
_ when typeof(decimal).IsAssignableFrom(targetType.Type) => ConvertToDecimal(),
|
||||
_ when typeof(double).IsAssignableFrom(targetType.Type) => ConvertToDouble(),
|
||||
_ when typeof(DateTime).IsAssignableFrom(targetType.Type) => ConvertToDateTime(),
|
||||
_ when typeof(TimeSpan).IsAssignableFrom(targetType.Type) => ConvertToTimeSpan(),
|
||||
_ => throw new DeclarativeActionException($"Unsupported type: '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
bool? ConvertToBool() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => bool.Parse(s),
|
||||
int i => i != 0,
|
||||
long l => l != 0,
|
||||
decimal c => c != 0,
|
||||
double d => d != 0,
|
||||
DateTime dt => dt > DateTime.MinValue,
|
||||
TimeSpan ts => ts > TimeSpan.MinValue,
|
||||
_ => sourceValue != null,
|
||||
};
|
||||
|
||||
int? ConvertToInt() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => int.Parse(s),
|
||||
int i => i,
|
||||
long l => Convert.ToInt32(l),
|
||||
decimal c => Convert.ToInt32(c),
|
||||
double d => Convert.ToInt32(d),
|
||||
DateTime dt => Convert.ToInt32(dt),
|
||||
TimeSpan ts => Convert.ToInt32(ts),
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
long? ConvertToLong() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => long.Parse(s),
|
||||
int i => i,
|
||||
long l => l,
|
||||
decimal c => Convert.ToInt64(c),
|
||||
double d => Convert.ToInt64(d),
|
||||
DateTime dt => Convert.ToInt64(dt),
|
||||
TimeSpan ts => Convert.ToInt64(ts),
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
decimal? ConvertToDecimal() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => decimal.Parse(s),
|
||||
int i => i,
|
||||
long l => l,
|
||||
decimal c => c,
|
||||
double d => Convert.ToDecimal(d),
|
||||
DateTime dt => Convert.ToDecimal(dt),
|
||||
TimeSpan ts => Convert.ToDecimal(ts),
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
double? ConvertToDouble() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => double.Parse(s),
|
||||
int i => i,
|
||||
long l => l,
|
||||
decimal c => Convert.ToDouble(c),
|
||||
double d => d,
|
||||
DateTime dt => dt.Ticks,
|
||||
TimeSpan ts => ts.Ticks,
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
DateTime? ConvertToDateTime() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => DateTime.Parse(s),
|
||||
int i => new DateTime(i),
|
||||
long l => new DateTime(l),
|
||||
decimal c => new DateTime(Convert.ToInt64(c)),
|
||||
double d => new DateTime(Convert.ToInt64(d)),
|
||||
DateTime dt => dt,
|
||||
TimeSpan ts => DateTime.Now.Date.AddTicks(ts.Ticks),
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
TimeSpan? ConvertToTimeSpan() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string s => TimeSpan.Parse(s),
|
||||
int i => TimeSpan.FromTicks(i),
|
||||
long l => TimeSpan.FromTicks(l),
|
||||
decimal c => TimeSpan.FromTicks(Convert.ToInt64(c)),
|
||||
double d => TimeSpan.FromTicks(Convert.ToInt64(d)),
|
||||
DateTime dt => dt.TimeOfDay,
|
||||
TimeSpan ts => ts,
|
||||
_ => throw new DeclarativeActionException($"Unsupported target type for '{sourceValue.GetType().Name}': '{targetType.Type.Name}'."),
|
||||
};
|
||||
|
||||
object? ConvertToList() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
//string jsonText => JsonDocument.Parse(jsonText.TrimJsonDelimiter()).ParseRecord(targetType),
|
||||
_ => throw new DeclarativeActionException($"Cannot convert '{sourceValue?.GetType().Name}' to 'Record' (expected JSON string)."),
|
||||
};
|
||||
|
||||
object? ConvertToRecord() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string jsonText => JsonDocument.Parse(jsonText.TrimJsonDelimiter()).ParseRecord(targetType),
|
||||
_ => throw new DeclarativeActionException($"Cannot convert '{sourceValue?.GetType().Name}' to 'Record' (expected JSON string)."),
|
||||
};
|
||||
|
||||
string? ConvertToString() =>
|
||||
sourceValue switch
|
||||
{
|
||||
null => null,
|
||||
string sourceText => sourceText,
|
||||
DateTime dateTime => dateTime.ToString("o"), // ISO 8601
|
||||
TimeSpan timeSpan => timeSpan.ToString("c"), // Constant ("c") format
|
||||
_ => $"{sourceValue}",
|
||||
};
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.Extensions;
|
||||
|
||||
internal static class RecordDataTypeExtensions
|
||||
{
|
||||
public static RecordValue ParseRecord(this RecordDataType recordType, JsonElement currentElement)
|
||||
{
|
||||
return FormulaValue.NewRecordFromFields(ParseValues());
|
||||
|
||||
IEnumerable<NamedValue> ParseValues()
|
||||
{
|
||||
foreach (KeyValuePair<string, PropertyInfo> property in recordType.Properties)
|
||||
{
|
||||
JsonElement propertyElement = currentElement.GetProperty(property.Key);
|
||||
FormulaValue? parsedValue =
|
||||
property.Value.Type switch
|
||||
{
|
||||
StringDataType => FormulaValue.New(propertyElement.GetString()),
|
||||
NumberDataType => FormulaValue.New(propertyElement.GetDecimal()),
|
||||
BooleanDataType => FormulaValue.New(propertyElement.GetBoolean()),
|
||||
DateTimeDataType => FormulaValue.New(propertyElement.GetDateTime()),
|
||||
DateDataType => FormulaValue.New(propertyElement.GetDateTime()),
|
||||
TimeDataType => FormulaValue.New(propertyElement.GetDateTimeOffset().TimeOfDay),
|
||||
RecordDataType recordType => recordType.ParseRecord(propertyElement),
|
||||
TableDataType tableType => ParseTable(tableType, propertyElement),
|
||||
_ => throw new InvalidOperationException($"Unsupported data type '{property.Value.Type}' for property '{property.Key}'"),
|
||||
};
|
||||
yield return new NamedValue(property.Key, parsedValue);
|
||||
}
|
||||
|
||||
static TableValue ParseTable(TableDataType tableType, JsonElement propertyElement)
|
||||
{
|
||||
RecordDataType recordType = tableType.ToRecord();
|
||||
return
|
||||
FormulaValue.NewTable(
|
||||
recordType.ToRecordType(),
|
||||
propertyElement.EnumerateArray().Select(tableElement => ParseRecord(recordType, tableElement)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.PowerFx.Types;
|
||||
|
||||
@@ -27,4 +29,30 @@ internal static partial class StringExtensions
|
||||
|
||||
public static FormulaValue ToFormula(this string? value) =>
|
||||
string.IsNullOrWhiteSpace(value) ? FormulaValue.NewBlank() : FormulaValue.New(value);
|
||||
|
||||
public static string FormatType(this string identifier) => FormatIdentifier(identifier);
|
||||
|
||||
public static string FormatName(this string identifier) => FormatIdentifier(identifier, skipFirst: true);
|
||||
|
||||
private static string FormatIdentifier(string identifier, bool skipFirst = false)
|
||||
{
|
||||
string[] words = identifier.Split('_');
|
||||
|
||||
// Capitalize each word
|
||||
for (int index = skipFirst ? 1 : 0; index < words.Length; ++index)
|
||||
{
|
||||
words[index] = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(words[index]);
|
||||
}
|
||||
|
||||
// Combine the words and return
|
||||
return string.Concat(words);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ByLine(this string source)
|
||||
{
|
||||
foreach (string line in source.Trim().Split('\n'))
|
||||
{
|
||||
yield return line.TrimEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user