diff --git a/OpenAI_API/Chat/ChatEndpoint.cs b/OpenAI_API/Chat/ChatEndpoint.cs index 1dee60b..e0e38a4 100644 --- a/OpenAI_API/Chat/ChatEndpoint.cs +++ b/OpenAI_API/Chat/ChatEndpoint.cs @@ -29,6 +29,13 @@ public class ChatEndpoint : EndpointBase, IChatEndpoint /// internal ChatEndpoint(OpenAIAPI api) : base(api) { } + /// + /// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of as . + /// + /// + /// + internal ChatEndpoint(HttpClient httpClient, OpenAIAPI api) : base(api) { } + /// /// Creates an ongoing chat which can easily encapsulate the conversation. This is the simplest way to use the Chat endpoint. /// diff --git a/OpenAI_API/Completions/CompletionEndpoint.cs b/OpenAI_API/Completions/CompletionEndpoint.cs index 519d70a..5f4af43 100644 --- a/OpenAI_API/Completions/CompletionEndpoint.cs +++ b/OpenAI_API/Completions/CompletionEndpoint.cs @@ -27,6 +27,13 @@ public class CompletionEndpoint : EndpointBase, ICompletionEndpoint /// internal CompletionEndpoint(OpenAIAPI api) : base(api) { } + /// + /// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of as . + /// + /// + /// + internal CompletionEndpoint(HttpClient httpClient, OpenAIAPI api) : base(httpClient, api) { } + #region Non-streaming /// diff --git a/OpenAI_API/Embedding/EmbeddingEndpoint.cs b/OpenAI_API/Embedding/EmbeddingEndpoint.cs index a324de9..ad9b743 100644 --- a/OpenAI_API/Embedding/EmbeddingEndpoint.cs +++ b/OpenAI_API/Embedding/EmbeddingEndpoint.cs @@ -1,4 +1,5 @@ -using OpenAI_API.Models; +using System.Net.Http; +using OpenAI_API.Models; using System.Threading.Tasks; namespace OpenAI_API.Embedding @@ -24,6 +25,13 @@ public class EmbeddingEndpoint : EndpointBase, IEmbeddingEndpoint /// internal EmbeddingEndpoint(OpenAIAPI api) : base(api) { } + /// + /// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of as . + /// + /// + /// + internal EmbeddingEndpoint(HttpClient httpClient, OpenAIAPI api) : base(httpClient, api) { } + /// /// Ask the API to embedd text using the default embedding model /// diff --git a/OpenAI_API/EndpointBase.cs b/OpenAI_API/EndpointBase.cs index 66a4ee0..155a4c3 100644 --- a/OpenAI_API/EndpointBase.cs +++ b/OpenAI_API/EndpointBase.cs @@ -18,6 +18,8 @@ public abstract class EndpointBase { private const string UserAgent = "okgodoit/dotnet_openai_api"; + private HttpClient _httpClient; + /// /// The internal reference to the API, mostly used for authentication /// @@ -32,6 +34,18 @@ internal EndpointBase(OpenAIAPI api) this._Api = api; } + /// + /// Constructor of the api endpoint base, to be called from the contructor of any devived classes. + /// Rather than instantiating any endpoint yourself, access it through an instance of . + /// + /// + /// + internal EndpointBase(HttpClient httpClient, OpenAIAPI api) + { + this._Api = api; + this._httpClient = httpClient; + } + /// /// The name of the endpoint, which is the final path segment in the API URL. Must be overriden in a derived class. /// @@ -67,15 +81,17 @@ protected HttpClient GetClient() _Api.SharedHttpClient. } */ - - HttpClient client = new HttpClient(); - client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _Api.Auth.ApiKey); + + _httpClient ??= new HttpClient(); + + _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _Api.Auth.ApiKey); // Further authentication-header used for Azure openAI service - client.DefaultRequestHeaders.Add("api-key", _Api.Auth.ApiKey); - client.DefaultRequestHeaders.Add("User-Agent", UserAgent); - if (!string.IsNullOrEmpty(_Api.Auth.OpenAIOrganization)) client.DefaultRequestHeaders.Add("OpenAI-Organization", _Api.Auth.OpenAIOrganization); + _httpClient.DefaultRequestHeaders.Add("api-key", _Api.Auth.ApiKey); + _httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent); + if (!string.IsNullOrEmpty(_Api.Auth.OpenAIOrganization)) + _httpClient.DefaultRequestHeaders.Add("OpenAI-Organization", _Api.Auth.OpenAIOrganization); - return client; + return _httpClient; } /// diff --git a/OpenAI_API/Files/FilesEndpoint.cs b/OpenAI_API/Files/FilesEndpoint.cs index 0435a9c..b6cbb14 100644 --- a/OpenAI_API/Files/FilesEndpoint.cs +++ b/OpenAI_API/Files/FilesEndpoint.cs @@ -17,6 +17,13 @@ public class FilesEndpoint : EndpointBase, IFilesEndpoint /// internal FilesEndpoint(OpenAIAPI api) : base(api) { } + /// + /// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of as . + /// + /// + /// + internal FilesEndpoint(HttpClient httpClient, OpenAIAPI api) : base(httpClient, api) { } + /// /// The name of the endpoint, which is the final path segment in the API URL. For example, "files". /// diff --git a/OpenAI_API/Images/ImageGenerationEndpoint.cs b/OpenAI_API/Images/ImageGenerationEndpoint.cs index 06d55a2..4f924d0 100644 --- a/OpenAI_API/Images/ImageGenerationEndpoint.cs +++ b/OpenAI_API/Images/ImageGenerationEndpoint.cs @@ -1,6 +1,7 @@ using OpenAI_API.Models; using System; using System.Collections.Generic; +using System.Net.Http; using System.Text; using System.Threading.Tasks; @@ -22,6 +23,13 @@ public class ImageGenerationEndpoint : EndpointBase, IImageGenerationEndpoint /// internal ImageGenerationEndpoint(OpenAIAPI api) : base(api) { } + /// + /// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of as . + /// + /// + /// + internal ImageGenerationEndpoint(HttpClient httpClient, OpenAIAPI api) : base(httpClient, api) { } + /// /// Ask the API to Creates an image given a prompt. /// diff --git a/OpenAI_API/Model/ModelsEndpoint.cs b/OpenAI_API/Model/ModelsEndpoint.cs index da13e78..6a544ad 100644 --- a/OpenAI_API/Model/ModelsEndpoint.cs +++ b/OpenAI_API/Model/ModelsEndpoint.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.Net.Http; using System.Threading.Tasks; namespace OpenAI_API.Models @@ -21,6 +22,13 @@ public class ModelsEndpoint : EndpointBase, IModelsEndpoint /// internal ModelsEndpoint(OpenAIAPI api) : base(api) { } + /// + /// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of as . + /// + /// + /// + internal ModelsEndpoint(HttpClient httpClient, OpenAIAPI api) : base(httpClient, api) { } + /// /// Get details about a particular Model from the API, specifically properties such as and permissions. /// diff --git a/OpenAI_API/Moderation/ModerationEndpoint.cs b/OpenAI_API/Moderation/ModerationEndpoint.cs index 1fe6069..af81945 100644 --- a/OpenAI_API/Moderation/ModerationEndpoint.cs +++ b/OpenAI_API/Moderation/ModerationEndpoint.cs @@ -1,6 +1,7 @@ using OpenAI_API.Models; using System; using System.Collections.Generic; +using System.Net.Http; using System.Text; using System.Threading.Tasks; @@ -27,6 +28,13 @@ public class ModerationEndpoint : EndpointBase, IModerationEndpoint /// internal ModerationEndpoint(OpenAIAPI api) : base(api) { } + /// + /// Constructor of the api endpoint. Rather than instantiating this yourself, access it through an instance of as . + /// + /// + /// + internal ModerationEndpoint(HttpClient httpClient, OpenAIAPI api) : base(httpClient, api) { } + /// /// Ask the API to classify the text using the default model. /// diff --git a/OpenAI_API/OpenAIAPI.cs b/OpenAI_API/OpenAIAPI.cs index f415410..abaa46b 100644 --- a/OpenAI_API/OpenAIAPI.cs +++ b/OpenAI_API/OpenAIAPI.cs @@ -1,4 +1,5 @@ -using OpenAI_API.Chat; +using System.Net.Http; +using OpenAI_API.Chat; using OpenAI_API.Completions; using OpenAI_API.Embedding; using OpenAI_API.Files; @@ -47,6 +48,23 @@ public OpenAIAPI(APIAuthentication apiKeys = null) ImageGenerations = new ImageGenerationEndpoint(this); } + /// + /// Creates a new entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints + /// + /// The HttpClient to use while calling OpenAI's API. By injecting it, you can customize its behavior. + /// The API authentication information to use for API calls, or to attempt to use the , potentially loading from environment vars or from a config file. + public OpenAIAPI(HttpClient httpClient, APIAuthentication apiKeys = null) + { + this.Auth = apiKeys.ThisOrDefault(); + Completions = new CompletionEndpoint(httpClient, this); + Models = new ModelsEndpoint(httpClient, this); + Files = new FilesEndpoint(httpClient, this); + Embeddings = new EmbeddingEndpoint(httpClient,this); + Chat = new ChatEndpoint(httpClient, this); + Moderation = new ModerationEndpoint(httpClient, this); + ImageGenerations = new ImageGenerationEndpoint(httpClient, this); + } + /// /// Instantiates a version of the API for connecting to the Azure OpenAI endpoint instead of the main OpenAI endpoint. /// @@ -62,6 +80,22 @@ public static OpenAIAPI ForAzure(string YourResourceName, string deploymentId, A return api; } + /// + /// Instantiates a version of the API for connecting to the Azure OpenAI endpoint instead of the main OpenAI endpoint. + /// + /// The HttpClient to use while calling OpenAI's API. By injecting it, you can customize its behavior. + /// The name of your Azure OpenAI Resource + /// The name of your model deployment. You're required to first deploy a model before you can make calls. + /// The API authentication information to use for API calls, or to attempt to use the , potentially loading from environment vars or from a config file. Currently this library only supports the api-key flow, not the AD-Flow. + /// + public static OpenAIAPI ForAzure(HttpClient httpClient, string YourResourceName, string deploymentId, APIAuthentication apiKey = null) + { + OpenAIAPI api = new OpenAIAPI(httpClient, apiKey); + api.ApiVersion = "2022-12-01"; + api.ApiUrlFormat = $"https://{YourResourceName}.openai.azure.com/openai/deployments/{deploymentId}/" + "{1}?api-version={0}"; + return api; + } + /// /// Text generation is the core function of the API. You give the API a prompt, and it generates a completion. The way you “program” the API to do a task is by simply describing the task in plain english or providing a few written examples. This simple approach works for a wide range of use cases, including summarization, translation, grammar correction, question answering, chatbots, composing emails, and much more (see the prompt library for inspiration). /// diff --git a/README.md b/README.md index c137a8f..04cfd04 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Console.WriteLine(result); * [Files API](#files-for-fine-tuning) * [Image APIs (DALL-E)](#images) * [Azure](#azure) + * [Customize the HTTP Client](#customize-the-http-client) * [Additonal Documentation](#documentation) * [License](#license) @@ -276,6 +277,29 @@ OpenAIAPI api = OpenAIAPI.ForAzure("YourResourceName", "deploymentId", "api-key" You may then use the `api` object like normal. You may also specify the `APIAuthentication` is any of the other ways listed in the [Authentication](#authentication) section above. Currently this library only supports the api-key flow, not the AD-Flow. +## Customize the HTTP Client + +You can customize the HttpClient used in the wrapper by specifing it in the constructor or in the `ForAzure` static method. +```csharp +builder.Services.AddHttpClient() + .SetHandlerLifetime(TimeSpan.FromMinutes(30)) + .AddPolicyHandler(RetryPolicy.GetTooManyRequestRetryPolicy()) +``` +And then in your class +```csharp +public class OpenAiBusinessService +{ + private readonly OpenAIAPI _azureOpenAiApiWrapper; + private readonly OpenAIAPI _openAiWrapper; + + public OpenAiBusinessService(HttpClient httpClient) + { + _azureOpenAiApiWrapper = OpenAIAPI.ForAzure("resource", "deploymentId", "apiKey"); + _openAiWrapper = new OpenAIAPI(httpClient, new APIAuthentication("apiKey")); + + } +} +``` ## Documentation Every single class, method, and property has extensive XML documentation, so it should show up automatically in IntelliSense. That combined with the official OpenAI documentation should be enough to get started. Feel free to open an issue here if you have any questions. Better documentation may come later.