mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET Workflows - WIP Declarative action update (#1761)
* WIP * Fixed build errors (#1638) Comment and nullable type alignment * Sync to SDK update * Checkpoint * Checkpoint: Tests passing * Checkpoint: EndWorkflow * Add trace * .NET: Azure.AI.Agents Package Split + Initial Extensions (#1657) * Move packages * Update nuget.config * Address Xmldoc * Remove format from branches checks * Address Xmldocs * Add more details to the implementation * Moving Agent logic to ChatClient * Adding Name and Id overrides to AzureAIAgent * Updating extensions * Add GetAiAgent extensions * Adding support for version as name can conflict 409 using the Agents API with same name * Addressing more updates to the extensions * More improvements * Remove debugging code from sample * Address copilot feedback * Apply suggestions from co-pilot code review * Checkpoint * Update Directory.Packages.props Fix package version rollback: Azure.AI.Agents.Persistent (beta-6 => beta-7) * Add project reference * .NET: Add comprehensive unit tests for Microsoft.Agents.AI.AzureAIAgents extension methods (#1786) * Initial plan * Add comprehensive unit test project for Microsoft.Agents.AI.AzureAIAgents Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Add README documenting test project and package dependency requirements Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Fix documentation URL to use learn.microsoft.com Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> * Bump back AAAP 1.2.0-beta.7 * Address AI generated UT's * Remove UT Readme * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: rogerbarreto <19890735+rogerbarreto@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * .NET: Change model to be required just for prompt agent definition specific extensions (#1812) * Remove unneeded model from extensions * Add noop justification * Update Package Nameing: V1 -> AzureAI.Persistent / V2 -> AzureAI (#1829) * Checkpoint for merge * No build errors * .NET: Update Extensions for Strict Agent Definitions + Improvements (#1892) * Update Package Nameing: V1 -> AzureAI.Persistent / V2 -> AzureAI * Update agents and extensions to comply with strict agent definitions * More static updates * Address UT, and ResponseTool support * Improving reusability extensions * Addressing ResponseTools Unit Tests and extension setup * Adapted workaround on breaking AAA with OpenAI 2.6.0 * Small updates * Remove strictness when retrieving agents, improved XmlDocs * Improve sample comments * Update dotnet/tests/Microsoft.Agents.AI.AzureAI.UnitTests/AgentsClientExtensionsTests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Address PR comments * Address UT failing * Address Copilot feedback * Address Copilot feedback * Address comment typo * Address PR feedback * Address typo * Add missing Extensions with ChatClientAgentOptions * Address comments --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Updated package version (#1897) * Version update (#1901) * Checkpoint * Updated package version (#1906) * Checkpoint * Checkpoint * Checkpoint * Align with azure ai agent * Update dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Workflows/Declarative/MCPToolApproval/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Refactored external input * Update dotnet/samples/GettingStarted/Workflows/Declarative/MCPToolApproval/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Agent tools patch * Demos validated * Checkpoint * Hygiene * Checkpoint - Samples * Update dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Checkpoint * Checkpoint - Deep Research * Update baseline * Update * Typo * Checkpoint * Typos * Sample cleanup * Update dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/AzureAgentProvider.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/src/Microsoft.Agents.AI.AzureAI/AgentsClientExtensions.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Workflows/Declarative/FunctionTools/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Workflows/Declarative/ToolApproval/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update dotnet/samples/GettingStarted/Workflows/Declarative/DeepResearch/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Typo * Typo * Fix input loop * Sample - Function Calling / External Input * Typo * Finessed * Checkpoint * Fix feed * Checkpoint - so close * Ding dong! * "there" *** * Fixup comments * Fix sample * Code analysis * Header * Typo (variableName) * Remove dead code * Skip test (agent api ratchet) * Comment * Update dotnet/samples/GettingStarted/Workflows/Declarative/StudentTeacher/Program.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Typo --------- Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com>
This commit is contained in:
co-authored by
rogerbarreto
Copilot
copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Copilot
Dmytro Struk
parent
7c3d4fcf30
commit
2b869c2396
+2
-1
@@ -15,6 +15,7 @@ public sealed class DeclarativeEjectionTest(ITestOutputHelper output) : Workflow
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("AddConversationMessage.yaml")]
|
||||
[InlineData("CancelWorkflow.yaml")]
|
||||
[InlineData("ClearAllVariables.yaml")]
|
||||
[InlineData("CopyConversationMessages.yaml")]
|
||||
[InlineData("Condition.yaml")]
|
||||
@@ -23,7 +24,7 @@ public sealed class DeclarativeEjectionTest(ITestOutputHelper output) : Workflow
|
||||
[InlineData("EditTable.yaml")]
|
||||
[InlineData("EditTableV2.yaml")]
|
||||
[InlineData("EndConversation.yaml")]
|
||||
[InlineData("EndDialog.yaml")]
|
||||
[InlineData("EndWorkflow.yaml")]
|
||||
[InlineData("Goto.yaml")]
|
||||
[InlineData("InvokeAgent.yaml")]
|
||||
[InlineData("LoopBreak.yaml")]
|
||||
|
||||
-16
@@ -44,18 +44,6 @@ public class InvokeAzureAgentTemplateTest(ITestOutputHelper output) : WorkflowAc
|
||||
BoolExpression.Expression("1 < 2"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AdditionalInstructions()
|
||||
{
|
||||
// Act, Assert
|
||||
this.ExecuteTest(
|
||||
nameof(VariableConversation),
|
||||
StringExpression.Literal("asst_123abc"),
|
||||
StringExpression.Variable(PropertyPath.TopicVariable("TestConversation")),
|
||||
"MyMessages",
|
||||
additionalInstructions: "Test instructions...");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InputMessagesVariable()
|
||||
{
|
||||
@@ -86,7 +74,6 @@ public class InvokeAzureAgentTemplateTest(ITestOutputHelper output) : WorkflowAc
|
||||
StringExpression.Builder conversation,
|
||||
string? messagesVariable = null,
|
||||
BoolExpression.Builder? autoSend = null,
|
||||
string? additionalInstructions = null,
|
||||
ValueExpression.Builder? messages = null)
|
||||
{
|
||||
// Arrange
|
||||
@@ -97,7 +84,6 @@ public class InvokeAzureAgentTemplateTest(ITestOutputHelper output) : WorkflowAc
|
||||
conversation,
|
||||
messagesVariable,
|
||||
autoSend,
|
||||
additionalInstructions is null ? null : (TemplateLine.Builder)TemplateLine.Parse(additionalInstructions),
|
||||
messages);
|
||||
|
||||
// Act
|
||||
@@ -117,7 +103,6 @@ public class InvokeAzureAgentTemplateTest(ITestOutputHelper output) : WorkflowAc
|
||||
StringExpression.Builder conversation,
|
||||
string? messagesVariable = null,
|
||||
BoolExpression.Builder? autoSend = null,
|
||||
TemplateLine.Builder? additionalInstructions = null,
|
||||
ValueExpression.Builder? messages = null)
|
||||
{
|
||||
InitializablePropertyPath? outputMessages = null;
|
||||
@@ -141,7 +126,6 @@ public class InvokeAzureAgentTemplateTest(ITestOutputHelper output) : WorkflowAc
|
||||
new AzureAgentInput.Builder
|
||||
{
|
||||
Messages = messages,
|
||||
AdditionalInstructions = additionalInstructions,
|
||||
},
|
||||
Output =
|
||||
new AzureAgentOutput.Builder
|
||||
|
||||
+3
-4
@@ -145,8 +145,9 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("CancelWorkflow.yaml", 1, "end_all")]
|
||||
[InlineData("EndConversation.yaml", 1, "end_all")]
|
||||
[InlineData("EndDialog.yaml", 1, "end_all")]
|
||||
[InlineData("EndWorkflow.yaml", 1, "end_all")]
|
||||
[InlineData("EditTable.yaml", 2, "edit_var")]
|
||||
[InlineData("EditTableV2.yaml", 2, "edit_var")]
|
||||
[InlineData("ParseValue.yaml", 2, "parse_var")]
|
||||
@@ -170,8 +171,6 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
[InlineData(typeof(AdaptiveCardPrompt.Builder))]
|
||||
[InlineData(typeof(BeginDialog.Builder))]
|
||||
[InlineData(typeof(CSATQuestion.Builder))]
|
||||
[InlineData(typeof(CancelAllDialogs.Builder))]
|
||||
[InlineData(typeof(CancelDialog.Builder))]
|
||||
[InlineData(typeof(CreateSearchQuery.Builder))]
|
||||
[InlineData(typeof(DeleteActivity.Builder))]
|
||||
[InlineData(typeof(DisableTrigger.Builder))]
|
||||
@@ -231,7 +230,7 @@ public sealed class DeclarativeWorkflowTest(ITestOutputHelper output) : Workflow
|
||||
[InlineData("Condition.yaml", "setVariable_test")]
|
||||
[InlineData("ConditionElse.yaml", "setVariable_test")]
|
||||
[InlineData("EndConversation.yaml", "end_all")]
|
||||
[InlineData("EndDialog.yaml", "end_all")]
|
||||
[InlineData("EndWorkflow.yaml", "end_all")]
|
||||
[InlineData("EditTable.yaml", "edit_var")]
|
||||
[InlineData("EditTableV2.yaml", "edit_var")]
|
||||
[InlineData("Goto.yaml", "goto_end")]
|
||||
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Events;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Verify <see cref="AgentFunctionToolRequest"/> class
|
||||
/// </summary>
|
||||
public sealed class AgentFunctionToolRequestTest(ITestOutputHelper output) : EventTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public void VerifySerializationEmpty()
|
||||
{
|
||||
// Arrange & Act
|
||||
AgentFunctionToolRequest copy = VerifyEventSerialization(new AgentFunctionToolRequest("testagent", []));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("testagent", copy.AgentName);
|
||||
Assert.Empty(copy.FunctionCalls);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifySerializationWithRequests()
|
||||
{
|
||||
// Arrange & Act
|
||||
AgentFunctionToolRequest copy =
|
||||
VerifyEventSerialization(
|
||||
new AgentFunctionToolRequest(
|
||||
"agent",
|
||||
[
|
||||
new FunctionCallContent("call1", "result1"),
|
||||
new FunctionCallContent("call2", "result2", new Dictionary<string, object?>() { { "name", "Clam Chowder" } }),
|
||||
]));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("agent", copy.AgentName);
|
||||
Assert.Equal(2, copy.FunctionCalls.Count);
|
||||
Assert.IsType<FunctionCallContent>(copy.FunctionCalls[0]);
|
||||
Assert.Null(copy.FunctionCalls[0].Arguments);
|
||||
Assert.IsType<FunctionCallContent>(copy.FunctionCalls[1]);
|
||||
Assert.NotNull(copy.FunctionCalls[1].Arguments);
|
||||
Assert.NotEmpty(copy.FunctionCalls[1].Arguments!);
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Events;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Verify <see cref="AgentFunctionToolResponse"/> class
|
||||
/// </summary>
|
||||
public sealed class AgentFunctionToolResponseTest(ITestOutputHelper output) : EventTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public void VerifySerializationEmpty()
|
||||
{
|
||||
// Arrange & Act
|
||||
AgentFunctionToolResponse copy = VerifyEventSerialization(new AgentFunctionToolResponse("testagent", []));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("testagent", copy.AgentName);
|
||||
Assert.Empty(copy.FunctionResults);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifySerializationWithResults()
|
||||
{
|
||||
// Arrange & Act
|
||||
AgentFunctionToolResponse copy =
|
||||
VerifyEventSerialization(
|
||||
new AgentFunctionToolResponse(
|
||||
"agent",
|
||||
[
|
||||
new FunctionResultContent("call1", "result1"),
|
||||
new FunctionResultContent("call2", "result2"),
|
||||
]));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("agent", copy.AgentName);
|
||||
Assert.Equal(2, copy.FunctionResults.Count);
|
||||
Assert.IsType<FunctionResultContent>(copy.FunctionResults[0]);
|
||||
Assert.Equal("call1", copy.FunctionResults[0].CallId);
|
||||
Assert.IsType<FunctionResultContent>(copy.FunctionResults[1]);
|
||||
Assert.Equal("call2", copy.FunctionResults[1].CallId);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
@@ -19,4 +20,17 @@ public abstract class EventTest(ITestOutputHelper output) : WorkflowTest(output)
|
||||
Assert.NotNull(copy);
|
||||
return copy;
|
||||
}
|
||||
|
||||
protected static void AssertMessage(ChatMessage source, ChatMessage copy)
|
||||
{
|
||||
Assert.Equal(source.Role, copy.Role);
|
||||
Assert.Equal(source.Text, copy.Text);
|
||||
Assert.Equal(source.Contents.Count, copy.Contents.Count);
|
||||
}
|
||||
|
||||
protected static TContent AssertContent<TContent>(ChatMessage message) where TContent : AIContent
|
||||
{
|
||||
TContent[] contents = message.Contents.OfType<TContent>().ToArray();
|
||||
return Assert.Single(contents);
|
||||
}
|
||||
}
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Events;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Verify <see cref="ExternalInputRequest"/> class
|
||||
/// </summary>
|
||||
public sealed class ExternalInputRequestTest(ITestOutputHelper output) : EventTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public void VerifySerializationWithText()
|
||||
{
|
||||
// Arrange
|
||||
ExternalInputRequest source = new(new AgentRunResponse(new ChatMessage(ChatRole.User, "Wassup?")));
|
||||
|
||||
// Act
|
||||
ExternalInputRequest copy = VerifyEventSerialization(source);
|
||||
|
||||
// Assert
|
||||
ChatMessage messageCopy = Assert.Single(source.AgentResponse.Messages);
|
||||
AssertMessage(messageCopy, copy.AgentResponse.Messages[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifySerializationWithRequests()
|
||||
{
|
||||
// Arrange
|
||||
ExternalInputRequest source =
|
||||
new(new AgentRunResponse(
|
||||
new ChatMessage(
|
||||
ChatRole.Assistant,
|
||||
[
|
||||
new McpServerToolApprovalRequestContent("call1", new McpServerToolCallContent("call1", "testmcp", "server-name")),
|
||||
new FunctionApprovalRequestContent("call2", new FunctionCallContent("call2", "result1")),
|
||||
new FunctionCallContent("call3", "myfunc"),
|
||||
new TextContent("Heya"),
|
||||
])));
|
||||
|
||||
// Act
|
||||
ExternalInputRequest copy = VerifyEventSerialization(source);
|
||||
|
||||
// Assert
|
||||
ChatMessage messageCopy = Assert.Single(source.AgentResponse.Messages);
|
||||
Assert.Equal(messageCopy.Contents.Count, copy.AgentResponse.Messages[0].Contents.Count);
|
||||
|
||||
McpServerToolApprovalRequestContent mcpRequest = AssertContent<McpServerToolApprovalRequestContent>(messageCopy);
|
||||
Assert.Equal("call1", mcpRequest.Id);
|
||||
|
||||
FunctionApprovalRequestContent functionRequest = AssertContent<FunctionApprovalRequestContent>(messageCopy);
|
||||
Assert.Equal("call2", functionRequest.Id);
|
||||
|
||||
FunctionCallContent functionCall = AssertContent<FunctionCallContent>(messageCopy);
|
||||
Assert.Equal("call3", functionCall.CallId);
|
||||
|
||||
TextContent textContent = AssertContent<TextContent>(messageCopy);
|
||||
Assert.Equal("Heya", textContent.Text);
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Events;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Verify <see cref="ExternalInputResponse"/> class
|
||||
/// </summary>
|
||||
public sealed class ExternalInputResponseTest(ITestOutputHelper output) : EventTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public void VerifySerializationEmpty()
|
||||
{
|
||||
// Arrange
|
||||
ExternalInputResponse source = new(new ChatMessage(ChatRole.User, "Wassup?"));
|
||||
|
||||
// Act
|
||||
ExternalInputResponse copy = VerifyEventSerialization(source);
|
||||
|
||||
// Assert
|
||||
ChatMessage messageCopy = Assert.Single(source.Messages);
|
||||
AssertMessage(messageCopy, copy.Messages[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifySerializationWithResponses()
|
||||
{
|
||||
// Arrange
|
||||
ExternalInputResponse source =
|
||||
new(new ChatMessage(
|
||||
ChatRole.Assistant,
|
||||
[
|
||||
new McpServerToolApprovalRequestContent("call1", new McpServerToolCallContent("call1", "testmcp", "server-name")).CreateResponse(approved: true),
|
||||
new FunctionApprovalRequestContent("call2", new FunctionCallContent("call2", "result1")).CreateResponse(approved: true),
|
||||
new FunctionResultContent("call3", 33),
|
||||
new TextContent("Heya"),
|
||||
]));
|
||||
|
||||
// Act
|
||||
ExternalInputResponse copy = VerifyEventSerialization(source);
|
||||
|
||||
// Assert
|
||||
ChatMessage responseMessage = Assert.Single(source.Messages);
|
||||
Assert.Equal(responseMessage.Contents.Count, copy.Messages[0].Contents.Count);
|
||||
|
||||
McpServerToolApprovalResponseContent mcpApproval = AssertContent<McpServerToolApprovalResponseContent>(responseMessage);
|
||||
Assert.Equal("call1", mcpApproval.Id);
|
||||
|
||||
FunctionApprovalResponseContent functionApproval = AssertContent<FunctionApprovalResponseContent>(responseMessage);
|
||||
Assert.Equal("call2", functionApproval.Id);
|
||||
|
||||
FunctionResultContent functionResult = AssertContent<FunctionResultContent>(responseMessage);
|
||||
Assert.Equal("call3", functionResult.CallId);
|
||||
|
||||
TextContent textContent = AssertContent<TextContent>(responseMessage);
|
||||
Assert.Equal("Heya", textContent.Text);
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Events;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Verify <see cref="UserInputRequest"/> class
|
||||
/// </summary>
|
||||
public sealed class UserInputRequestTest(ITestOutputHelper output) : EventTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public void VerifySerializationEmpty()
|
||||
{
|
||||
// Arrange & Act
|
||||
UserInputRequest copy = VerifyEventSerialization(new UserInputRequest("test agent", []));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test agent", copy.AgentName);
|
||||
Assert.Empty(copy.InputRequests);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifySerializationWithRequests()
|
||||
{
|
||||
// Arrange & Act
|
||||
UserInputRequest copy =
|
||||
VerifyEventSerialization(
|
||||
new UserInputRequest(
|
||||
"agent",
|
||||
[
|
||||
new McpServerToolApprovalRequestContent("call1", new McpServerToolCallContent("call1", "testmcp", "server-name")),
|
||||
new FunctionApprovalRequestContent("call2", new FunctionCallContent("call2", "result1")),
|
||||
]));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("agent", copy.AgentName);
|
||||
Assert.Equal(2, copy.InputRequests.Count);
|
||||
McpServerToolApprovalRequestContent mcpRequest = Assert.IsType<McpServerToolApprovalRequestContent>(copy.InputRequests[0]);
|
||||
Assert.Equal("call1", mcpRequest.Id);
|
||||
FunctionApprovalRequestContent functionRequest = Assert.IsType<FunctionApprovalRequestContent>(copy.InputRequests[1]);
|
||||
Assert.Equal("call2", functionRequest.Id);
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Events;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Verify <see cref="UserInputResponse"/> class
|
||||
/// </summary>
|
||||
public sealed class UserInputResponseTest(ITestOutputHelper output) : EventTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public void VerifySerializationEmpty()
|
||||
{
|
||||
// Arrange & Act
|
||||
UserInputResponse copy = VerifyEventSerialization(new UserInputResponse("testagent", []));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("testagent", copy.AgentName);
|
||||
Assert.Empty(copy.InputResponses);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifySerializationWithResponses()
|
||||
{
|
||||
// Arrange & Act
|
||||
UserInputResponse copy =
|
||||
VerifyEventSerialization(
|
||||
new UserInputResponse(
|
||||
"agent",
|
||||
[
|
||||
new McpServerToolApprovalRequestContent("call1", new McpServerToolCallContent("call1", "testmcp", "server-name")).CreateResponse(approved: true),
|
||||
new FunctionApprovalRequestContent("call2", new FunctionCallContent("call2", "result1")).CreateResponse(approved: true),
|
||||
]));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("agent", copy.AgentName);
|
||||
Assert.Equal(2, copy.InputResponses.Count);
|
||||
McpServerToolApprovalResponseContent mcpResponse = Assert.IsType<McpServerToolApprovalResponseContent>(copy.InputResponses[0]);
|
||||
Assert.Equal("call1", mcpResponse.Id);
|
||||
FunctionApprovalResponseContent functionResponse = Assert.IsType<FunctionApprovalResponseContent>(copy.InputResponses[1]);
|
||||
Assert.Equal("call2", functionResponse.Id);
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Events;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Verify <see cref="AnswerRequest"/> class
|
||||
/// </summary>
|
||||
public sealed class UserMessageRequestTest(ITestOutputHelper output) : EventTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public void VerifySerialization()
|
||||
{
|
||||
// Arrange & Act
|
||||
AnswerRequest copy = VerifyEventSerialization(new AnswerRequest("wassup"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("wassup", copy.Prompt);
|
||||
}
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using Microsoft.Agents.AI.Workflows.Declarative.Events;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.Agents.AI.Workflows.Declarative.UnitTests.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Verify <see cref="AnswerResponse"/> class
|
||||
/// </summary>
|
||||
public sealed class UserMessageResponseTest(ITestOutputHelper output) : EventTest(output)
|
||||
{
|
||||
[Fact]
|
||||
public void VerifySerializationText()
|
||||
{
|
||||
// Arrange & Act
|
||||
AnswerResponse copy = VerifyEventSerialization(new AnswerResponse("test response"));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test response", copy.Value.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VerifySerializationMessage()
|
||||
{
|
||||
// Arrange & Act
|
||||
AnswerResponse copy = VerifyEventSerialization(new AnswerResponse(new ChatMessage(ChatRole.User, "test response")));
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test response", copy.Value.Text);
|
||||
}
|
||||
}
|
||||
+32
@@ -58,6 +58,38 @@ public sealed class JsonDocumentExtensionsTests
|
||||
Assert.Equal(expectedTimeSpan, result["time"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseRecord_Object_NoSchema_Succeeds()
|
||||
{
|
||||
// Arrange
|
||||
JsonDocument document = JsonDocument.Parse(
|
||||
"""
|
||||
{
|
||||
"text": "hello",
|
||||
"numberInt": 7,
|
||||
"numberLong": 9223372036854775807,
|
||||
"numberDecimal": 12.5,
|
||||
"numberDouble": 3.99E99,
|
||||
"flag": true,
|
||||
"date": "2024-10-01T12:34:56Z",
|
||||
"time": "12:34:56"
|
||||
}
|
||||
""");
|
||||
|
||||
// Act
|
||||
Dictionary<string, object?> result = document.ParseRecord(VariableType.RecordType);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("hello", result["text"]);
|
||||
Assert.Equal(7, result["numberInt"]);
|
||||
Assert.Equal(9223372036854775807L, result["numberLong"]);
|
||||
Assert.Equal(12.5m, result["numberDecimal"]);
|
||||
Assert.Equal(3.99E99, result["numberDouble"]);
|
||||
Assert.Equal(true, result["flag"]);
|
||||
Assert.Equal("2024-10-01T12:34:56Z", result["date"]);
|
||||
Assert.Equal("12:34:56", result["time"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseRecord_Object_NestedRecord_Succeeds()
|
||||
{
|
||||
|
||||
+6
-6
@@ -14,8 +14,8 @@ public sealed class TemplateExtensionsTests
|
||||
{
|
||||
// Arrange
|
||||
RecalcEngine engine = new();
|
||||
IEnumerable<TemplateLine> template = new List<TemplateLine>
|
||||
{
|
||||
IEnumerable<TemplateLine> template =
|
||||
[
|
||||
new TemplateLine.Builder
|
||||
{
|
||||
Segments =
|
||||
@@ -24,7 +24,7 @@ public sealed class TemplateExtensionsTests
|
||||
new TextSegment.Builder { Value = "World" }
|
||||
}
|
||||
}.Build()
|
||||
};
|
||||
];
|
||||
|
||||
// Act
|
||||
string result = engine.Format(template);
|
||||
@@ -38,8 +38,8 @@ public sealed class TemplateExtensionsTests
|
||||
{
|
||||
// Arrange
|
||||
RecalcEngine engine = new();
|
||||
IEnumerable<TemplateLine> template = new List<TemplateLine>
|
||||
{
|
||||
IEnumerable<TemplateLine> template =
|
||||
[
|
||||
new TemplateLine.Builder
|
||||
{
|
||||
Segments =
|
||||
@@ -54,7 +54,7 @@ public sealed class TemplateExtensionsTests
|
||||
new TextSegment.Builder { Value = "Line 2" }
|
||||
}
|
||||
}.Build()
|
||||
};
|
||||
];
|
||||
|
||||
// Act
|
||||
string result = engine.Format(template);
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
kind: Workflow
|
||||
trigger:
|
||||
|
||||
kind: OnConversationStart
|
||||
id: my_workflow
|
||||
actions:
|
||||
|
||||
- kind: CancelWorkflow
|
||||
id: end_all
|
||||
|
||||
- kind: SendActivity
|
||||
id: send_activity_1
|
||||
activity: NEVER 1!
|
||||
+1
-1
@@ -21,7 +21,7 @@ trigger:
|
||||
- id: condition_match
|
||||
condition: =Local.TestValue1 + Local.TestValue2 = 7
|
||||
actions:
|
||||
- kind: EndDialog
|
||||
- kind: EndWorkflow
|
||||
id: end_when_match
|
||||
|
||||
- kind: SendActivity
|
||||
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// <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;
|
||||
|
||||
namespace Test.WorkflowProviders;
|
||||
|
||||
/// <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 WorkflowProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The root executor for a declarative workflow.
|
||||
/// </summary>
|
||||
internal sealed class MyWorkflowRootExecutor<TInput>(
|
||||
DeclarativeWorkflowOptions options,
|
||||
Func<TInput, ChatMessage> inputTransform) :
|
||||
RootExecutor<TInput>("my_workflow_Root", options, inputTransform)
|
||||
where TInput : notnull
|
||||
{
|
||||
protected override async ValueTask ExecuteAsync(TInput message, IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats a message template and sends an activity event.
|
||||
/// </summary>
|
||||
internal sealed class SendActivity1Executor(FormulaSession session) : ActionExecutor(id: "send_activity_1", session)
|
||||
{
|
||||
// <inheritdoc />
|
||||
protected override async ValueTask<object?> ExecuteAsync(IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
string activityText =
|
||||
await context.FormatTemplateAsync(
|
||||
"""
|
||||
NEVER 1!
|
||||
"""
|
||||
);
|
||||
AgentRunResponse response = new([new ChatMessage(ChatRole.Assistant, activityText)]);
|
||||
await context.AddEventAsync(new AgentRunResponseEvent(this.Id, response)).ConfigureAwait(false);
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
MyWorkflowRootExecutor<TInput> myWorkflowRoot = new(options, inputTransform);
|
||||
DelegateExecutor myWorkflow = new(id: "my_workflow", myWorkflowRoot.Session);
|
||||
DelegateExecutor endAll = new(id: "end_all", myWorkflowRoot.Session);
|
||||
DelegateExecutor endAllRestart = new(id: "end_all_Restart", myWorkflowRoot.Session);
|
||||
SendActivity1Executor sendActivity1 = new(myWorkflowRoot.Session);
|
||||
|
||||
// Define the workflow builder
|
||||
WorkflowBuilder builder = new(myWorkflowRoot);
|
||||
|
||||
// Connect executors
|
||||
builder.AddEdge(myWorkflowRoot, myWorkflow);
|
||||
builder.AddEdge(myWorkflow, endAll);
|
||||
builder.AddEdge(endAllRestart, sendActivity1);
|
||||
|
||||
// Build the workflow
|
||||
return builder.Build(validateOrphans: false);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ trigger:
|
||||
id: my_workflow
|
||||
actions:
|
||||
|
||||
- kind: EndDialog
|
||||
- kind: EndWorkflow
|
||||
id: end_all
|
||||
|
||||
- kind: SendActivity
|
||||
-2
@@ -68,7 +68,6 @@ public static class WorkflowProvider
|
||||
|
||||
string? conversationId = await context.ReadStateAsync<string>(key: "ConversationId", scopeName: "System").ConfigureAwait(false);
|
||||
bool autoSend = true;
|
||||
string? additionalInstructions = null;
|
||||
IList<ChatMessage>? inputMessages = await context.EvaluateListAsync<ChatMessage>("[UserMessage(System.LastMessageText)]").ConfigureAwait(false);
|
||||
|
||||
AgentRunResponse agentResponse =
|
||||
@@ -77,7 +76,6 @@ public static class WorkflowProvider
|
||||
agentName,
|
||||
conversationId,
|
||||
autoSend,
|
||||
additionalInstructions,
|
||||
inputMessages,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user