Skip to content

.Net: Start to add integration tests #12272

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions dotnet/src/Agents/OpenAI/OpenAIResponseAgentInvokeOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Microsoft.SemanticKernel.Agents.OpenAI;

/// <summary>
/// Optional parameters for <see cref="OpenAIResponseAgent"/> invocation.
/// </summary>
public sealed class OpenAIResponseAgentInvokeOptions : AgentInvokeOptions
{
/// <summary>
/// Initializes a new instance of the <see cref="OpenAIResponseAgentInvokeOptions"/> class.
/// </summary>
public OpenAIResponseAgentInvokeOptions()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="OpenAIResponseAgentInvokeOptions"/> class by cloning the provided options.
/// </summary>
/// <param name="options">The options to clone.</param>
public OpenAIResponseAgentInvokeOptions(AgentInvokeOptions options)
: base(options)
{
Verify.NotNull(options);
}

/// <summary>
/// Initializes a new instance of the <see cref="OpenAIResponseAgentInvokeOptions"/> class by cloning the provided options.
/// </summary>
/// <param name="options">The options to clone.</param>
public OpenAIResponseAgentInvokeOptions(OpenAIResponseAgentInvokeOptions options)
: base(options)
{
Verify.NotNull(options);
}
}
15 changes: 11 additions & 4 deletions dotnet/src/Agents/OpenAI/OpenAIResponseAgentThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,28 @@ public OpenAIResponseAgentThread(OpenAIResponseClient client, string responseId)
}

/// <inheritdoc />
protected override Task DeleteInternalAsync(CancellationToken cancellationToken = default)
protected async override Task DeleteInternalAsync(CancellationToken cancellationToken = default)
{
if (this._isDeleted)
{
return Task.CompletedTask;
return;
}

if (this.ResponseId is null)
{
throw new InvalidOperationException("This thread cannot be deleted, since it has not been created.");
}

this._isDeleted = true;
try
{
await this._client.DeleteResponseAsync(this.ResponseId, cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
throw new AgentThreadOperationException("The thread could not be deleted due to an error response from the service.", ex);
}

return Task.CompletedTask;
this._isDeleted = true;
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Threading.Tasks;
using Xunit;

namespace SemanticKernel.IntegrationTests.Agents.CommonInterfaceConformance.AgentThreadConformance;

public class OpenAIResponseAgentThreadTests() : AgentThreadTests(() => new OpenAIResponseAgentFixture())
{
[Fact]
public override Task OnNewMessageWithServiceFailureThrowsAgentOperationExceptionAsync()
{
// Test not applicable since we cannot add a message to the thread we can only respond to a message.
return Task.CompletedTask;
}

[Fact]
public override Task UsingThreadBeforeCreateCreatesAsync()
{
// Test not applicable since we cannot create a thread we can only respond to a message.
return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) Microsoft. All rights reserved.
using System.ClientModel;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;
using OpenAI;
using OpenAI.Responses;
using SemanticKernel.IntegrationTests.TestSettings;

namespace SemanticKernel.IntegrationTests.Agents.CommonInterfaceConformance;

/// <summary>
/// Contains setup and teardown for the <see cref="OpenAIResponseAgent"/> tests.
/// </summary>
public class OpenAIResponseAgentFixture : AgentFixture
{
private readonly IConfigurationRoot _configuration = new ConfigurationBuilder()
.AddJsonFile(path: "testsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile(path: "testsettings.development.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddUserSecrets<OpenAIResponseAgentFixture>()
.Build();

private OpenAIResponseClient? _responseClient;
private OpenAIResponseAgent? _agent;
private OpenAIResponseAgentThread? _thread;
private OpenAIResponseAgentThread? _createdThread;
private OpenAIResponseAgentThread? _serviceFailingAgentThread;
private OpenAIResponseAgentThread? _createdServiceFailingAgentThread;

public OpenAIResponseClient ResponseClient => this._responseClient!;

public override Agent Agent => this._agent!;

public override AgentThread AgentThread => this._thread!;

public override AgentThread CreatedAgentThread => this._createdThread!;

public override AgentThread ServiceFailingAgentThread => this._serviceFailingAgentThread!;

public override AgentThread CreatedServiceFailingAgentThread => this._createdServiceFailingAgentThread!;

public override AgentThread GetNewThread()
{
return new OpenAIResponseAgentThread(this._responseClient!);
}

public override async Task<ChatHistory> GetChatHistory()
{
var chatHistory = new ChatHistory();
await foreach (var existingMessage in this._thread!.GetMessagesAsync().ConfigureAwait(false))
{
chatHistory.Add(existingMessage);
}
return chatHistory;
}

public override async Task DisposeAsync()
{
if (this._thread!.Id is not null)
{
try
{
await this._responseClient!.DeleteResponseAsync(this._thread!.Id);
}
catch (ClientResultException ex) when (ex.Status == 404)
{
}
}

if (this._createdThread!.Id is not null)
{
try
{
await this._responseClient!.DeleteResponseAsync(this._createdThread!.Id);
}
catch (ClientResultException ex) when (ex.Status == 404)
{
}
}
}

public override Task DeleteThread(AgentThread thread)
{
return this._responseClient!.DeleteResponseAsync(thread.Id);
}

public override async Task InitializeAsync()
{
OpenAIConfiguration configuration = this._configuration.GetSection("OpenAI").Get<OpenAIConfiguration>()!;
var options = new OpenAIClientOptions();
this._responseClient = new(model: configuration.ChatModelId, credential: new ApiKeyCredential(configuration.ApiKey), options: options);

var kernelBuilder = Kernel.CreateBuilder();
Kernel kernel = kernelBuilder.Build();

this._agent = new OpenAIResponseAgent(this._responseClient)
{
Name = "HelpfulAssistant",
Instructions = "You are a helpful assistant.",
Kernel = kernel
};
this._thread = new OpenAIResponseAgentThread(this._responseClient);

var response = await this._responseClient.CreateResponseAsync([ResponseItem.CreateUserMessageItem("Hello")]);
this._createdThread = new OpenAIResponseAgentThread(this._responseClient, response.Value.Id);

var serviceFailingClient = new OpenAIResponseClient(configuration.ModelId, credential: new ApiKeyCredential("FakeApiKey"), options: options);
this._serviceFailingAgentThread = new OpenAIResponseAgentThread(serviceFailingClient);

this._createdServiceFailingAgentThread = new OpenAIResponseAgentThread(this._responseClient, "FakeId");
}
}
Loading
Loading