Skip to content
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

Add extension method for OpenAISettingsBuilder #192

Merged
merged 1 commit into from
Aug 15, 2024
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using AzureOpenAIProxy.ApiApp.Builders;
using AzureOpenAIProxy.ApiApp.Configurations;

namespace AzureOpenAIProxy.ApiApp.Extensions;

/// <summary>
/// This represents the extensions entity for the OpenAI settings builder.
/// </summary>
public static class OpenAISettingsBuilderExtensions
{
/// <summary>
/// Sets the list of <see cref="OpenAIInstanceSettings"/> instances from the app settings.
/// </summary>
/// <param name="builder"><see cref="IOpenAISettingsBuilder"/> instance.</param>
/// <param name="sp"><see cref="IServiceProvider"/> instance.</param>
/// <returns>Returns <see cref="IOpenAISettingsBuilder"/> instance.</returns>
public static IOpenAISettingsBuilder WithAppSettings(this IOpenAISettingsBuilder builder, IServiceProvider sp)
{
var configuration = sp.GetService<IConfiguration>()
?? throw new InvalidOperationException($"{nameof(IConfiguration)} service is not registered.");

var settings = configuration.GetSection(AzureSettings.Name).GetSection(OpenAISettings.Name).Get<OpenAISettings>()
?? throw new InvalidOperationException($"{nameof(OpenAISettings)} could not be retrieved from the configuration.");

builder.SetOpenAIInstances(settings.Instances);

return builder;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using AzureOpenAIProxy.ApiApp.Configurations;
using AzureOpenAIProxy.ApiApp.Builders;
using AzureOpenAIProxy.ApiApp.Configurations;
using AzureOpenAIProxy.ApiApp.Filters;
using AzureOpenAIProxy.ApiApp.Services;

Expand All @@ -20,16 +21,10 @@ public static IServiceCollection AddOpenAISettings(this IServiceCollection servi
{
services.AddSingleton<OpenAISettings>(sp =>
{
//var settings = new OpenAISettingsBuilder()
// .WithAppSettings(sp)
// .WithKeyVault(sp)
// .Build();

var configuration = sp.GetService<IConfiguration>()
?? throw new InvalidOperationException($"{nameof(IConfiguration)} service is not registered.");

var settings = configuration.GetSection(AzureSettings.Name).GetSection(OpenAISettings.Name).Get<OpenAISettings>()
?? throw new InvalidOperationException($"{nameof(OpenAISettings)} could not be retrieved from the configuration.");
var settings = new OpenAISettingsBuilder()
.WithAppSettings(sp)
//.WithKeyVault(sp)
.Build();

return settings;
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
using AzureOpenAIProxy.ApiApp.Builders;
using AzureOpenAIProxy.ApiApp.Extensions;

using FluentAssertions;

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

using NSubstitute;

namespace AzureOpenAIProxy.ApiApp.Tests.Extensions;

public class OpenAISettingsBuilderExtensionsTests
{
[Fact]
public void Given_Null_AppSettings_When_Invoked_WithAppSettings_Then_It_Should_Throw_Exception()
{
// Arrange
var config = default(IConfiguration);

var sp = Substitute.For<IServiceProvider>();
ServiceProviderServiceExtensions.GetService<IConfiguration>(sp).Returns(config);

var builder = new OpenAISettingsBuilder();

// Act
Action action = () => builder.WithAppSettings(sp)
.Build();

// Assert
action.Should().Throw<InvalidOperationException>();
}

[Fact]
public void Given_Empty_AzureSettings_When_Invoked_WithAppSettings_Then_It_Should_Throw_Exception()
{
// Arrange
var dict = new Dictionary<string, string>()
{
{ "Azure", string.Empty }
};
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.

var sp = Substitute.For<IServiceProvider>();
ServiceProviderServiceExtensions.GetService<IConfiguration>(sp).Returns(config);

var builder = new OpenAISettingsBuilder();

// Act
Action action = () => builder.WithAppSettings(sp)
.Build();

// Assert
action.Should().Throw<InvalidOperationException>();
}

[Fact]
public void Given_Empty_OpenAISettings_When_Invoked_WithAppSettings_Then_It_Should_Throw_Exception()
{
// Arrange
var dict = new Dictionary<string, string>()
{
{ "Azure:OpenAI", string.Empty }
};
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.

var sp = Substitute.For<IServiceProvider>();
ServiceProviderServiceExtensions.GetService<IConfiguration>(sp).Returns(config);

var builder = new OpenAISettingsBuilder();

// Act
Action action = () => builder.WithAppSettings(sp)
.Build();

// Assert
action.Should().Throw<InvalidOperationException>();
}

[Fact]
public void Given_Null_OpenAIInstances_When_Invoked_WithAppSettings_Then_It_Should_Return_Empty_Instance()
{
// Arrange
var dict = new Dictionary<string, string>()
{
{ "Azure:OpenAI:Instances", string.Empty }
};
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.

var sp = Substitute.For<IServiceProvider>();
ServiceProviderServiceExtensions.GetService<IConfiguration>(sp).Returns(config);

var builder = new OpenAISettingsBuilder();

// Act
var result = builder.WithAppSettings(sp)
.Build();

// Assert
result.Instances.Should().NotBeNull()
.And.BeEmpty();
}

[Fact]
public void Given_AppSettings_When_Invoked_WithAppSettings_Then_It_Should_Return_Instance()
{
// Arrange
var dict = new Dictionary<string, string>()
{
{ "Azure:OpenAI:Instances:0", string.Empty },
};
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.

var sp = Substitute.For<IServiceProvider>();
ServiceProviderServiceExtensions.GetService<IConfiguration>(sp).Returns(config);

var builder = new OpenAISettingsBuilder();

// Act
var result = builder.WithAppSettings(sp)
.Build();

// Assert
result.Instances.Should().NotBeNull()
.And.HaveCount(1);
}

[Theory]
[InlineData("http://localhost")]
public void Given_AppSettings_When_Invoked_WithAppSettings_Then_It_Should_Return_Endpoint(string endpoint)
{
// Arrange
var dict = new Dictionary<string, string>()
{
{ "Azure:OpenAI:Instances:0:Endpoint", endpoint },
};
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.

var sp = Substitute.For<IServiceProvider>();
ServiceProviderServiceExtensions.GetService<IConfiguration>(sp).Returns(config);

var builder = new OpenAISettingsBuilder();

// Act
var result = builder.WithAppSettings(sp)
.Build();

// Assert
result.Instances.First().Endpoint.Should().Be(endpoint);
}

[Theory]
[InlineData("api-key")]
public void Given_AppSettings_When_Invoked_WithAppSettings_Then_It_Should_Return_ApiKey(string apiKey)
{
// Arrange
var dict = new Dictionary<string, string>()
{
{ "Azure:OpenAI:Instances:0:ApiKey", apiKey },
};
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.

var sp = Substitute.For<IServiceProvider>();
ServiceProviderServiceExtensions.GetService<IConfiguration>(sp).Returns(config);

var builder = new OpenAISettingsBuilder();

// Act
var result = builder.WithAppSettings(sp)
.Build();

// Assert
result.Instances.First().ApiKey.Should().Be(apiKey);
}

[Theory]
[InlineData("deployment-name-1")]
[InlineData("deployment-name-1", "deployment-name-2")]
[InlineData("deployment-name-1", "deployment-name-2", "deployment-name-3")]
public void Given_AppSettings_When_Invoked_WithAppSettings_Then_It_Should_Return_Instances(params string[] deploymentNames)
{
// Arrange
var dict = new Dictionary<string, string>();
for (var i = 0; i < deploymentNames.Length; i++)
{
dict.Add($"Azure:OpenAI:Instances:0:DeploymentNames:{i}", deploymentNames[i]);
}
#pragma warning disable CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.
var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build();
#pragma warning restore CS8620 // Argument cannot be used for parameter due to differences in the nullability of reference types.

var sp = Substitute.For<IServiceProvider>();
ServiceProviderServiceExtensions.GetService<IConfiguration>(sp).Returns(config);

var builder = new OpenAISettingsBuilder();

// Act
var result = builder.WithAppSettings(sp)
.Build();

// Assert
result.Instances.First().DeploymentNames.Should().HaveCount(deploymentNames.Length);
foreach (var dn in deploymentNames)
{
result.Instances.First().DeploymentNames.Should().Contain(dn);
}
}
}