diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/AddConversationMessageTemplate.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/AddConversationMessageTemplate.cs index 25cfb6b89e..99aaf03292 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/AddConversationMessageTemplate.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/AddConversationMessageTemplate.cs @@ -60,8 +60,8 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen "is.Id}\");\n }\n ChatMessage newMessage = new(ChatRole."); this.Write(this.ToStringHelper.ToStringWithCulture(FormatEnum(this.Model.Role, RoleMap))); this.Write(", await this.GetContentAsync(context).ConfigureAwait(false)) { AdditionalProperti" + - "es = this.GetMetadata() };\n await agentProvider.CreateMessageAsync(conver" + - "sationId, newMessage, cancellationToken).ConfigureAwait(false);"); + "es = this.GetMetadata() };\n newMessage = await agentProvider.CreateMessag" + + "eAsync(conversationId, newMessage, cancellationToken).ConfigureAwait(false);"); AssignVariable(this.Message, "newMessage"); diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/AddConversationMessageTemplate.tt b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/AddConversationMessageTemplate.tt index 043288596c..439f62f8db 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/AddConversationMessageTemplate.tt +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/AddConversationMessageTemplate.tt @@ -17,7 +17,7 @@ internal sealed class <#= this.Name #>Executor(FormulaSession session, WorkflowA throw new DeclarativeActionException($"Conversation identifier must be defined: {this.Id}"); } ChatMessage newMessage = new(ChatRole.<#= FormatEnum(this.Model.Role, RoleMap) #>, await this.GetContentAsync(context).ConfigureAwait(false)) { AdditionalProperties = this.GetMetadata() }; - await agentProvider.CreateMessageAsync(conversationId, newMessage, cancellationToken).ConfigureAwait(false);<# + newMessage = await agentProvider.CreateMessageAsync(conversationId, newMessage, cancellationToken).ConfigureAwait(false);<# AssignVariable(this.Message, "newMessage"); #> return default; diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/RetrieveConversationMessagesTemplate.cs b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/RetrieveConversationMessagesTemplate.cs index 3fbb809041..cdd4c502fc 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/RetrieveConversationMessagesTemplate.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/RetrieveConversationMessagesTemplate.cs @@ -1,7 +1,7 @@ // ------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version: 18.0.0.0 +// Runtime Version: 17.0.0.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -19,7 +19,7 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen /// /// Class to produce the template output /// - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "17.0.0.0")] internal partial class RetrieveConversationMessagesTemplate : ActionTemplate { /// @@ -44,8 +44,8 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen this.Write("\n"); this.Write("\n"); this.Write("\n"); - this.Write("f\n/// \n/// Retrieves a specific message from an agent conversation.\n/// " + - "\ninternal sealed class "); + this.Write("\n/// \n/// Retrieves a specific message from an agent conversation.\n/// <" + + "/summary>\ninternal sealed class "); this.Write(this.ToStringHelper.ToStringWithCulture(this.Name)); this.Write("Executor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExe" + "cutor(id: \""); @@ -60,14 +60,19 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.CodeGen EvaluateStringExpression(this.Model.MessageBefore, "before", isNullable: true); EvaluateEnumExpression(this.Model.SortOrder, "newestFirst", SortMap, defaultValue: DefaultSort); this.Write(@" - ChatMessage messages = - await agentProvider.GetMessageAsync( - converationId, + IAsyncEnumerable messagesResult = + agentProvider.GetMessagesAsync( + conversationId, limit, after, before, newestFirst, - cancellationToken).ConfigureAwait(false);"); + cancellationToken); + List messages = []; + await foreach (ChatMessage message in messagesResult.ConfigureAwait(false)) + { + messages.Add(message); + }"); AssignVariable(this.Model.Messages, "messages"); diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/RetrieveConversationMessagesTemplate.tt b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/RetrieveConversationMessagesTemplate.tt index 3d8a651301..96aec79282 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/RetrieveConversationMessagesTemplate.tt +++ b/dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/CodeGen/RetrieveConversationMessagesTemplate.tt @@ -1,7 +1,7 @@ <#@ template language="C#" inherits="ActionTemplate" visibility="internal" linePragmas="false" #> <#@ output extension=".cs" #> <#@ assembly name="System.Core" #> -<#@ include file="Snippets/Index.tt" once="true" #>f +<#@ include file="Snippets/Index.tt" once="true" #> /// /// Retrieves a specific message from an agent conversation. /// @@ -15,14 +15,19 @@ internal sealed class <#= this.Name #>Executor(FormulaSession session, WorkflowA EvaluateStringExpression(this.Model.MessageAfter, "after", isNullable: true); EvaluateStringExpression(this.Model.MessageBefore, "before", isNullable: true); EvaluateEnumExpression(this.Model.SortOrder, "newestFirst", SortMap, defaultValue: DefaultSort); #> - ChatMessage messages = - await agentProvider.GetMessageAsync( - converationId, + IAsyncEnumerable messagesResult = + agentProvider.GetMessagesAsync( + conversationId, limit, after, before, newestFirst, - cancellationToken).ConfigureAwait(false);<# + cancellationToken); + List messages = []; + await foreach (ChatMessage message in messagesResult.ConfigureAwait(false)) + { + messages.Add(message); + }<# AssignVariable(this.Model.Messages, "messages"); #> return default; diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeCodeGenTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeCodeGenTest.cs index 1fd4440be8..0dd9f08940 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeCodeGenTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.IntegrationTests/DeclarativeCodeGenTest.cs @@ -19,8 +19,8 @@ public sealed class DeclarativeCodeGenTest(ITestOutputHelper output) : WorkflowT [InlineData("SendActivity.yaml", "SendActivity.json")] [InlineData("InvokeAgent.yaml", "InvokeAgent.json")] [InlineData("InvokeAgent.yaml", "InvokeAgent.json", true)] - [InlineData("ConversationMessages.yaml", "ConversationMessages.json", Skip = "Issue #1236")] - [InlineData("ConversationMessages.yaml", "ConversationMessages.json", true, Skip = "Issue #1236")] + [InlineData("ConversationMessages.yaml", "ConversationMessages.json")] + [InlineData("ConversationMessages.yaml", "ConversationMessages.json", true)] public Task ValidateCaseAsync(string workflowFileName, string testcaseFileName, bool externalConveration = false) => this.RunWorkflowAsync(Path.Combine(Environment.CurrentDirectory, "Workflows", workflowFileName), testcaseFileName, externalConveration); diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CopyConversationMessagesTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CopyConversationMessagesTemplateTest.cs index e7793529a5..e78a63fe08 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CopyConversationMessagesTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/CopyConversationMessagesTemplateTest.cs @@ -10,15 +10,25 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; public class CopyConversationMessagesTemplateTest(ITestOutputHelper output) : WorkflowActionTemplateTest(output) { [Fact] - public void CopyConversationMessages() + public void CopyConversationMessagesLiteral() { // Act, Assert this.ExecuteTest( - nameof(CopyConversationMessages), + nameof(CopyConversationMessagesLiteral), StringExpression.Literal("#conv_dm99"), ValueExpression.Variable(PropertyPath.TopicVariable("MyMessages"))); } + [Fact] + public void CopyConversationMessagesVariable() + { + // Act, Assert + this.ExecuteTest( + nameof(CopyConversationMessagesVariable), + StringExpression.Variable(PropertyPath.TopicVariable("TestConversation")), + ValueExpression.Variable(PropertyPath.TopicVariable("MyMessages"))); + } + private void ExecuteTest( string displayName, StringExpression conversation, diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/DeclarativeEjectionTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/DeclarativeEjectionTest.cs index 190e1078f5..8350a81524 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/DeclarativeEjectionTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/DeclarativeEjectionTest.cs @@ -14,9 +14,12 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; public sealed class DeclarativeEjectionTest(ITestOutputHelper output) : WorkflowTest(output) { [Theory] + [InlineData("AddConversationMessage.yaml")] [InlineData("ClearAllVariables.yaml")] + [InlineData("CopyConversationMessages.yaml")] [InlineData("Condition.yaml")] [InlineData("ConditionElse.yaml")] + [InlineData("CreateConversation.yaml")] [InlineData("EditTable.yaml")] [InlineData("EditTableV2.yaml")] [InlineData("EndConversation.yaml")] @@ -28,6 +31,8 @@ public sealed class DeclarativeEjectionTest(ITestOutputHelper output) : Workflow [InlineData("LoopEach.yaml")] [InlineData("ParseValue.yaml")] [InlineData("ResetVariable.yaml")] + [InlineData("RetrieveConversationMessage.yaml")] + [InlineData("RetrieveConversationMessages.yaml")] [InlineData("SendActivity.yaml")] [InlineData("SetVariable.yaml")] [InlineData("SetTextVariable.yaml")] diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessageTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessageTemplateTest.cs index ac6b88da84..f548692bb3 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessageTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessageTemplateTest.cs @@ -10,14 +10,24 @@ namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.CodeGen; public class RetrieveConversationMessageTemplateTest(ITestOutputHelper output) : WorkflowActionTemplateTest(output) { [Fact] - public void RetrieveMessageLiteral() + public void RetrieveConversationVariable() { // Act, Assert this.ExecuteTest( - nameof(RetrieveMessageLiteral), + nameof(RetrieveConversationVariable), + "TestVariable", + StringExpression.Variable(PropertyPath.TopicVariable("TestConversation")), + StringExpression.Literal("#mid_43")); + } + [Fact] + public void RetrieveMessageVariable() + { + // Act, Assert + this.ExecuteTest( + nameof(RetrieveMessageVariable), "TestVariable", StringExpression.Literal("#cid_3"), - StringExpression.Literal("#mid_43")); + StringExpression.Variable(PropertyPath.TopicVariable("TestMessage"))); } private void ExecuteTest( diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessagesTemplateTest.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessagesTemplateTest.cs index ef24147c57..567c2bd489 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessagesTemplateTest.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/CodeGen/RetrieveConversationMessagesTemplateTest.cs @@ -16,7 +16,7 @@ public class RetrieveConversationMessagesTemplateTest(ITestOutputHelper output) this.ExecuteTest( nameof(DefaultQuery), "TestVariable", - StringExpression.Literal("#cid_3")); + StringExpression.Variable(PropertyPath.TopicVariable("TestConversation"))); } [Fact] diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/AddConversationMessage.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/AddConversationMessage.cs new file mode 100644 index 0000000000..5b86a2c0fb --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/AddConversationMessage.cs @@ -0,0 +1,119 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// ------------------------------------------------------------------------------ + +#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; + +namespace Test.WorkflowProviders; + +/// +/// This class provides a factory method to create a instance. +/// +/// +/// 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 +/// +public static class WorkflowProvider +{ + /// + /// The root executor for a declarative workflow. + /// + internal sealed class WorkflowTestRootExecutor( + DeclarativeWorkflowOptions options, + Func inputTransform) : + RootExecutor("workflow_test_Root", options, inputTransform) + where TInput : notnull + { + protected override async ValueTask ExecuteAsync(TInput message, IWorkflowContext context, CancellationToken cancellationToken) + { + // Initialize variables + await context.QueueStateUpdateAsync("MyMessage1", UnassignedValue.Instance, "Local").ConfigureAwait(false); + await context.QueueStateUpdateAsync("TestInput", UnassignedValue.Instance, "Local").ConfigureAwait(false); + } + } + + /// + /// Adds a new message to the specified agent conversation + /// + internal sealed class AddMessageExecutor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExecutor(id: "add_message", session) + { + // + protected override async ValueTask ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken) + { + string? conversationId = await context.ReadStateAsync(key: "ConversationId", scopeName: "System").ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(conversationId)) + { + throw new DeclarativeActionException($"Conversation identifier must be defined: {this.Id}"); + } + ChatMessage newMessage = new(ChatRole.User, await this.GetContentAsync(context).ConfigureAwait(false)) { AdditionalProperties = this.GetMetadata() }; + newMessage = await agentProvider.CreateMessageAsync(conversationId, newMessage, cancellationToken).ConfigureAwait(false); + await context.QueueStateUpdateAsync(key: "MyMessage1", value: newMessage, scopeName: "Local").ConfigureAwait(false); + + return default; + } + + private async ValueTask> GetContentAsync(IWorkflowContext context) + { + List content = []; + + string contentValue1 = + await context.FormatTemplateAsync( + """ + {Local.TestInput} + """); + content.Add(new TextContent(contentValue1)); + return content; + } + + private AdditionalPropertiesDictionary? GetMetadata() + { + Dictionary? metadata = null; + + if (metadata is null) + { + return null; + } + + return new AdditionalPropertiesDictionary(metadata); + } + } + + public static Workflow CreateWorkflow( + DeclarativeWorkflowOptions options, + Func? inputTransform = null) + where TInput : notnull + { + // Create root executor to initialize the workflow. + inputTransform ??= (message) => DeclarativeWorkflowBuilder.DefaultTransform(message); + WorkflowTestRootExecutor workflowTestRoot = new(options, inputTransform); + DelegateExecutor workflowTest = new(id: "workflow_test", workflowTestRoot.Session); + AddMessageExecutor addMessage = new(workflowTestRoot.Session, options.AgentProvider); + + // Define the workflow builder + WorkflowBuilder builder = new(workflowTestRoot); + + // Connect executors + builder.AddEdge(workflowTestRoot, workflowTest); + builder.AddEdge(workflowTest, addMessage); + + // Build the workflow + return builder.Build(); + } +} \ No newline at end of file diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/AddConversationMessage.yaml b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/AddConversationMessage.yaml new file mode 100644 index 0000000000..7c03153a71 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/AddConversationMessage.yaml @@ -0,0 +1,15 @@ +kind: Workflow +trigger: + + kind: OnConversationStart + id: workflow_test + actions: + + - kind: AddConversationMessage + id: add_message + message: Local.MyMessage1 + role: User + conversationId: =System.ConversationId + content: + - type: Text + value: {Local.TestInput} diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CopyConversationMessages.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CopyConversationMessages.cs new file mode 100644 index 0000000000..e9ca90fd83 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CopyConversationMessages.cs @@ -0,0 +1,95 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// ------------------------------------------------------------------------------ + +#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; + +namespace Test.WorkflowProviders; + +/// +/// This class provides a factory method to create a instance. +/// +/// +/// 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 +/// +public static class WorkflowProvider +{ + /// + /// The root executor for a declarative workflow. + /// + internal sealed class WorkflowTestRootExecutor( + DeclarativeWorkflowOptions options, + Func inputTransform) : + RootExecutor("workflow_test_Root", options, inputTransform) + where TInput : notnull + { + protected override async ValueTask ExecuteAsync(TInput message, IWorkflowContext context, CancellationToken cancellationToken) + { + } + } + + /// + /// Copies one or more messages into the specified agent conversation. + /// + internal sealed class CopyMessagesExecutor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExecutor(id: "copy_messages", session) + { + // + protected override async ValueTask ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken) + { + string? conversationId = await context.ReadStateAsync(key: "ConversationId", scopeName: "System").ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(conversationId)) + { + throw new DeclarativeActionException($"Conversation identifier must be defined: {this.Id}"); + } + ChatMessage[]? messages = await context.EvaluateValueAsync("""[UserMessage("Hello, how can I assist you today?")]""").ConfigureAwait(false); + if (messages is not null) + { + foreach (ChatMessage message in messages) + { + await agentProvider.CreateMessageAsync(conversationId, message, cancellationToken).ConfigureAwait(false); + } + } + return default; + } + } + + public static Workflow CreateWorkflow( + DeclarativeWorkflowOptions options, + Func? inputTransform = null) + where TInput : notnull + { + // Create root executor to initialize the workflow. + inputTransform ??= (message) => DeclarativeWorkflowBuilder.DefaultTransform(message); + WorkflowTestRootExecutor workflowTestRoot = new(options, inputTransform); + DelegateExecutor workflowTest = new(id: "workflow_test", workflowTestRoot.Session); + CopyMessagesExecutor copyMessages = new(workflowTestRoot.Session, options.AgentProvider); + + // Define the workflow builder + WorkflowBuilder builder = new(workflowTestRoot); + + // Connect executors + builder.AddEdge(workflowTestRoot, workflowTest); + builder.AddEdge(workflowTest, copyMessages); + + // Build the workflow + return builder.Build(); + } +} \ No newline at end of file diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CopyConversationMessages.yaml b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CopyConversationMessages.yaml new file mode 100644 index 0000000000..b0af5cf48a --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CopyConversationMessages.yaml @@ -0,0 +1,12 @@ +kind: Workflow +trigger: + + kind: OnConversationStart + id: workflow_test + actions: + + - kind: CopyConversationMessages + id: copy_messages + conversationId: =System.ConversationId + messages: =[UserMessage("Hello, how can I assist you today?")] + diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CreateConversation.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CreateConversation.cs new file mode 100644 index 0000000000..edba442230 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CreateConversation.cs @@ -0,0 +1,88 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// ------------------------------------------------------------------------------ + +#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; + +namespace Test.WorkflowProviders; + +/// +/// This class provides a factory method to create a instance. +/// +/// +/// 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 +/// +public static class WorkflowProvider +{ + /// + /// The root executor for a declarative workflow. + /// + internal sealed class WorkflowTestRootExecutor( + DeclarativeWorkflowOptions options, + Func inputTransform) : + RootExecutor("workflow_test_Root", options, inputTransform) + where TInput : notnull + { + protected override async ValueTask ExecuteAsync(TInput message, IWorkflowContext context, CancellationToken cancellationToken) + { + // Initialize variables + await context.QueueStateUpdateAsync("PrivateConversationId", UnassignedValue.Instance, "Local").ConfigureAwait(false); + } + } + + /// + /// Creates a new conversation and stores the identifier value to the "Local.PrivateConversationId" variable. + /// + internal sealed class ConversationCreateExecutor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExecutor(id: "conversation_create", session) + { + protected override async ValueTask ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken) + { + string conversationId = await agentProvider.CreateConversationAsync(cancellationToken).ConfigureAwait(false); + await context.QueueStateUpdateAsync(key: "PrivateConversationId", value: conversationId, scopeName: "Local").ConfigureAwait(false); + + await context.AddEventAsync(new ConversationUpdateEvent(conversationId)).ConfigureAwait(false); + + return default; + } + } + + public static Workflow CreateWorkflow( + DeclarativeWorkflowOptions options, + Func? inputTransform = null) + where TInput : notnull + { + // Create root executor to initialize the workflow. + inputTransform ??= (message) => DeclarativeWorkflowBuilder.DefaultTransform(message); + WorkflowTestRootExecutor workflowTestRoot = new(options, inputTransform); + DelegateExecutor workflowTest = new(id: "workflow_test", workflowTestRoot.Session); + ConversationCreateExecutor conversationCreate = new(workflowTestRoot.Session, options.AgentProvider); + + // Define the workflow builder + WorkflowBuilder builder = new(workflowTestRoot); + + // Connect executors + builder.AddEdge(workflowTestRoot, workflowTest); + builder.AddEdge(workflowTest, conversationCreate); + + // Build the workflow + return builder.Build(); + } +} \ No newline at end of file diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CreateConversation.yaml b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CreateConversation.yaml new file mode 100644 index 0000000000..8777092ece --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/CreateConversation.yaml @@ -0,0 +1,10 @@ +kind: Workflow +trigger: + + kind: OnConversationStart + id: workflow_test + actions: + + - kind: CreateConversation + id: conversation_create + conversationId: Local.PrivateConversationId diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessage.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessage.cs new file mode 100644 index 0000000000..38bcbbca33 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessage.cs @@ -0,0 +1,91 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// ------------------------------------------------------------------------------ + +#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; + +namespace Test.WorkflowProviders; + +/// +/// This class provides a factory method to create a instance. +/// +/// +/// 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 +/// +public static class WorkflowProvider +{ + /// + /// The root executor for a declarative workflow. + /// + internal sealed class WorkflowTestRootExecutor( + DeclarativeWorkflowOptions options, + Func inputTransform) : + RootExecutor("workflow_test_Root", options, inputTransform) + where TInput : notnull + { + protected override async ValueTask ExecuteAsync(TInput message, IWorkflowContext context, CancellationToken cancellationToken) + { + // Initialize variables + await context.QueueStateUpdateAsync("MyMessage1Copy", UnassignedValue.Instance, "Local").ConfigureAwait(false); + await context.QueueStateUpdateAsync("MyMessageId", UnassignedValue.Instance, "Local").ConfigureAwait(false); + await context.QueueStateUpdateAsync("PrivateConversationId", UnassignedValue.Instance, "Local").ConfigureAwait(false); + } + } + + /// + /// Retrieves a list of messages from an agent conversation. + /// + internal sealed class GetMessageSingleExecutor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExecutor(id: "get_message_single", session) + { + // + protected override async ValueTask ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken) + { + string conversationId = await context.ReadStateAsync(key: "PrivateConversationId", scopeName: "Local").ConfigureAwait(false); + string messageId = await context.ReadStateAsync(key: "MyMessageId", scopeName: "Local").ConfigureAwait(false); + ChatMessage message = await agentProvider.GetMessageAsync(conversationId, messageId, cancellationToken).ConfigureAwait(false); + await context.QueueStateUpdateAsync(key: "MyMessage1Copy", value: message, scopeName: "Local").ConfigureAwait(false); + + return default; + } + } + + public static Workflow CreateWorkflow( + DeclarativeWorkflowOptions options, + Func? inputTransform = null) + where TInput : notnull + { + // Create root executor to initialize the workflow. + inputTransform ??= (message) => DeclarativeWorkflowBuilder.DefaultTransform(message); + WorkflowTestRootExecutor workflowTestRoot = new(options, inputTransform); + DelegateExecutor workflowTest = new(id: "workflow_test", workflowTestRoot.Session); + GetMessageSingleExecutor getMessageSingle = new(workflowTestRoot.Session, options.AgentProvider); + + // Define the workflow builder + WorkflowBuilder builder = new(workflowTestRoot); + + // Connect executors + builder.AddEdge(workflowTestRoot, workflowTest); + builder.AddEdge(workflowTest, getMessageSingle); + + // Build the workflow + return builder.Build(); + } +} \ No newline at end of file diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessage.yaml b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessage.yaml new file mode 100644 index 0000000000..e1658b7e47 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessage.yaml @@ -0,0 +1,12 @@ +kind: Workflow +trigger: + + kind: OnConversationStart + id: workflow_test + actions: + + - kind: RetrieveConversationMessage + id: get_message_single + message: Local.MyMessage1Copy + conversationId: =Local.PrivateConversationId + messageId: =Local.MyMessageId diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessages.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessages.cs new file mode 100644 index 0000000000..de4c56e9e6 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessages.cs @@ -0,0 +1,104 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// ------------------------------------------------------------------------------ + +#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; + +namespace Test.WorkflowProviders; + +/// +/// This class provides a factory method to create a instance. +/// +/// +/// 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 +/// +public static class WorkflowProvider +{ + /// + /// The root executor for a declarative workflow. + /// + internal sealed class WorkflowTestRootExecutor( + DeclarativeWorkflowOptions options, + Func inputTransform) : + RootExecutor("workflow_test_Root", options, inputTransform) + where TInput : notnull + { + protected override async ValueTask ExecuteAsync(TInput message, IWorkflowContext context, CancellationToken cancellationToken) + { + // Initialize variables + await context.QueueStateUpdateAsync("AllMessages", UnassignedValue.Instance, "Local").ConfigureAwait(false); + } + } + + /// + /// Retrieves a specific message from an agent conversation. + /// + internal sealed class GetMessagesAllExecutor(FormulaSession session, WorkflowAgentProvider agentProvider) : ActionExecutor(id: "get_messages_all", session) + { + // + protected override async ValueTask ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken) + { + string conversationId = await context.ReadStateAsync(key: "ConversationId", scopeName: "System").ConfigureAwait(false); + int limit = 20; + string? after = null; + string? before = null; + bool newestFirst = false; + IAsyncEnumerable messagesResult = + agentProvider.GetMessagesAsync( + conversationId, + limit, + after, + before, + newestFirst, + cancellationToken); + List messages = []; + await foreach (ChatMessage message in messagesResult.ConfigureAwait(false)) + { + messages.Add(message); + } + await context.QueueStateUpdateAsync(key: "AllMessages", value: messages, scopeName: "Local").ConfigureAwait(false); + + return default; + } + } + + public static Workflow CreateWorkflow( + DeclarativeWorkflowOptions options, + Func? inputTransform = null) + where TInput : notnull + { + // Create root executor to initialize the workflow. + inputTransform ??= (message) => DeclarativeWorkflowBuilder.DefaultTransform(message); + WorkflowTestRootExecutor workflowTestRoot = new(options, inputTransform); + DelegateExecutor workflowTest = new(id: "workflow_test", workflowTestRoot.Session); + GetMessagesAllExecutor getMessagesAll = new(workflowTestRoot.Session, options.AgentProvider); + + // Define the workflow builder + WorkflowBuilder builder = new(workflowTestRoot); + + // Connect executors + builder.AddEdge(workflowTestRoot, workflowTest); + builder.AddEdge(workflowTest, getMessagesAll); + + // Build the workflow + return builder.Build(); + } +} \ No newline at end of file diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessages.yaml b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessages.yaml new file mode 100644 index 0000000000..e06e0c12a6 --- /dev/null +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.Declarative.UnitTests/Workflows/RetrieveConversationMessages.yaml @@ -0,0 +1,11 @@ +kind: Workflow +trigger: + + kind: OnConversationStart + id: workflow_test + actions: + + - kind: RetrieveConversationMessages + id: get_messages_all + messages: Local.AllMessages + conversationId: =System.ConversationId