chore: import upstream snapshot with attribution
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Microsoft.Agents.AI.DurableTask.State;
|
||||
using Microsoft.DurableTask;
|
||||
using Microsoft.DurableTask.Client;
|
||||
using Microsoft.DurableTask.Client.Entities;
|
||||
using Microsoft.DurableTask.Entities;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using OpenAI.Chat;
|
||||
|
||||
namespace Microsoft.Agents.AI.DurableTask.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for scenarios where an external client interacts with Durable Task Agents.
|
||||
/// </summary>
|
||||
[Collection("Sequential")]
|
||||
[Trait("Category", "Integration")]
|
||||
public sealed class AgentEntityTests(ITestOutputHelper outputHelper) : IDisposable
|
||||
{
|
||||
private const string DisabledDueToFailingCiJob = "Disabled due to persistent CI failures. See #6732.";
|
||||
|
||||
private static readonly TimeSpan s_defaultTimeout = Debugger.IsAttached
|
||||
? TimeSpan.FromMinutes(5)
|
||||
: TimeSpan.FromSeconds(30);
|
||||
|
||||
private static readonly IConfiguration s_configuration =
|
||||
new ConfigurationBuilder()
|
||||
.AddUserSecrets(Assembly.GetExecutingAssembly())
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
private readonly ITestOutputHelper _outputHelper = outputHelper;
|
||||
private readonly CancellationTokenSource _cts = new(delay: s_defaultTimeout);
|
||||
|
||||
private CancellationToken TestTimeoutToken => this._cts.Token;
|
||||
|
||||
public void Dispose() => this._cts.Dispose();
|
||||
|
||||
[Fact(Skip = DisabledDueToFailingCiJob)]
|
||||
public async Task EntityNamePrefixAsync()
|
||||
{
|
||||
// Setup
|
||||
AIAgent simpleAgent = TestHelper.GetAzureOpenAIChatClient(s_configuration).AsAIAgent(
|
||||
name: "TestAgent",
|
||||
instructions: "You are a helpful assistant that always responds with a friendly greeting."
|
||||
);
|
||||
|
||||
using TestHelper testHelper = TestHelper.Start([simpleAgent], this._outputHelper);
|
||||
|
||||
// A proxy agent is needed to call the hosted test agent
|
||||
AIAgent simpleAgentProxy = simpleAgent.AsDurableAgentProxy(testHelper.Services);
|
||||
|
||||
AgentSession session = await simpleAgentProxy.CreateSessionAsync(this.TestTimeoutToken);
|
||||
|
||||
DurableTaskClient client = testHelper.GetClient();
|
||||
|
||||
AgentSessionId sessionId = session.GetService<AgentSessionId>();
|
||||
EntityInstanceId expectedEntityId = new($"dafx-{simpleAgent.Name}", sessionId.Key);
|
||||
|
||||
EntityMetadata? entity = await client.Entities.GetEntityAsync(expectedEntityId, false, this.TestTimeoutToken);
|
||||
|
||||
Assert.Null(entity);
|
||||
|
||||
// Act: send a prompt to the agent
|
||||
await simpleAgentProxy.RunAsync(
|
||||
message: "Hello!",
|
||||
session,
|
||||
cancellationToken: this.TestTimeoutToken);
|
||||
|
||||
// Assert: verify the agent state was stored with the correct entity name prefix
|
||||
entity = await client.Entities.GetEntityAsync(expectedEntityId, true, this.TestTimeoutToken);
|
||||
|
||||
Assert.NotNull(entity);
|
||||
Assert.True(entity.IncludesState);
|
||||
|
||||
DurableAgentState state = entity.State.ReadAs<DurableAgentState>();
|
||||
|
||||
DurableAgentStateRequest request = Assert.Single(state.Data.ConversationHistory.OfType<DurableAgentStateRequest>());
|
||||
|
||||
Assert.Null(request.OrchestrationId);
|
||||
}
|
||||
|
||||
[Theory(Skip = DisabledDueToFailingCiJob)]
|
||||
[InlineData("run")]
|
||||
[InlineData("Run")]
|
||||
[InlineData("RunAgentAsync")]
|
||||
public async Task RunAgentMethodNamesAllWorkAsync(string runAgentMethodName)
|
||||
{
|
||||
// Setup
|
||||
AIAgent simpleAgent = TestHelper.GetAzureOpenAIChatClient(s_configuration).AsAIAgent(
|
||||
name: "TestAgent",
|
||||
instructions: "You are a helpful assistant that always responds with a friendly greeting."
|
||||
);
|
||||
|
||||
using TestHelper testHelper = TestHelper.Start([simpleAgent], this._outputHelper);
|
||||
|
||||
// A proxy agent is needed to call the hosted test agent
|
||||
AIAgent simpleAgentProxy = simpleAgent.AsDurableAgentProxy(testHelper.Services);
|
||||
|
||||
AgentSession session = await simpleAgentProxy.CreateSessionAsync(this.TestTimeoutToken);
|
||||
|
||||
DurableTaskClient client = testHelper.GetClient();
|
||||
|
||||
AgentSessionId sessionId = session.GetService<AgentSessionId>();
|
||||
EntityInstanceId expectedEntityId = new($"dafx-{simpleAgent.Name}", sessionId.Key);
|
||||
|
||||
EntityMetadata? entity = await client.Entities.GetEntityAsync(expectedEntityId, false, this.TestTimeoutToken);
|
||||
|
||||
Assert.Null(entity);
|
||||
|
||||
// Act: send a prompt to the agent
|
||||
await client.Entities.SignalEntityAsync(
|
||||
expectedEntityId,
|
||||
runAgentMethodName,
|
||||
new RunRequest("Hello!"),
|
||||
cancellation: this.TestTimeoutToken);
|
||||
|
||||
while (!this.TestTimeoutToken.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(500, this.TestTimeoutToken);
|
||||
|
||||
// Assert: verify the agent state was stored with the correct entity name prefix
|
||||
entity = await client.Entities.GetEntityAsync(expectedEntityId, true, this.TestTimeoutToken);
|
||||
|
||||
if (entity is not null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.NotNull(entity);
|
||||
Assert.True(entity.IncludesState);
|
||||
|
||||
DurableAgentState state = entity.State.ReadAs<DurableAgentState>();
|
||||
|
||||
DurableAgentStateRequest request = Assert.Single(state.Data.ConversationHistory.OfType<DurableAgentStateRequest>());
|
||||
|
||||
Assert.Null(request.OrchestrationId);
|
||||
}
|
||||
|
||||
[Fact(Skip = DisabledDueToFailingCiJob)]
|
||||
public async Task OrchestrationIdSetDuringOrchestrationAsync()
|
||||
{
|
||||
// Arrange
|
||||
AIAgent simpleAgent = TestHelper.GetAzureOpenAIChatClient(s_configuration).AsAIAgent(
|
||||
name: "TestAgent",
|
||||
instructions: "You are a helpful assistant that always responds with a friendly greeting."
|
||||
);
|
||||
|
||||
using TestHelper testHelper = TestHelper.Start(
|
||||
[simpleAgent],
|
||||
this._outputHelper,
|
||||
registry => registry.AddOrchestrator<TestOrchestrator>());
|
||||
|
||||
DurableTaskClient client = testHelper.GetClient();
|
||||
|
||||
// Act
|
||||
string orchestrationId = await client.ScheduleNewOrchestrationInstanceAsync(nameof(TestOrchestrator), "What is the capital of Maine?");
|
||||
|
||||
OrchestrationMetadata? status = await client.WaitForInstanceCompletionAsync(
|
||||
orchestrationId,
|
||||
true,
|
||||
this.TestTimeoutToken);
|
||||
|
||||
// Assert
|
||||
EntityInstanceId expectedEntityId = AgentSessionId.Parse(status.ReadOutputAs<string>()!);
|
||||
|
||||
EntityMetadata? entity = await client.Entities.GetEntityAsync(expectedEntityId, true, this.TestTimeoutToken);
|
||||
|
||||
Assert.NotNull(entity);
|
||||
Assert.True(entity.IncludesState);
|
||||
|
||||
DurableAgentState state = entity.State.ReadAs<DurableAgentState>();
|
||||
|
||||
DurableAgentStateRequest request = Assert.Single(state.Data.ConversationHistory.OfType<DurableAgentStateRequest>());
|
||||
|
||||
Assert.Equal(orchestrationId, request.OrchestrationId);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1812:Avoid uninstantiated internal classes", Justification = "Constructed via reflection.")]
|
||||
private sealed class TestOrchestrator : TaskOrchestrator<string, string>
|
||||
{
|
||||
public override async Task<string> RunAsync(TaskOrchestrationContext context, string input)
|
||||
{
|
||||
DurableAIAgent writer = context.GetAgent("TestAgent");
|
||||
AgentSession writerSession = await writer.CreateSessionAsync();
|
||||
|
||||
await writer.RunAsync(
|
||||
message: context.GetInput<string>()!,
|
||||
session: writerSession);
|
||||
|
||||
AgentSessionId sessionId = writerSession.GetService<AgentSessionId>();
|
||||
|
||||
return sessionId.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user