From 7ad41be2d071229522a986a39c08f1b091b69bf8 Mon Sep 17 00:00:00 2001 From: Rafsanul Hasan Date: Sun, 19 Mar 2023 22:00:30 +0600 Subject: [PATCH] DI Issue: registered httpclient and RESFulSense in .NET fashion. Used constant strings instead of magic strings for better readability. Removed some warnings --- .../CompletionClientTests.Prompts.cs | 6 ++- .../Completions/CompletionClientTests.cs | 5 +- .../Completions/CompletionServiceTests.cs | 2 +- OpenAI.NET/Brokers/DependencyInjection.cs | 54 +++++++++++++++++++ .../AuthorizationMessageHandler.cs | 48 +++++++++++++++++ OpenAI.NET/Brokers/OpenAIs/OpenAIBroker.cs | 45 +++++----------- OpenAI.NET/Clients/OpenAIs/OpenAIClient.cs | 11 ++-- OpenAI.NET/OpenAI.NET.csproj | 2 + 8 files changed, 133 insertions(+), 40 deletions(-) create mode 100644 OpenAI.NET/Brokers/DependencyInjection.cs create mode 100644 OpenAI.NET/Brokers/HttpMessageHandlers/AuthorizationMessageHandler.cs diff --git a/OpenAI.NET.Tests.Acceptance/Clients/Completions/CompletionClientTests.Prompts.cs b/OpenAI.NET.Tests.Acceptance/Clients/Completions/CompletionClientTests.Prompts.cs index 98c9c069..68ca104a 100644 --- a/OpenAI.NET.Tests.Acceptance/Clients/Completions/CompletionClientTests.Prompts.cs +++ b/OpenAI.NET.Tests.Acceptance/Clients/Completions/CompletionClientTests.Prompts.cs @@ -33,8 +33,10 @@ public async Task ShouldPromptCompletionAsync() Completion expectedCompletion = inputCompletion.DeepClone(); expectedCompletion = ConvertToCompletion(inputCompletion, completionResponse); - var jsonSerializationSettings = new JsonSerializerSettings(); - jsonSerializationSettings.DefaultValueHandling = DefaultValueHandling.Ignore; + JsonSerializerSettings jsonSerializationSettings = new() + { + DefaultValueHandling = DefaultValueHandling.Ignore, + }; this.wireMockServer.Given( Request.Create() diff --git a/OpenAI.NET.Tests.Acceptance/Clients/Completions/CompletionClientTests.cs b/OpenAI.NET.Tests.Acceptance/Clients/Completions/CompletionClientTests.cs index 8e7f2085..60ebefae 100644 --- a/OpenAI.NET.Tests.Acceptance/Clients/Completions/CompletionClientTests.cs +++ b/OpenAI.NET.Tests.Acceptance/Clients/Completions/CompletionClientTests.cs @@ -20,6 +20,9 @@ public partial class CompletionClientTests : IDisposable private readonly string apiKey; private readonly string organizationId; + /// + /// Initializes a new instance of the class. + /// public CompletionClientTests() { this.wireMockServer = WireMockServer.Start(1989); @@ -30,7 +33,7 @@ public CompletionClientTests() { ApiUrl = "http://localhost:1989", ApiKey = this.apiKey, - OrganizationId = this.organizationId + OrganizationId = this.organizationId, }; this.openAIClient = new OpenAIClient(openAiConfiguration); diff --git a/OpenAI.NET.Tests.Unit/Services/Foundations/Completions/CompletionServiceTests.cs b/OpenAI.NET.Tests.Unit/Services/Foundations/Completions/CompletionServiceTests.cs index b8def782..b6ba5975 100644 --- a/OpenAI.NET.Tests.Unit/Services/Foundations/Completions/CompletionServiceTests.cs +++ b/OpenAI.NET.Tests.Unit/Services/Foundations/Completions/CompletionServiceTests.cs @@ -75,7 +75,7 @@ private static dynamic CreateRandomCompletionProperties() Created = GetRandomNumber(), ResponseModel = GetRandomString(), Choices = CreateRandomChoicesList(), - Usage = CreateRandomUsage() + Usage = CreateRandomUsage(), }; } diff --git a/OpenAI.NET/Brokers/DependencyInjection.cs b/OpenAI.NET/Brokers/DependencyInjection.cs new file mode 100644 index 00000000..2480f24e --- /dev/null +++ b/OpenAI.NET/Brokers/DependencyInjection.cs @@ -0,0 +1,54 @@ +using Microsoft.Extensions.DependencyInjection; + +using OpenAI.NET.Brokers.HttpMessageHandlers; +using OpenAI.NET.Brokers.OpenAIs; +using OpenAI.NET.Models.Configurations; + +using RESTFulSense.Clients; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace OpenAI.NET.Brokers +{ + internal static class DependencyInjection + { + public static IServiceCollection AddBrokers( + this IServiceCollection services) + { + services + .AddRestfulHttpClient() + .AddScoped(); + + return services; + } + + private static IServiceCollection AddRestfulHttpClient( + this IServiceCollection services) + { + services + .AddHttpClient((configuration, httpClient) => + { + ApiConfigurations apiConfigurations = configuration.GetRequiredService(); + httpClient.BaseAddress = new Uri(uriString: apiConfigurations.ApiUrl); + }) + .AddHttpMessageHandler() + .Services + .AddScoped(); + + services + .AddScoped(configuration => + { + IHttpClientFactory clientFactory = configuration.GetRequiredService(); + HttpClient httpClient = clientFactory.CreateClient(nameof(RESTFulApiFactoryClient)); + return new RESTFulApiFactoryClient(httpClient); + }); + + return services; + } + } +} diff --git a/OpenAI.NET/Brokers/HttpMessageHandlers/AuthorizationMessageHandler.cs b/OpenAI.NET/Brokers/HttpMessageHandlers/AuthorizationMessageHandler.cs new file mode 100644 index 00000000..2e6aae96 --- /dev/null +++ b/OpenAI.NET/Brokers/HttpMessageHandlers/AuthorizationMessageHandler.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; + +using OpenAI.NET.Models.Configurations; + +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; + +namespace OpenAI.NET.Brokers.HttpMessageHandlers +{ + internal class AuthorizationMessageHandler : DelegatingHandler + { + private const string OpenAIOrganizationIdHeaderKey = "OpenAI-Organization"; + private readonly ApiConfigurations apiConfigurations; + + public AuthorizationMessageHandler(ApiConfigurations apiConfigurations) + { + this.apiConfigurations = apiConfigurations; + } + + protected override HttpResponseMessage Send(HttpRequestMessage request, CancellationToken cancellationToken) + { + this.AddRequestHeaders(request); + + return base.Send(request, cancellationToken); + } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + this.AddRequestHeaders(request); + + return base.SendAsync(request, cancellationToken); + } + + private void AddRequestHeaders(HttpRequestMessage request) + { + request.Headers.Add( + name: OpenAIOrganizationIdHeaderKey, + value: this.apiConfigurations.OrganizationId); + + request.Headers.Authorization ??= + new AuthenticationHeaderValue( + scheme: JwtBearerDefaults.AuthenticationScheme, + parameter: this.apiConfigurations.ApiKey); + } + } +} diff --git a/OpenAI.NET/Brokers/OpenAIs/OpenAIBroker.cs b/OpenAI.NET/Brokers/OpenAIs/OpenAIBroker.cs index fd2849e8..9c22ca02 100644 --- a/OpenAI.NET/Brokers/OpenAIs/OpenAIBroker.cs +++ b/OpenAI.NET/Brokers/OpenAIs/OpenAIBroker.cs @@ -2,26 +2,28 @@ // Copyright (c) Coalition of the Good-Hearted Engineers // --------------------------------------------------------------- +using Microsoft.Extensions.DependencyInjection; + +using RESTFulSense.Clients; + using System; using System.Net.Http; -using System.Net.Http.Headers; +using System.Net.Mime; using System.Threading.Tasks; -using OpenAI.NET.Models.Configurations; -using RESTFulSense.Clients; namespace OpenAI.NET.Brokers.OpenAIs { internal partial class OpenAIBroker : IOpenAIBroker { - private readonly ApiConfigurations apiConfigurations; private readonly IRESTFulApiFactoryClient apiClient; - private readonly HttpClient httpClient; - public OpenAIBroker(ApiConfigurations apiConfigurations) + /// + /// Initializes a new instance of the class. + /// + /// The RESTFulSenses Api Client. + public OpenAIBroker(IRESTFulApiFactoryClient apiClient) { - this.apiConfigurations = apiConfigurations; - this.httpClient = SetupHttpClient(); - this.apiClient = SetupApiClient(); + this.apiClient = apiClient; } private async ValueTask GetAsync(string relativeUrl) => @@ -35,7 +37,7 @@ private async ValueTask PostAsync(string relativeUrl return await this.apiClient.PostContentAsync( relativeUrl, content, - mediaType: "application/json", + mediaType: MediaTypeNames.Application.Json, ignoreNulls: true); } @@ -45,28 +47,5 @@ private async ValueTask PutAsync(string relativeUrl, T content) => private async ValueTask DeleteAsync(string relativeUrl) => await this.apiClient.DeleteContentAsync(relativeUrl); - - private HttpClient SetupHttpClient() - { - var httpClient = new HttpClient() - { - BaseAddress = - new Uri(uriString: this.apiConfigurations.ApiUrl), - }; - - httpClient.DefaultRequestHeaders.Authorization = - new AuthenticationHeaderValue( - scheme: "Bearer", - parameter: this.apiConfigurations.ApiKey); - - httpClient.DefaultRequestHeaders.Add( - name: "OpenAI-Organization", - value: this.apiConfigurations.OrganizationId); - - return httpClient; - } - - private IRESTFulApiFactoryClient SetupApiClient() => - new RESTFulApiFactoryClient(this.httpClient); } } diff --git a/OpenAI.NET/Clients/OpenAIs/OpenAIClient.cs b/OpenAI.NET/Clients/OpenAIs/OpenAIClient.cs index b4dc6aef..ea7216aa 100644 --- a/OpenAI.NET/Clients/OpenAIs/OpenAIClient.cs +++ b/OpenAI.NET/Clients/OpenAIs/OpenAIClient.cs @@ -4,7 +4,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using OpenAI.NET.Brokers.OpenAIs; + +using OpenAI.NET.Brokers; using OpenAI.NET.Clients.Completions; using OpenAI.NET.Models.Configurations; using OpenAI.NET.Services.Foundations.Completions; @@ -13,6 +14,10 @@ namespace OpenAI.NET.Clients.OpenAIs { public class OpenAIClient : IOpenAIClient { + /// + /// Initializes a new instance of the class. + /// + /// The api configurations. public OpenAIClient(ApiConfigurations apiConfigurations) { IHost host = RegisterServices(apiConfigurations); @@ -30,10 +35,10 @@ private static IHost RegisterServices(ApiConfigurations apiConfigurations) builder.ConfigureServices(configuration => { - configuration.AddTransient(); + configuration.AddSingleton(_ => apiConfigurations); + configuration.AddBrokers(); configuration.AddTransient(); configuration.AddTransient(); - configuration.AddSingleton(options => apiConfigurations); }); IHost host = builder.Build(); diff --git a/OpenAI.NET/OpenAI.NET.csproj b/OpenAI.NET/OpenAI.NET.csproj index 68a2a72f..1b2bf9c8 100644 --- a/OpenAI.NET/OpenAI.NET.csproj +++ b/OpenAI.NET/OpenAI.NET.csproj @@ -13,8 +13,10 @@ + +