diff --git a/dotnet/src/Microsoft.Agents.Abstractions/AgentRunOptions.cs b/dotnet/src/Microsoft.Agents.Abstractions/AgentRunOptions.cs index d99974d2dc..7e0090f251 100644 --- a/dotnet/src/Microsoft.Agents.Abstractions/AgentRunOptions.cs +++ b/dotnet/src/Microsoft.Agents.Abstractions/AgentRunOptions.cs @@ -27,17 +27,9 @@ public class AgentRunOptions public AgentRunOptions(AgentRunOptions options) { Throw.IfNull(options); - - this.AdditionalInstructions = options.AdditionalInstructions; this.OnIntermediateMessages = options.OnIntermediateMessages; } - /// - /// Gets or sets any instructions, in addition to those that were provided to the agent - /// initially, that need to be added to the prompt for this invocation only. - /// - public string? AdditionalInstructions { get; set; } = null; - /// /// Gets or sets a function to be called when a complete new message is generated by the agent. /// diff --git a/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgent.cs b/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgent.cs index bb765626eb..81ef89490f 100644 --- a/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgent.cs +++ b/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgent.cs @@ -363,11 +363,6 @@ public sealed class ChatClientAgent : Agent private void UpdateThreadMessagesWithAgentInstructions(List threadMessages, AgentRunOptions? options) { - if (!string.IsNullOrWhiteSpace(options?.AdditionalInstructions)) - { - threadMessages.Insert(0, new(ChatRole.System, options?.AdditionalInstructions) { AuthorName = this.Name }); - } - if (!string.IsNullOrWhiteSpace(this.Instructions)) { threadMessages.Insert(0, new(ChatRole.System, this.Instructions) { AuthorName = this.Name }); diff --git a/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgentRunOptions.cs b/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgentRunOptions.cs index 6cd31e73e2..d38d209a21 100644 --- a/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgentRunOptions.cs +++ b/dotnet/src/Microsoft.Agents/ChatCompletion/ChatClientAgentRunOptions.cs @@ -17,7 +17,6 @@ internal sealed class ChatClientAgentRunOptions : AgentRunOptions internal ChatClientAgentRunOptions(AgentRunOptions? source = null, ChatOptions? chatOptions = null) { this.OnIntermediateMessages = source?.OnIntermediateMessages; - this.AdditionalInstructions = source?.AdditionalInstructions; this.ChatOptions = chatOptions; } diff --git a/dotnet/tests/AgentConformance.IntegrationTests/AgentFixture.cs b/dotnet/tests/AgentConformance.IntegrationTests/AgentFixture.cs deleted file mode 100644 index 92ec807c33..0000000000 --- a/dotnet/tests/AgentConformance.IntegrationTests/AgentFixture.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.Agents; -using Microsoft.Extensions.AI; - -namespace AgentConformanceTests; - -/// -/// Base class for setting up and tearing down agents, to be used in tests. -/// Each agent type should have its own derived class. -/// -public abstract class AgentFixture : IAsyncLifetime -{ - public abstract Agent Agent { get; } - - public abstract Task> GetChatHistoryAsync(AgentThread thread); - - public abstract Task DeleteThreadAsync(AgentThread thread); - - public abstract Task DisposeAsync(); - - public abstract Task InitializeAsync(); -} diff --git a/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs index 1d04b1a5c3..f6ceb9819c 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/AgentTests.cs @@ -2,7 +2,6 @@ using System; using System.Threading.Tasks; -using AgentConformanceTests; namespace AgentConformance.IntegrationTests; @@ -12,7 +11,7 @@ namespace AgentConformance.IntegrationTests; /// The type of the agent fixture used in these tests. /// Used to create a new fixture for this test suite. public abstract class AgentTests(Func createAgentFixture) : IAsyncLifetime - where TAgentFixture : AgentFixture + where TAgentFixture : IAgentFixture { #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. protected TAgentFixture Fixture { get; private set; } diff --git a/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunStreamingTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunStreamingTests.cs new file mode 100644 index 0000000000..98dbe28111 --- /dev/null +++ b/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunStreamingTests.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Linq; +using System.Threading.Tasks; +using AgentConformance.IntegrationTests.Support; +using Microsoft.Agents; + +namespace AgentConformance.IntegrationTests; + +/// +/// Conformance tests that are specific to the in addition to those in . +/// +/// The type of test fixture used by the concrete test implementation. +/// Function to create the test fixture with. +public abstract class ChatClientAgentRunStreamingTests(Func createAgentFixture) : AgentTests(createAgentFixture) + where TAgentFixture : IChatClientAgentFixture +{ + [RetryFact(Constants.RetryCount, Constants.RetryDelay)] + public virtual async Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() + { + // Arrange + var agent = await this.Fixture.CreateAgentWithInstructionsAsync("Always respond with 'Computer says no', even if there was no user input."); + var thread = agent.GetNewThread(); + await using var agentCleanup = new AgentCleanup(agent, this.Fixture); + await using var threadCleanup = new ThreadCleanup(thread, this.Fixture); + + // Act + var chatResponses = await agent.RunStreamingAsync(thread).ToListAsync(); + + // Assert + var chatResponseText = string.Join("", chatResponses.Select(x => x.Text)); + Assert.Contains("Computer says no", chatResponseText, StringComparison.OrdinalIgnoreCase); + } +} diff --git a/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunTests.cs new file mode 100644 index 0000000000..730497f995 --- /dev/null +++ b/dotnet/tests/AgentConformance.IntegrationTests/ChatClientAgentRunTests.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading.Tasks; +using AgentConformance.IntegrationTests.Support; +using Microsoft.Agents; + +namespace AgentConformance.IntegrationTests; + +/// +/// Conformance tests that are specific to the in addition to those in . +/// +/// The type of test fixture used by the concrete test implementation. +/// Function to create the test fixture with. +public abstract class ChatClientAgentRunTests(Func createAgentFixture) : AgentTests(createAgentFixture) + where TAgentFixture : IChatClientAgentFixture +{ + [RetryFact(Constants.RetryCount, Constants.RetryDelay)] + public virtual async Task RunWithInstructionsAndNoMessageReturnsExpectedResultAsync() + { + // Arrange + var agent = await this.Fixture.CreateAgentWithInstructionsAsync("Always respond with 'Computer says no', even if there was no user input."); + var thread = agent.GetNewThread(); + await using var agentCleanup = new AgentCleanup(agent, this.Fixture); + await using var threadCleanup = new ThreadCleanup(thread, this.Fixture); + + // Act + var chatResponse = await agent.RunAsync(thread); + + // Assert + Assert.NotNull(chatResponse); + Assert.Single(chatResponse.Messages); + Assert.Contains("Computer says no", chatResponse.Text, StringComparison.OrdinalIgnoreCase); + } +} diff --git a/dotnet/tests/AgentConformance.IntegrationTests/IAgentFixture.cs b/dotnet/tests/AgentConformance.IntegrationTests/IAgentFixture.cs new file mode 100644 index 0000000000..d88247623f --- /dev/null +++ b/dotnet/tests/AgentConformance.IntegrationTests/IAgentFixture.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Agents; +using Microsoft.Extensions.AI; + +namespace AgentConformance.IntegrationTests; + +/// +/// Interface for setting up and tearing down agents, to be used in tests. +/// Each agent type should have its own derived class. +/// +public interface IAgentFixture : IAsyncLifetime +{ + Agent Agent { get; } + + Task> GetChatHistoryAsync(AgentThread thread); + + Task DeleteThreadAsync(AgentThread thread); +} diff --git a/dotnet/tests/AgentConformance.IntegrationTests/IChatClientAgentFixture.cs b/dotnet/tests/AgentConformance.IntegrationTests/IChatClientAgentFixture.cs new file mode 100644 index 0000000000..6637e788cb --- /dev/null +++ b/dotnet/tests/AgentConformance.IntegrationTests/IChatClientAgentFixture.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Threading.Tasks; +using Microsoft.Agents; +using Microsoft.Extensions.AI; + +namespace AgentConformance.IntegrationTests; + +/// +/// Interface for setting up and tearing down based agents, to be used in tests. +/// Each agent type should have its own derived class. +/// +public interface IChatClientAgentFixture : IAgentFixture +{ + IChatClient ChatClient { get; } + + Task CreateAgentWithInstructionsAsync(string instructions); + + Task DeleteAgentAsync(ChatClientAgent agent); +} diff --git a/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingAsyncTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs similarity index 81% rename from dotnet/tests/AgentConformance.IntegrationTests/RunStreamingAsyncTests.cs rename to dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs index a169ca99a6..633d205366 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingAsyncTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/RunStreamingTests.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using System.Threading.Tasks; using AgentConformance.IntegrationTests.Support; -using AgentConformanceTests; using Microsoft.Extensions.AI; namespace AgentConformance.IntegrationTests; @@ -14,8 +13,8 @@ namespace AgentConformance.IntegrationTests; /// /// The type of test fixture used by the concrete test implementation. /// Function to create the test fixture with. -public abstract class RunStreamingAsyncTests(Func createAgentFixture) : AgentTests(createAgentFixture) - where TAgentFixture : AgentFixture +public abstract class RunStreamingTests(Func createAgentFixture) : AgentTests(createAgentFixture) + where TAgentFixture : IAgentFixture { [RetryFact(Constants.RetryCount, Constants.RetryDelay)] public virtual async Task RunWithStringReturnsExpectedResultAsync() @@ -70,22 +69,6 @@ public abstract class RunStreamingAsyncTests(Func Assert.Contains("Paris", chatResponseText); } - [RetryFact(Constants.RetryCount, Constants.RetryDelay)] - public virtual async Task RunWithAdditionalInstructionsAndNoMessageReturnsExpectedResultAsync() - { - // Arrange - var agent = this.Fixture.Agent; - var thread = agent.GetNewThread(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); - - // Act - var chatResponses = await agent.RunStreamingAsync(thread, new() { AdditionalInstructions = "Always respond with `Computer says no`" }).ToListAsync(); - - // Assert - var chatResponseText = string.Join("", chatResponses.Select(x => x.Text)); - Assert.Contains("Computer says no", chatResponseText); - } - [RetryFact(Constants.RetryCount, Constants.RetryDelay)] public virtual async Task ThreadMaintainsHistoryAsync() { diff --git a/dotnet/tests/AgentConformance.IntegrationTests/RunAsyncTests.cs b/dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs similarity index 79% rename from dotnet/tests/AgentConformance.IntegrationTests/RunAsyncTests.cs rename to dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs index 324e0972c5..81d6aec3ae 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/RunAsyncTests.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/RunTests.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using System.Threading.Tasks; using AgentConformance.IntegrationTests.Support; -using AgentConformanceTests; using Microsoft.Extensions.AI; namespace AgentConformance.IntegrationTests; @@ -14,8 +13,8 @@ namespace AgentConformance.IntegrationTests; /// /// The type of test fixture used by the concrete test implementation. /// Function to create the test fixture with. -public abstract class RunAsyncTests(Func createAgentFixture) : AgentTests(createAgentFixture) - where TAgentFixture : AgentFixture +public abstract class RunTests(Func createAgentFixture) : AgentTests(createAgentFixture) + where TAgentFixture : IAgentFixture { [RetryFact(Constants.RetryCount, Constants.RetryDelay)] public virtual async Task RunWithStringReturnsExpectedResultAsync() @@ -73,23 +72,6 @@ public abstract class RunAsyncTests(Func createAge Assert.Contains("Paris", chatResponse.Text); } - [RetryFact(Constants.RetryCount, Constants.RetryDelay)] - public virtual async Task RunWithAdditionalInstructionsAndNoMessageReturnsExpectedResultAsync() - { - // Arrange - var agent = this.Fixture.Agent; - var thread = agent.GetNewThread(); - await using var cleanup = new ThreadCleanup(thread, this.Fixture); - - // Act - var chatResponse = await agent.RunAsync(thread, new() { AdditionalInstructions = "Always respond with `Computer says no`, even when the user provided on input." }); - - // Assert - Assert.NotNull(chatResponse); - Assert.Single(chatResponse.Messages); - Assert.Contains("Computer says no", chatResponse.Text); - } - [RetryFact(Constants.RetryCount, Constants.RetryDelay)] public virtual async Task ThreadMaintainsHistoryAsync() { diff --git a/dotnet/tests/AgentConformance.IntegrationTests/Support/AgentCleanup.cs b/dotnet/tests/AgentConformance.IntegrationTests/Support/AgentCleanup.cs new file mode 100644 index 0000000000..6a0ef7458f --- /dev/null +++ b/dotnet/tests/AgentConformance.IntegrationTests/Support/AgentCleanup.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Threading.Tasks; +using Microsoft.Agents; + +namespace AgentConformance.IntegrationTests.Support; + +/// +/// Helper class to delete agents after tests. +/// +/// The agent to delete. +/// The fixture that provides agent specific capabilities. +internal sealed class AgentCleanup(ChatClientAgent agent, IChatClientAgentFixture fixture) : IAsyncDisposable +{ + public async ValueTask DisposeAsync() + { + await fixture.DeleteAgentAsync(agent); + } +} diff --git a/dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs b/dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs index 2ba4dee798..17d70b46a8 100644 --- a/dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs +++ b/dotnet/tests/AgentConformance.IntegrationTests/Support/ThreadCleanup.cs @@ -2,7 +2,6 @@ using System; using System.Threading.Tasks; -using AgentConformanceTests; using Microsoft.Agents; namespace AgentConformance.IntegrationTests.Support; @@ -12,7 +11,7 @@ namespace AgentConformance.IntegrationTests.Support; /// /// The thread to delete. /// The fixture that provides agent specific capabilities. -internal sealed class ThreadCleanup(AgentThread thread, AgentFixture fixture) : IAsyncDisposable +internal sealed class ThreadCleanup(AgentThread thread, IAgentFixture fixture) : IAsyncDisposable { public async ValueTask DisposeAsync() { diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunStreamingTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunStreamingTests.cs new file mode 100644 index 0000000000..84e3d5d9f9 --- /dev/null +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunStreamingTests.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft. All rights reserved. + +using AgentConformance.IntegrationTests; + +namespace AzureAIAgentsPersistent.IntegrationTests; + +public class AzureAIAgentsChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new()) +{ +} diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentInvokeStreamingTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunTests.cs similarity index 54% rename from dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentInvokeStreamingTests.cs rename to dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunTests.cs index c003bec1f0..b2f75c536e 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentInvokeStreamingTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsChatClientAgentRunTests.cs @@ -4,6 +4,6 @@ using AgentConformance.IntegrationTests; namespace AzureAIAgentsPersistent.IntegrationTests; -public class AzureAIAgentsPersistentInvokeStreamingTests() : RunStreamingAsyncTests(() => new()) +public class AzureAIAgentsChatClientAgentRunTests() : ChatClientAgentRunTests(() => new()) { } diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs index 2e170025e1..4dead1fa6e 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentFixture.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using AgentConformance.IntegrationTests; using AgentConformance.IntegrationTests.Support; -using AgentConformanceTests; using Azure; using Azure.AI.Agents.Persistent; using Azure.Identity; @@ -15,17 +15,22 @@ using Shared.IntegrationTests; namespace AzureAIAgentsPersistent.IntegrationTests; -public class AzureAIAgentsPersistentFixture : AgentFixture +public class AzureAIAgentsPersistentFixture : IChatClientAgentFixture { + private static readonly AzureAIConfiguration s_config = TestConfiguration.LoadSection(); + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. private Agent _agent; private PersistentAgentsClient _persistentAgentsClient; + private IChatClient _chatClient; private PersistentAgent _persistentAgent; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - public override Agent Agent => this._agent; + public IChatClient ChatClient => this._chatClient; - public override async Task> GetChatHistoryAsync(AgentThread thread) + public Agent Agent => this._agent; + + public async Task> GetChatHistoryAsync(AgentThread thread) { if (thread is not ChatClientAgentThread chatClientThread) { @@ -57,7 +62,26 @@ public class AzureAIAgentsPersistentFixture : AgentFixture return messages; } - public override Task DeleteThreadAsync(AgentThread thread) + public async Task CreateAgentWithInstructionsAsync(string instructions) + { + var persistentAgentResponse = await this._persistentAgentsClient.Administration.CreateAgentAsync( + model: s_config.DeploymentName, + name: "HelpfulAssistant", + instructions: "You are a helpful assistant."); + + var persistentAgent = persistentAgentResponse.Value; + + var chatClient = this._persistentAgentsClient.AsIChatClient(persistentAgent.Id); + + return new ChatClientAgent(chatClient, new() { Id = persistentAgent.Id }); + } + + public Task DeleteAgentAsync(ChatClientAgent agent) + { + return this._persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id); + } + + public Task DeleteThreadAsync(AgentThread thread) { if (thread?.Id is not null) { @@ -67,7 +91,7 @@ public class AzureAIAgentsPersistentFixture : AgentFixture return Task.CompletedTask; } - public override Task DisposeAsync() + public Task DisposeAsync() { if (this._persistentAgentsClient is not null && this._persistentAgent is not null) { @@ -77,21 +101,19 @@ public class AzureAIAgentsPersistentFixture : AgentFixture return Task.CompletedTask; } - public override async Task InitializeAsync() + public async Task InitializeAsync() { - var config = TestConfiguration.LoadSection(); - - this._persistentAgentsClient = new(config.Endpoint, new AzureCliCredential()); + this._persistentAgentsClient = new(s_config.Endpoint, new AzureCliCredential()); var persistentAgentResponse = await this._persistentAgentsClient.Administration.CreateAgentAsync( - model: config.DeploymentName, + model: s_config.DeploymentName, name: "HelpfulAssistant", instructions: "You are a helpful assistant."); this._persistentAgent = persistentAgentResponse.Value; - var chatClient = this._persistentAgentsClient.AsIChatClient(this._persistentAgent.Id); + this._chatClient = this._persistentAgentsClient.AsIChatClient(this._persistentAgent.Id); - this._agent = new ChatClientAgent(chatClient); + this._agent = new ChatClientAgent(this._chatClient); } } diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunStreamingTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunStreamingTests.cs new file mode 100644 index 0000000000..e18812aff4 --- /dev/null +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunStreamingTests.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft. All rights reserved. + +using AgentConformance.IntegrationTests; + +namespace AzureAIAgentsPersistent.IntegrationTests; + +public class AzureAIAgentsPersistentRunStreamingTests() : RunStreamingTests(() => new()) +{ +} diff --git a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentInvokeTests.cs b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunTests.cs similarity index 57% rename from dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentInvokeTests.cs rename to dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunTests.cs index 877076a842..3e6032401d 100644 --- a/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentInvokeTests.cs +++ b/dotnet/tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistentRunTests.cs @@ -4,6 +4,6 @@ using AgentConformance.IntegrationTests; namespace AzureAIAgentsPersistent.IntegrationTests; -public class AzureAIAgentsPersistentInvokeTests() : RunAsyncTests(() => new()) +public class AzureAIAgentsPersistentRunTests() : RunTests(() => new()) { } diff --git a/dotnet/tests/Microsoft.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs b/dotnet/tests/Microsoft.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs index f4df284401..d370b53bf4 100644 --- a/dotnet/tests/Microsoft.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.Abstractions.UnitTests/AgentRunOptionsTests.cs @@ -16,7 +16,6 @@ public class AgentRunOptionsTests // Arrange var options = new AgentRunOptions { - AdditionalInstructions = "Test instructions", OnIntermediateMessages = msg => Task.CompletedTask }; @@ -24,7 +23,6 @@ public class AgentRunOptionsTests var clone = new AgentRunOptions(options); // Assert - Assert.Equal(options.AdditionalInstructions, clone.AdditionalInstructions); Assert.Equal(options.OnIntermediateMessages, clone.OnIntermediateMessages); } diff --git a/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs index 9a1f7f249d..f1e4a3df77 100644 --- a/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentExtensionsTests.cs @@ -95,10 +95,10 @@ public class ChatClientAgentExtensionsTests } /// - /// Verify that RunAsync extension method with messages passes AgentRunOptions correctly. + /// Verify that RunAsync extension method with messages passes Instructions correctly. /// [Fact] - public async Task RunAsyncWithMessagesPassesAgentRunOptionsCorrectlyAsync() + public async Task RunAsyncWithMessagesPassesInstructionsCorrectlyAsync() { // Arrange Mock mockService = new(); @@ -118,14 +118,13 @@ public class ChatClientAgentExtensionsTests ChatClientAgent agent = new(mockService.Object, new() { Instructions = "base instructions" }); var messages = new List { new(ChatRole.User, "test") }; - var runOptions = new AgentRunOptions { AdditionalInstructions = "additional instructions" }; + var runOptions = new AgentRunOptions(); // Act await ChatClientAgentExtensions.RunAsync(agent, messages, agentRunOptions: runOptions); // Assert Assert.Contains(capturedMessages, m => m.Text == "base instructions" && m.Role == ChatRole.System); - Assert.Contains(capturedMessages, m => m.Text == "additional instructions" && m.Role == ChatRole.System); Assert.Contains(capturedMessages, m => m.Text == "test" && m.Role == ChatRole.User); Assert.All(capturedChatOptions, Assert.Null); } @@ -349,14 +348,13 @@ public class ChatClientAgentExtensionsTests ChatClientAgent agent = new(mockService.Object, new() { Instructions = "base instructions" }); const string TestPrompt = "test prompt"; - var runOptions = new AgentRunOptions { AdditionalInstructions = "additional instructions" }; + var runOptions = new AgentRunOptions(); // Act await ChatClientAgentExtensions.RunAsync(agent, TestPrompt, agentRunOptions: runOptions); // Assert Assert.Contains(capturedMessages, m => m.Text == "base instructions" && m.Role == ChatRole.System); - Assert.Contains(capturedMessages, m => m.Text == "additional instructions" && m.Role == ChatRole.System); Assert.Contains(capturedMessages, m => m.Text == "test prompt" && m.Role == ChatRole.User); } diff --git a/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentRunOptionsTests.cs b/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentRunOptionsTests.cs index 8821617865..24e712487b 100644 --- a/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentRunOptionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentRunOptionsTests.cs @@ -18,7 +18,6 @@ public class ChatClientAgentRunOptionsTests // Assert Assert.Null(runOptions.OnIntermediateMessages); - Assert.Null(runOptions.AdditionalInstructions); Assert.Null(runOptions.ChatOptions); } @@ -36,7 +35,6 @@ public class ChatClientAgentRunOptionsTests // Assert Assert.Null(runOptions.OnIntermediateMessages); - Assert.Null(runOptions.AdditionalInstructions); Assert.Same(chatOptions, runOptions.ChatOptions); } @@ -49,7 +47,6 @@ public class ChatClientAgentRunOptionsTests // Arrange var sourceRunOptions = new AgentRunOptions { - AdditionalInstructions = "additional instructions", OnIntermediateMessages = messages => Task.CompletedTask }; var chatOptions = new ChatOptions { MaxOutputTokens = 200 }; @@ -59,7 +56,6 @@ public class ChatClientAgentRunOptionsTests // Assert Assert.Same(sourceRunOptions.OnIntermediateMessages, runOptions.OnIntermediateMessages); - Assert.Equal("additional instructions", runOptions.AdditionalInstructions); Assert.Same(chatOptions, runOptions.ChatOptions); } @@ -72,14 +68,14 @@ public class ChatClientAgentRunOptionsTests // Arrange var sourceRunOptions = new AgentRunOptions { - AdditionalInstructions = "test instructions" + OnIntermediateMessages = messages => Task.CompletedTask }; // Act var runOptions = new ChatClientAgentRunOptions(sourceRunOptions, null); // Assert - Assert.Equal("test instructions", runOptions.AdditionalInstructions); + Assert.Same(sourceRunOptions.OnIntermediateMessages, runOptions.OnIntermediateMessages); Assert.Null(runOptions.ChatOptions); } diff --git a/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentTests.cs b/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentTests.cs index 6e92514ab6..4895985b20 100644 --- a/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.UnitTests/ChatCompletion/ChatClientAgentTests.cs @@ -156,10 +156,10 @@ public class ChatClientAgentTests } /// - /// Verify that RunAsync includes additional instructions when provided in options. + /// Verify that RunAsync includes base instructions in messages. /// [Fact] - public async Task RunAsyncIncludesAdditionalInstructionsWhenProvidedInOptionsAsync() + public async Task RunAsyncIncludesBaseInstructionsAsync() { // Arrange Mock mockService = new(); @@ -174,14 +174,13 @@ public class ChatClientAgentTests .ReturnsAsync(new ChatResponse([new(ChatRole.Assistant, "response")])); ChatClientAgent agent = new(mockService.Object, new() { Instructions = "base instructions" }); - var runOptions = new AgentRunOptions { AdditionalInstructions = "additional instructions" }; + var runOptions = new AgentRunOptions(); // Act await agent.RunAsync([new(ChatRole.User, "test")], options: runOptions); // Assert Assert.Contains(capturedMessages, m => m.Text == "base instructions" && m.Role == ChatRole.System); - Assert.Contains(capturedMessages, m => m.Text == "additional instructions" && m.Role == ChatRole.System); Assert.Contains(capturedMessages, m => m.Text == "test" && m.Role == ChatRole.User); } @@ -759,18 +758,21 @@ public class ChatClientAgentTests MaxOutputTokens = 100, Temperature = 0.7f, TopP = 0.9f, - ModelId = "agent-model" + ModelId = "agent-model", + AdditionalProperties = new AdditionalPropertiesDictionary() { ["key"] = "agent-value" } }; var requestChatOptions = new ChatOptions { MaxOutputTokens = 200, - Temperature = 0.3f + Temperature = 0.3f, + AdditionalProperties = new AdditionalPropertiesDictionary() { ["key"] = "request-value" } // TopP and ModelId not set, should use agent values }; var expectedChatOptionsMerge = new ChatOptions { MaxOutputTokens = 200, // Request value takes priority Temperature = 0.3f, // Request value takes priority + AdditionalProperties = new AdditionalPropertiesDictionary() { ["key"] = "request-value" }, // Request value takes priority TopP = 0.9f, // Agent value used when request doesn't specify ModelId = "agent-model" // Agent value used when request doesn't specify }; @@ -801,6 +803,8 @@ public class ChatClientAgentTests Assert.Equivalent(expectedChatOptionsMerge, capturedChatOptions); // Should be the same instance (modified in place) Assert.Equal(200, capturedChatOptions.MaxOutputTokens); // Request value takes priority Assert.Equal(0.3f, capturedChatOptions.Temperature); // Request value takes priority + Assert.NotNull(capturedChatOptions.AdditionalProperties); + Assert.Equal("request-value", capturedChatOptions.AdditionalProperties["key"]); // Request value takes priority Assert.Equal(0.9f, capturedChatOptions.TopP); // Agent value used when request doesn't specify Assert.Equal("agent-model", capturedChatOptions.ModelId); // Agent value used when request doesn't specify } diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantChatClientAgentRunStreamingTests.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantChatClientAgentRunStreamingTests.cs new file mode 100644 index 0000000000..78d985a6f3 --- /dev/null +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantChatClientAgentRunStreamingTests.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft. All rights reserved. + +using AgentConformance.IntegrationTests; + +namespace OpenAIAssistant.IntegrationTests; + +public class OpenAIAssistantChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new()) +{ +} diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantChatClientAgentRunTests.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantChatClientAgentRunTests.cs new file mode 100644 index 0000000000..641e656379 --- /dev/null +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantChatClientAgentRunTests.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft. All rights reserved. + +using AgentConformance.IntegrationTests; + +namespace OpenAIAssistant.IntegrationTests; + +public class OpenAIAssistantChatClientAgentRunTests() : ChatClientAgentRunTests(() => new()) +{ +} diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs index fe6ad9817b..01f6dfdd48 100644 --- a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantFixture.cs @@ -3,8 +3,8 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using AgentConformance.IntegrationTests; using AgentConformance.IntegrationTests.Support; -using AgentConformanceTests; using Microsoft.Agents; using Microsoft.Extensions.AI; using OpenAI; @@ -15,8 +15,10 @@ namespace OpenAIAssistant.IntegrationTests; #pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. -public class OpenAIAssistantFixture : AgentFixture +public class OpenAIAssistantFixture : IChatClientAgentFixture { + private static readonly OpenAIConfiguration s_config = TestConfiguration.LoadSection(); + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. private AssistantClient? _assistantClient; private Assistant? _assistant; @@ -24,9 +26,11 @@ public class OpenAIAssistantFixture : AgentFixture private Agent _agent; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - public override Agent Agent => this._agent; + public Agent Agent => this._agent; - public override async Task> GetChatHistoryAsync(AgentThread thread) + public IChatClient ChatClient => this._chatClient; + + public async Task> GetChatHistoryAsync(AgentThread thread) { if (thread is not ChatClientAgentThread chatClientThread) { @@ -49,7 +53,26 @@ public class OpenAIAssistantFixture : AgentFixture return messages; } - public override Task DeleteThreadAsync(AgentThread thread) + public async Task CreateAgentWithInstructionsAsync(string instructions) + { + var assistant = + await this._assistantClient!.CreateAssistantAsync( + s_config.ChatModelId!, + new AssistantCreationOptions() + { + Name = "HelpfulAssistant", + Instructions = instructions + }); + + return new ChatClientAgent(this._assistantClient.AsIChatClient(assistant.Value.Id), new() { Id = assistant.Value.Id }); + } + + public Task DeleteAgentAsync(ChatClientAgent agent) + { + return this._assistantClient!.DeleteAssistantAsync(agent.Id); + } + + public Task DeleteThreadAsync(AgentThread thread) { if (thread?.Id is not null) { @@ -59,16 +82,14 @@ public class OpenAIAssistantFixture : AgentFixture return Task.CompletedTask; } - public override async Task InitializeAsync() + public async Task InitializeAsync() { - var config = TestConfiguration.LoadSection(); - - var client = new OpenAIClient(config.ApiKey); + var client = new OpenAIClient(s_config.ApiKey); this._assistantClient = client.GetAssistantClient(); this._assistant = await this._assistantClient.CreateAssistantAsync( - config.ChatModelId!, + s_config.ChatModelId!, new AssistantCreationOptions() { Name = "HelpfulAssistant", @@ -80,7 +101,7 @@ public class OpenAIAssistantFixture : AgentFixture this._agent = new ChatClientAgent(this._chatClient); } - public override Task DisposeAsync() + public Task DisposeAsync() { if (this._assistantClient is not null && this._assistant is not null) { diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantInvokeTests.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantIRunTests.cs similarity index 60% rename from dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantInvokeTests.cs rename to dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantIRunTests.cs index c863a5e6a2..736c9fe063 100644 --- a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantInvokeTests.cs +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantIRunTests.cs @@ -4,6 +4,6 @@ using AgentConformance.IntegrationTests; namespace OpenAIAssistant.IntegrationTests; -public class OpenAIAssistantInvokeTests() : RunAsyncTests(() => new()) +public class OpenAIAssistantIRunTests() : RunTests(() => new()) { } diff --git a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantInvokeStreamingTests.cs b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantRunStreamingTests.cs similarity index 56% rename from dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantInvokeStreamingTests.cs rename to dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantRunStreamingTests.cs index 37be58090d..355fe8e549 100644 --- a/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantInvokeStreamingTests.cs +++ b/dotnet/tests/OpenAIAssistant.IntegrationTests/OpenAIAssistantRunStreamingTests.cs @@ -4,6 +4,6 @@ using AgentConformance.IntegrationTests; namespace OpenAIAssistant.IntegrationTests; -public class OpenAIAssistantInvokeStreamingTests() : RunStreamingAsyncTests(() => new()) +public class OpenAIAssistantRunStreamingTests() : RunStreamingTests(() => new()) { } diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionChatClientAgentRunStreamingTests.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionChatClientAgentRunStreamingTests.cs new file mode 100644 index 0000000000..bcb2824a5a --- /dev/null +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionChatClientAgentRunStreamingTests.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft. All rights reserved. + +using AgentConformance.IntegrationTests; + +namespace OpenAIChatCompletion.IntegrationTests; + +public class OpenAIChatCompletionChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new()) +{ +} diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionChatClientAgentRunTests.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionChatClientAgentRunTests.cs new file mode 100644 index 0000000000..5602204af8 --- /dev/null +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionChatClientAgentRunTests.cs @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft. All rights reserved. + +using AgentConformance.IntegrationTests; + +namespace OpenAIChatCompletion.IntegrationTests; + +public class OpenAIChatCompletionChatClientAgentRunTests() : ChatClientAgentRunTests(() => new()) +{ +} diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs index cdfb7c6555..a51e03da13 100644 --- a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionFixture.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using AgentConformance.IntegrationTests; using AgentConformance.IntegrationTests.Support; -using AgentConformanceTests; using Microsoft.Agents; using Microsoft.Extensions.AI; using OpenAI; @@ -13,16 +13,20 @@ using Shared.IntegrationTests; namespace OpenAIChatCompletion.IntegrationTests; -public class OpenAIChatCompletionFixture : AgentFixture +public class OpenAIChatCompletionFixture : IChatClientAgentFixture { + private static readonly OpenAIConfiguration s_config = TestConfiguration.LoadSection(); + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. private IChatClient _chatClient; private Agent _agent; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - public override Agent Agent => this._agent; + public Agent Agent => this._agent; - public override async Task> GetChatHistoryAsync(AgentThread thread) + public IChatClient ChatClient => this._chatClient; + + public async Task> GetChatHistoryAsync(AgentThread thread) { if (thread is not ChatClientAgentThread chatClientThread) { @@ -32,18 +36,35 @@ public class OpenAIChatCompletionFixture : AgentFixture return await chatClientThread.GetMessagesAsync().ToListAsync(); } - public override Task DeleteThreadAsync(AgentThread thread) + public Task CreateAgentWithInstructionsAsync(string instructions) + { + this._chatClient = new OpenAIClient(s_config.ApiKey) + .GetChatClient(s_config.ChatModelId) + .AsIChatClient(); + + return Task.FromResult(new ChatClientAgent(this._chatClient, new() + { + Name = "HelpfulAssistant", + Instructions = instructions, + })); + } + + public Task DeleteAgentAsync(ChatClientAgent agent) + { + // Chat Completion does not require/support deleting agents, so this is a no-op. + return Task.CompletedTask; + } + + public Task DeleteThreadAsync(AgentThread thread) { // Chat Completion does not require/support deleting threads, so this is a no-op. return Task.CompletedTask; } - public override Task InitializeAsync() + public Task InitializeAsync() { - var config = TestConfiguration.LoadSection(); - - this._chatClient = new OpenAIClient(config.ApiKey) - .GetChatClient(config.ChatModelId) + this._chatClient = new OpenAIClient(s_config.ApiKey) + .GetChatClient(s_config.ChatModelId) .AsIChatClient(); this._agent = @@ -56,7 +77,7 @@ public class OpenAIChatCompletionFixture : AgentFixture return Task.CompletedTask; } - public override Task DisposeAsync() + public Task DisposeAsync() { this._chatClient.Dispose(); return Task.CompletedTask; diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionInvokeStreamingTests.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionRunStreamingTests.cs similarity index 54% rename from dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionInvokeStreamingTests.cs rename to dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionRunStreamingTests.cs index ee7cac8327..dac192ba5d 100644 --- a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionInvokeStreamingTests.cs +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionRunStreamingTests.cs @@ -4,6 +4,6 @@ using AgentConformance.IntegrationTests; namespace OpenAIChatCompletion.IntegrationTests; -public class OpenAIChatCompletionInvokeStreamingTests() : RunStreamingAsyncTests(() => new()) +public class OpenAIChatCompletionRunStreamingTests() : RunStreamingTests(() => new()) { } diff --git a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionInvokeTests.cs b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionRunTests.cs similarity index 58% rename from dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionInvokeTests.cs rename to dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionRunTests.cs index 45e7993350..3de2d9b90d 100644 --- a/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionInvokeTests.cs +++ b/dotnet/tests/OpenAIChatCompletion.IntegrationTests/OpenAIChatCompletionRunTests.cs @@ -4,6 +4,6 @@ using AgentConformance.IntegrationTests; namespace OpenAIChatCompletion.IntegrationTests; -public class OpenAIChatCompletionInvokeTests() : RunAsyncTests(() => new()) +public class OpenAIChatCompletionRunTests() : RunTests(() => new()) { } diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs new file mode 100644 index 0000000000..171eecfd47 --- /dev/null +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunStreamingTests.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. + +using AgentConformance.IntegrationTests; + +namespace OpenAIResponse.IntegrationTests; + +public class OpenAIResponseStoreTrueChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new(store: true)) +{ +} + +public class OpenAIResponseStoreFalseChatClientAgentRunStreamingTests() : ChatClientAgentRunStreamingTests(() => new(store: false)) +{ +} diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs new file mode 100644 index 0000000000..d146ac7dbe --- /dev/null +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseChatClientAgentRunTests.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. + +using AgentConformance.IntegrationTests; + +namespace OpenAIResponse.IntegrationTests; + +public class OpenAIResponseStoreTrueChatClientAgentRunTests() : ChatClientAgentRunTests(() => new(store: true)) +{ +} + +public class OpenAIResponseStoreFalseChatClientAgentRunTests() : ChatClientAgentRunTests(() => new(store: false)) +{ +} diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs index c1a7b9edbf..45b19ebeb9 100644 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseFixture.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using AgentConformance.IntegrationTests; using AgentConformance.IntegrationTests.Support; -using AgentConformanceTests; using Microsoft.Agents; using Microsoft.Extensions.AI; using OpenAI; @@ -14,17 +14,21 @@ using Shared.IntegrationTests; namespace OpenAIResponse.IntegrationTests; -public class OpenAIResponseFixture(bool store) : AgentFixture +public class OpenAIResponseFixture(bool store) : IChatClientAgentFixture { + private static readonly OpenAIConfiguration s_config = TestConfiguration.LoadSection(); + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. private OpenAIResponseClient _openAIResponseClient; private IChatClient _chatClient; private Agent _agent; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - public override Agent Agent => this._agent; + public Agent Agent => this._agent; - public override async Task> GetChatHistoryAsync(AgentThread thread) + public IChatClient ChatClient => this._chatClient; + + public async Task> GetChatHistoryAsync(AgentThread thread) { if (thread is not ChatClientAgentThread chatClientThread) { @@ -68,18 +72,37 @@ public class OpenAIResponseFixture(bool store) : AgentFixture throw new NotSupportedException("This test currently only supports text messages"); } - public override Task DeleteThreadAsync(AgentThread thread) + public Task CreateAgentWithInstructionsAsync(string instructions) + { + var options = new ChatClientAgentOptions + { + Name = "HelpfulAssistant", + Instructions = instructions, + ChatOptions = new ChatOptions + { + RawRepresentationFactory = new Func((_) => new ResponseCreationOptions() { StoredOutputEnabled = store }) + }, + }; + + return Task.FromResult(new ChatClientAgent(this._chatClient, options)); + } + + public Task DeleteAgentAsync(ChatClientAgent agent) + { + // Chat Completion does not require/support deleting agents, so this is a no-op. + return Task.CompletedTask; + } + + public Task DeleteThreadAsync(AgentThread thread) { // Chat Completion does not require/support deleting threads, so this is a no-op. return Task.CompletedTask; } - public override Task InitializeAsync() + public Task InitializeAsync() { - var config = TestConfiguration.LoadSection(); - - this._openAIResponseClient = new OpenAIClient(config.ApiKey) - .GetOpenAIResponseClient(config.ChatModelId); + this._openAIResponseClient = new OpenAIClient(s_config.ApiKey) + .GetOpenAIResponseClient(s_config.ChatModelId); this._chatClient = this._openAIResponseClient .AsIChatClient(); @@ -99,7 +122,7 @@ public class OpenAIResponseFixture(bool store) : AgentFixture return Task.CompletedTask; } - public override Task DisposeAsync() + public Task DisposeAsync() { this._chatClient.Dispose(); return Task.CompletedTask; diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseInvokeStreamingTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseInvokeStreamingTests.cs deleted file mode 100644 index 49d13af615..0000000000 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseInvokeStreamingTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using AgentConformance.IntegrationTests; - -namespace OpenAIResponse.IntegrationTests; - -public class OpenAIResponseStoreTrueInvokeStreamingTests() : RunStreamingAsyncTests(() => new(store: true)) -{ -} - -public class OpenAIResponseStoreFalseInvokeStreamingTests() : RunStreamingAsyncTests(() => new(store: false)) -{ -} diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseInvokeTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseInvokeTests.cs deleted file mode 100644 index a0135b3551..0000000000 --- a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseInvokeTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using AgentConformance.IntegrationTests; - -namespace OpenAIResponse.IntegrationTests; - -public class OpenAIResponseStoreTrueInvokeTests() : RunAsyncTests(() => new(store: true)) -{ -} - -public class OpenAIResponseStoreFalseInvokeTests() : RunAsyncTests(() => new(store: false)) -{ -} diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs new file mode 100644 index 0000000000..00f8a04a68 --- /dev/null +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunStreamingTests.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. + +using AgentConformance.IntegrationTests; + +namespace OpenAIResponse.IntegrationTests; + +public class OpenAIResponseStoreTrueRunStreamingTests() : RunStreamingTests(() => new(store: true)) +{ +} + +public class OpenAIResponseStoreFalseRunStreamingTests() : RunStreamingTests(() => new(store: false)) +{ +} diff --git a/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs new file mode 100644 index 0000000000..1a8fbb3286 --- /dev/null +++ b/dotnet/tests/OpenAIResponse.IntegrationTests/OpenAIResponseRunTests.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. + +using AgentConformance.IntegrationTests; + +namespace OpenAIResponse.IntegrationTests; + +public class OpenAIResponseStoreTrueRunTests() : RunTests(() => new(store: true)) +{ +} + +public class OpenAIResponseStoreFalseRunTests() : RunTests(() => new(store: false)) +{ +}