diff --git a/OpenAI_API/APIAuthentication.cs b/OpenAI_API/APIAuthentication.cs
index 4346281..7a739c1 100644
--- a/OpenAI_API/APIAuthentication.cs
+++ b/OpenAI_API/APIAuthentication.cs
@@ -157,34 +157,6 @@ public static APIAuthentication LoadFromPath(string directory = null, string fil
return new APIAuthentication(key, org);
}
-
-
- ///
- /// Tests the api key against the OpenAI API, to ensure it is valid. This hits the models endpoint so should not be charged for usage.
- ///
- /// if the api key is valid, or if empty or not accepted by the OpenAI API.
- public async Task ValidateAPIKey()
- {
- if (string.IsNullOrEmpty(ApiKey))
- return false;
-
- var api = new OpenAIAPI(this);
-
- List results;
-
- try
- {
- results = await api.Models.GetModelsAsync();
- }
- catch (Exception ex)
- {
- Debug.WriteLine(ex.ToString());
- return false;
- }
-
- return (results.Count > 0);
- }
-
}
internal static class AuthHelpers
diff --git a/OpenAI_API/EndpointBase.cs b/OpenAI_API/EndpointBase.cs
index 5d74cfa..e096223 100644
--- a/OpenAI_API/EndpointBase.cs
+++ b/OpenAI_API/EndpointBase.cs
@@ -48,22 +48,19 @@ protected string Url
}
}
- ///
- /// Gets an HTTPClient with the appropriate authorization and other headers set
- ///
+ /// Configures an HttpClient with the appropriate authorization and other headers set
/// The fully initialized HttpClient
/// Thrown if there is no valid authentication. Please refer to for details.
- protected HttpClient GetClient()
+ internal static HttpClient ConfigureClient(HttpClient client, APIAuthentication auth)
{
- if (_Api.Auth?.ApiKey is null)
+ if (auth?.ApiKey is null)
{
throw new AuthenticationException("You must provide API authentication. Please refer to https://github.com/OkGoDoIt/OpenAI-API-dotnet#authentication for details.");
}
- HttpClient client = new HttpClient();
- client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _Api.Auth.ApiKey);
+ client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", auth.ApiKey);
client.DefaultRequestHeaders.Add("User-Agent", Value);
- if (!string.IsNullOrEmpty(_Api.Auth.OpenAIOrganization)) client.DefaultRequestHeaders.Add("OpenAI-Organization", _Api.Auth.OpenAIOrganization);
+ if (!string.IsNullOrEmpty(auth.OpenAIOrganization)) client.DefaultRequestHeaders.Add("OpenAI-Organization", auth.OpenAIOrganization);
return client;
}
@@ -99,11 +96,8 @@ private async Task HttpRequestRaw(string url = null, HttpMe
if (verb == null)
verb = HttpMethod.Get;
- var client = GetClient();
-
- HttpResponseMessage response = null;
- string resultAsString = null;
- HttpRequestMessage req = new HttpRequestMessage(verb, url);
+
+ using var req = new HttpRequestMessage(verb, url);
if (postData != null)
{
@@ -118,15 +112,16 @@ private async Task HttpRequestRaw(string url = null, HttpMe
req.Content = stringContent;
}
}
- response = await client.SendAsync(req, streaming ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead);
+ var response = await this._Api.Client.SendAsync(req, streaming ? HttpCompletionOption.ResponseHeadersRead : HttpCompletionOption.ResponseContentRead);
if (response.IsSuccessStatusCode)
{
return response;
}
else
{
- try
+ string resultAsString = null;
+ try
{
resultAsString = await response.Content.ReadAsStringAsync();
}
diff --git a/OpenAI_API/Files/FilesEndpoint.cs b/OpenAI_API/Files/FilesEndpoint.cs
index 6721a2e..b58eb50 100644
--- a/OpenAI_API/Files/FilesEndpoint.cs
+++ b/OpenAI_API/Files/FilesEndpoint.cs
@@ -71,7 +71,6 @@ public async Task DeleteFileAsync(string fileId)
/// The intendend purpose of the uploaded documents. Use "fine-tune" for Fine-tuning. This allows us to validate the format of the uploaded file.
public async Task UploadFileAsync(string filePath, string purpose = "fine-tune")
{
- HttpClient client = GetClient();
var content = new MultipartFormDataContent
{
{ new StringContent(purpose), "purpose" },
diff --git a/OpenAI_API/IOpenAI.cs b/OpenAI_API/IOpenAI.cs
new file mode 100644
index 0000000..37d027f
--- /dev/null
+++ b/OpenAI_API/IOpenAI.cs
@@ -0,0 +1,23 @@
+#nullable enable
+namespace OpenAI_API;
+using OpenAI_API.Completions;
+using OpenAI_API.Embedding;
+using OpenAI_API.Files;
+using OpenAI_API.Models;
+
+/// Entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints
+public interface IOpenAI
+{
+ /// 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).
+ public CompletionEndpoint Completions { get; }
+
+ /// The API lets you transform text into a vector (list) of floating point numbers. The distance between two vectors measures their relatedness. Small distances suggest high relatedness and large distances suggest low relatedness.
+ public EmbeddingEndpoint Embeddings { get; }
+
+ /// The API endpoint for querying available Engines/models
+ public ModelsEndpoint Models { get; }
+
+ /// The API lets you do operations with files. You can upload, delete or retrieve files. Files can be used for fine-tuning, search, etc.
+ public FilesEndpoint Files { get; }
+}
+
diff --git a/OpenAI_API/Model/ModelsEndpoint.cs b/OpenAI_API/Model/ModelsEndpoint.cs
index a197e68..16cc147 100644
--- a/OpenAI_API/Model/ModelsEndpoint.cs
+++ b/OpenAI_API/Model/ModelsEndpoint.cs
@@ -25,10 +25,12 @@ internal ModelsEndpoint(OpenAIAPI api) : base(api) { }
///
/// The id/name of the model to get more details about
/// Asynchronously returns the with all available properties
- public Task RetrieveModelDetailsAsync(string id)
+ public async Task RetrieveModelDetailsAsync(string id)
{
- return RetrieveModelDetailsAsync(id, _Api?.Auth);
- }
+ string resultAsString = await HttpGetContent($"{Url}/{id}");
+ var model = JsonConvert.DeserializeObject(resultAsString);
+ return model;
+ }
///
/// List all models via the API
@@ -43,14 +45,11 @@ public async Task> GetModelsAsync()
/// Get details about a particular Model from the API, specifically properties such as and permissions.
///
/// The id/name of the model to get more details about
- /// API authentication in order to call the API endpoint. If not specified, attempts to use a default.
+ /// Obsolete: IGNORED
/// Asynchronously returns the with all available properties
- public async Task RetrieveModelDetailsAsync(string id, APIAuthentication auth = null)
- {
- string resultAsString = await HttpGetContent($"{Url}/{id}");
- var model = JsonConvert.DeserializeObject(resultAsString);
- return model;
- }
+ [Obsolete("Use the overload without the APIAuthentication parameter instead, as it is ignored.", false)] // See #42
+ public Task RetrieveModelDetailsAsync(string id, APIAuthentication auth) =>
+ this.RetrieveModelDetailsAsync(id);
///
/// A helper class to deserialize the JSON API responses. This should not be used directly.
diff --git a/OpenAI_API/OpenAIAPI.cs b/OpenAI_API/OpenAIAPI.cs
index 727b261..6bf9555 100644
--- a/OpenAI_API/OpenAIAPI.cs
+++ b/OpenAI_API/OpenAIAPI.cs
@@ -1,42 +1,68 @@
-using OpenAI_API.Completions;
+using Microsoft.Extensions.Logging;
+using OpenAI_API.Completions;
using OpenAI_API.Embedding;
using OpenAI_API.Files;
using OpenAI_API.Models;
+using System.Net.Http;
namespace OpenAI_API
{
///
/// Entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints
///
- public class OpenAIAPI
- {
+ public class OpenAIAPI: IOpenAI
+ {
///
/// Base url for OpenAI
///
public string ApiUrlBase = "https://api.openai.com/v1/";
-
- ///
- /// The API authentication information to use for API calls
- ///
- public APIAuthentication Auth { get; set; }
- ///
- /// Creates a new entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints
- ///
- /// 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(APIAuthentication apiKeys = null)
- {
- this.Auth = apiKeys.ThisOrDefault();
- Completions = new CompletionEndpoint(this);
- Models = new ModelsEndpoint(this);
- Files = new FilesEndpoint(this);
- Embeddings = new EmbeddingEndpoint(this);
- }
+ /// The HTTP client configured with the authentication headers.
+ internal HttpClient Client { get; }
- ///
- /// 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).
- ///
- public CompletionEndpoint Completions { get; }
+ private OpenAIAPI()
+ {
+ this.Completions = new CompletionEndpoint(this);
+ this.Models = new ModelsEndpoint(this);
+ this.Files = new FilesEndpoint(this);
+ this.Embeddings = new EmbeddingEndpoint(this);
+ }
+
+ ///
+ /// Creates a new entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints
+ ///
+ /// The HTTP client configured with the authentication headers.
+ public OpenAIAPI(HttpClient httpClient) : this() =>
+ this.Client = httpClient;
+
+ ///
+ /// Creates a new entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints
+ ///
+ /// The authentication details for the API
+ [Obsolete("""
+ This constructor will generate a new HTTP client every time it is called.
+ Do not use this in scenarios where multiple instances of the API are required.
+ This is provided for backwards compatibility, use .NET Dependency Injection instead.
+ """, false)]
+ public OpenAIAPI(APIAuthentication auth) :
+ this(EndpointBase.ConfigureClient(new HttpClient(), auth)) { }
+
+ ///
+ /// Creates a new entry point to the OpenAPI API, handling auth and allowing access to the various API endpoints
+ ///
+ /// The authentication key for the API
+ [Obsolete("""
+ This constructor will generate a new HTTP client every time it is called.
+ Do not use this in scenarios where multiple instances of the API are required.
+ This is provided for backwards compatibility, use .NET Dependency Injection instead.
+ """, false)]
+ public OpenAIAPI(string key) :
+ this(EndpointBase.ConfigureClient(new HttpClient(), new APIAuthentication(key))) { }
+
+ ///
+ /// 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).
+ ///
+ public CompletionEndpoint Completions { get; }
///
/// The API lets you transform text into a vector (list) of floating point numbers. The distance between two vectors measures their relatedness. Small distances suggest high relatedness and large distances suggest low relatedness.
@@ -52,8 +78,5 @@ public OpenAIAPI(APIAuthentication apiKeys = null)
/// The API lets you do operations with files. You can upload, delete or retrieve files. Files can be used for fine-tuning, search, etc.
///
public FilesEndpoint Files { get; }
-
-
-
}
}
diff --git a/OpenAI_API/OpenAIApiExtensions.cs b/OpenAI_API/OpenAIApiExtensions.cs
new file mode 100644
index 0000000..4374345
--- /dev/null
+++ b/OpenAI_API/OpenAIApiExtensions.cs
@@ -0,0 +1,31 @@
+#nullable enable
+namespace Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Configuration;
+using OpenAI_API;
+
+public static class OpenAIApiExtensions
+{
+ /// Register for DI services. Read configuration from appsettings "openAI": { "key": "", "org": "" }
+ ///
+ ///
+ ///
+ public static IServiceCollection AddOpenAIService(this IServiceCollection services, IConfiguration configuration)
+ {
+ var section = configuration.GetSection("openAI");
+ if (!section.Exists()) return services;
+
+ string? key = section["key"];
+ if (key is null) return services;
+
+ string? organisation = section["org"];
+ return services.AddOpenAIService(new APIAuthentication(key, organisation));
+ }
+
+ public static IServiceCollection AddOpenAIService(this IServiceCollection services, APIAuthentication auth)
+ {
+ services.AddHttpClient(client =>
+ EndpointBase.ConfigureClient(client, auth));
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/OpenAI_API/OpenAI_API.csproj b/OpenAI_API/OpenAI_API.csproj
index b1f8fec..c9c18da 100644
--- a/OpenAI_API/OpenAI_API.csproj
+++ b/OpenAI_API/OpenAI_API.csproj
@@ -1,8 +1,7 @@
- netstandard2.0
- 8.0
+ net7.0
true
OkGoDoIt (Roger Pincombe)
OpenAI API
@@ -30,7 +29,8 @@
snupkg
true
-
+ enable
+ latest
@@ -41,7 +41,10 @@
-
+
+
+
+
diff --git a/OpenAI_Tests/AuthTests.cs b/OpenAI_Tests/AuthTests.cs
index e791c81..293c33b 100644
--- a/OpenAI_Tests/AuthTests.cs
+++ b/OpenAI_Tests/AuthTests.cs
@@ -1,4 +1,7 @@
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
+using OpenAI_API;
using System;
using System.IO;
using System.Threading.Tasks;
@@ -59,26 +62,24 @@ public void GetDefault()
}
-
-
- [Test]
- public void testHelper()
- {
- OpenAI_API.APIAuthentication defaultAuth = OpenAI_API.APIAuthentication.Default;
- OpenAI_API.APIAuthentication manualAuth = new OpenAI_API.APIAuthentication("pk-testAA");
- OpenAI_API.OpenAIAPI api = new OpenAI_API.OpenAIAPI();
- OpenAI_API.APIAuthentication shouldBeDefaultAuth = api.Auth;
- Assert.IsNotNull(shouldBeDefaultAuth);
- Assert.IsNotNull(shouldBeDefaultAuth.ApiKey);
- Assert.AreEqual(defaultAuth.ApiKey, shouldBeDefaultAuth.ApiKey);
-
- OpenAI_API.APIAuthentication.Default = new OpenAI_API.APIAuthentication("pk-testAA");
- api = new OpenAI_API.OpenAIAPI();
- OpenAI_API.APIAuthentication shouldBeManualAuth = api.Auth;
- Assert.IsNotNull(shouldBeManualAuth);
- Assert.IsNotNull(shouldBeManualAuth.ApiKey);
- Assert.AreEqual(manualAuth.ApiKey, shouldBeManualAuth.ApiKey);
- }
+ // [Test]
+ // public void testHelper()
+ // {
+ // OpenAI_API.APIAuthentication defaultAuth = OpenAI_API.APIAuthentication.Default;
+ // OpenAI_API.APIAuthentication manualAuth = new OpenAI_API.APIAuthentication("pk-testAA");
+ // OpenAI_API.OpenAIAPI api = AuthTests.InitService();
+ // OpenAI_API.APIAuthentication shouldBeDefaultAuth = api.Auth;
+ // Assert.IsNotNull(shouldBeDefaultAuth);
+ // Assert.IsNotNull(shouldBeDefaultAuth.ApiKey);
+ // Assert.AreEqual(defaultAuth.ApiKey, shouldBeDefaultAuth.ApiKey);
+ //
+ // OpenAI_API.APIAuthentication.Default = new OpenAI_API.APIAuthentication("pk-testAA");
+ // api = AuthTests.InitService();
+ // OpenAI_API.APIAuthentication shouldBeManualAuth = api.Auth;
+ // Assert.IsNotNull(shouldBeManualAuth);
+ // Assert.IsNotNull(shouldBeManualAuth.ApiKey);
+ // Assert.AreEqual(manualAuth.ApiKey, shouldBeManualAuth.ApiKey);
+ // }
[Test]
public void GetKey()
@@ -106,22 +107,35 @@ public void ParseKey()
Assert.AreEqual("orgTest", auth.OpenAIOrganization);
}
- [Test]
- public async Task TestBadKey()
- {
- var auth = new OpenAI_API.APIAuthentication("pk-testAA");
- Assert.IsFalse(await auth.ValidateAPIKey());
-
- auth = new OpenAI_API.APIAuthentication(null);
- Assert.IsFalse(await auth.ValidateAPIKey());
- }
-
- [Test]
- public async Task TestValidateGoodKey()
- {
- var auth = new OpenAI_API.APIAuthentication(Environment.GetEnvironmentVariable("TEST_OPENAI_SECRET_KEY"));
- Assert.IsTrue(await auth.ValidateAPIKey());
- }
+ // [Test]
+ // public async Task TestBadKey()
+ // {
+ // var auth = new OpenAI_API.APIAuthentication("pk-testAA");
+ // Assert.IsFalse(await auth.ValidateAPIKey());
+ //
+ // auth = new OpenAI_API.APIAuthentication(null);
+ // Assert.IsFalse(await auth.ValidateAPIKey());
+ // }
+ //
+ // [Test]
+ // public async Task TestValidateGoodKey()
+ // {
+ // var auth = new OpenAI_API.APIAuthentication(Environment.GetEnvironmentVariable("TEST_OPENAI_SECRET_KEY"));
+ // Assert.IsTrue(await auth.ValidateAPIKey());
+ // }
+
+ internal static IOpenAI InitService() {
+ IConfiguration config = new ConfigurationBuilder()
+ .AddJsonFile("appsettings.json", false, true)
+ .Build();
+
+ var services = new ServiceCollection();
+ services.AddOpenAIService(config);
+
+ var serviceProvider = services.BuildServiceProvider();
+
+ return serviceProvider.GetService();
+ }
}
}
\ No newline at end of file
diff --git a/OpenAI_Tests/CompletionEndpointTests.cs b/OpenAI_Tests/CompletionEndpointTests.cs
index 936cecb..40d4266 100644
--- a/OpenAI_Tests/CompletionEndpointTests.cs
+++ b/OpenAI_Tests/CompletionEndpointTests.cs
@@ -21,7 +21,7 @@ public void Setup()
[Test]
public void GetBasicCompletion()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
Assert.IsNotNull(api.Completions);
@@ -41,9 +41,9 @@ public void GetBasicCompletion()
[Test]
public void GetSimpleCompletion()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
- Assert.IsNotNull(api.Completions);
+ Assert.IsNotNull(api.Completions);
var results = api.Completions.CreateCompletionAsync("One Two Three Four Five Six Seven Eight Nine One Two Three Four Five Six Seven Eight", temperature: 0.1, max_tokens: 5).Result;
Assert.IsNotNull(results);
@@ -56,9 +56,9 @@ public void GetSimpleCompletion()
[Test]
public async Task CreateCompletionAsync_MultiplePrompts_ShouldReturnResult()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
- var completionReq = new CompletionRequest
+ var completionReq = new CompletionRequest
{
MultiplePrompts = new[]
{
@@ -80,9 +80,9 @@ public async Task CreateCompletionAsync_MultiplePrompts_ShouldReturnResult()
[TestCase(3)]
public void CreateCompletionAsync_ShouldNotAllowTemperatureOutside01(double temperature)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
- var completionReq = new CompletionRequest
+ var completionReq = new CompletionRequest
{
Prompt = "three four five",
Temperature = temperature,
@@ -100,9 +100,9 @@ public void CreateCompletionAsync_ShouldNotAllowTemperatureOutside01(double temp
[TestCase(2.0)]
public async Task ShouldBeMoreCreativeWithHighTemperature(double temperature)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
- var completionReq = new CompletionRequest
+ var completionReq = new CompletionRequest
{
Prompt = "three four five",
Temperature = temperature,
@@ -119,9 +119,9 @@ public async Task ShouldBeMoreCreativeWithHighTemperature(double temperature)
[TestCase(0.1)]
public async Task CreateCompletionAsync_ShouldGetSomeResultsWithVariousTopPValues(double topP)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
- var completionReq = new CompletionRequest
+ var completionReq = new CompletionRequest
{
Prompt = "three four five",
Temperature = 0,
@@ -140,7 +140,7 @@ public async Task CreateCompletionAsync_ShouldGetSomeResultsWithVariousTopPValue
[TestCase(1.0)]
public async Task CreateCompletionAsync_ShouldReturnSomeResultsForPresencePenalty(double presencePenalty)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api =AuthTests.InitService();
var completionReq = new CompletionRequest
{
@@ -161,7 +161,7 @@ public async Task CreateCompletionAsync_ShouldReturnSomeResultsForPresencePenalt
[TestCase(1.0)]
public async Task CreateCompletionAsync_ShouldReturnSomeResultsForFrequencyPenalty(double frequencyPenalty)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api =AuthTests.InitService();
var completionReq = new CompletionRequest
{
@@ -179,7 +179,7 @@ public async Task CreateCompletionAsync_ShouldReturnSomeResultsForFrequencyPenal
[Test]
public async Task CreateCompletionAsync_ShouldWorkForBiggerNumberOfCompletions()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api =AuthTests.InitService();
var completionReq = new CompletionRequest
{
@@ -199,7 +199,7 @@ public async Task CreateCompletionAsync_ShouldWorkForBiggerNumberOfCompletions()
[TestCase(5)]
public async Task CreateCompletionAsync_ShouldAlsoReturnLogProps(int logProps)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api =AuthTests.InitService();
var completionReq = new CompletionRequest
{
@@ -221,7 +221,7 @@ public async Task CreateCompletionAsync_ShouldAlsoReturnLogProps(int logProps)
[Test]
public async Task CreateCompletionAsync_Echo_ShouldReturnTheInput()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api =AuthTests.InitService();
var completionReq = new CompletionRequest
{
@@ -240,7 +240,7 @@ public async Task CreateCompletionAsync_Echo_ShouldReturnTheInput()
[TestCase("Friday")]
public async Task CreateCompletionAsync_ShouldStopOnStopSequence(string stopSeq)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var completionReq = new CompletionRequest
{
@@ -260,7 +260,7 @@ public async Task CreateCompletionAsync_ShouldStopOnStopSequence(string stopSeq)
[Test]
public async Task CreateCompletionAsync_MultipleParamShouldReturnTheSameDataAsSingleParamVersion()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var r = new CompletionRequest
{
@@ -296,7 +296,7 @@ public async Task CreateCompletionAsync_MultipleParamShouldReturnTheSameDataAsSi
[TestCase(7, 2)]
public async Task StreamCompletionAsync_ShouldStreamIndexAndData(int maxTokens, int numOutputs)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var completionRequest = new CompletionRequest
{
@@ -328,7 +328,7 @@ await api.Completions.StreamCompletionAsync(completionRequest, (index, result) =
[TestCase(7, 2)]
public async Task StreamCompletionAsync_ShouldStreamData(int maxTokens, int numOutputs)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var completionRequest = new CompletionRequest
{
@@ -357,7 +357,7 @@ await api.Completions.StreamCompletionAsync(completionRequest, result =>
[TestCase(7, 2)]
public async Task StreamCompletionEnumerableAsync_ShouldStreamData(int maxTokens, int numOutputs)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var completionRequest = new CompletionRequest
{
@@ -385,7 +385,7 @@ public async Task StreamCompletionEnumerableAsync_ShouldStreamData(int maxTokens
[Test]
public async Task StreamCompletionEnumerableAsync_MultipleParamShouldReturnTheSameDataAsSingleParamVersion()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var r = new CompletionRequest
{
diff --git a/OpenAI_Tests/EmbeddingEndpointTests.cs b/OpenAI_Tests/EmbeddingEndpointTests.cs
index ee58a57..cd221d6 100644
--- a/OpenAI_Tests/EmbeddingEndpointTests.cs
+++ b/OpenAI_Tests/EmbeddingEndpointTests.cs
@@ -17,7 +17,7 @@ public void Setup()
[Test]
public void GetBasicEmbedding()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
Assert.IsNotNull(api.Embeddings);
@@ -41,7 +41,7 @@ public void GetBasicEmbedding()
[Test]
public void GetSimpleEmbedding()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
Assert.IsNotNull(api.Embeddings);
diff --git a/OpenAI_Tests/FilesEndpointTests.cs b/OpenAI_Tests/FilesEndpointTests.cs
index 6cf3df8..00fd1b8 100644
--- a/OpenAI_Tests/FilesEndpointTests.cs
+++ b/OpenAI_Tests/FilesEndpointTests.cs
@@ -17,7 +17,7 @@ public void Setup()
[Order(1)]
public async Task UploadFile()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var response = await api.Files.UploadFileAsync("fine-tuning-data.jsonl");
Assert.IsNotNull(response);
Assert.IsTrue(response.Id.Length > 0);
@@ -33,7 +33,7 @@ public async Task UploadFile()
[Order(2)]
public async Task ListFiles()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var response = await api.Files.GetFilesAsync();
foreach (var file in response)
@@ -48,7 +48,7 @@ public async Task ListFiles()
[Order(3)]
public async Task GetFile()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var response = await api.Files.GetFilesAsync();
foreach (var file in response)
{
@@ -68,7 +68,7 @@ public async Task GetFile()
[Order(4)]
public async Task DeleteFiles()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var response = await api.Files.GetFilesAsync();
foreach (var file in response)
{
diff --git a/OpenAI_Tests/ModelEndpointTests.cs b/OpenAI_Tests/ModelEndpointTests.cs
index 3ccfef6..78dad3f 100644
--- a/OpenAI_Tests/ModelEndpointTests.cs
+++ b/OpenAI_Tests/ModelEndpointTests.cs
@@ -20,7 +20,7 @@ public void Setup()
[Test]
public void GetAllModels()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
Assert.IsNotNull(api.Models);
@@ -33,7 +33,7 @@ public void GetAllModels()
[Test]
public void GetModelDetails()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
Assert.IsNotNull(api.Models);
@@ -55,20 +55,20 @@ public void GetModelDetails()
[Test]
public async Task GetEnginesAsync_ShouldReturnTheEngineList()
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var models = await api.Models.GetModelsAsync();
models.Count.Should().BeGreaterOrEqualTo(5, "most engines should be returned");
}
- [Test]
- public void GetEnginesAsync_ShouldFailIfInvalidAuthIsProvided()
- {
- var api = new OpenAIAPI(new APIAuthentication(Guid.NewGuid().ToString()));
- Func act = () => api.Models.GetModelsAsync();
- act.Should()
- .ThrowAsync()
- .Where(exc => exc.Message.Contains("Incorrect API key provided"));
- }
+ // [Test]
+ // public void GetEnginesAsync_ShouldFailIfInvalidAuthIsProvided()
+ // {
+ // var api = new OpenAIAPI(new APIAuthentication(Guid.NewGuid().ToString()));
+ // Func act = () => api.Models.GetModelsAsync();
+ // act.Should()
+ // .ThrowAsync()
+ // .Where(exc => exc.Message.Contains("Incorrect API key provided"));
+ // }
[TestCase("ada")]
[TestCase("babbage")]
@@ -76,7 +76,7 @@ public void GetEnginesAsync_ShouldFailIfInvalidAuthIsProvided()
[TestCase("davinci")]
public async Task RetrieveEngineDetailsAsync_ShouldRetrieveEngineDetails(string modelId)
{
- var api = new OpenAI_API.OpenAIAPI();
+ var api = AuthTests.InitService();
var modelData = await api.Models.RetrieveModelDetailsAsync(modelId);
modelData?.ModelID?.Should()?.Be(modelId);
modelData.Created.Should().BeAfter(new DateTime(2018, 1, 1), "the model has a created date no earlier than 2018");
diff --git a/OpenAI_Tests/OpenAI_Tests.csproj b/OpenAI_Tests/OpenAI_Tests.csproj
index 805cc44..57ca2cd 100644
--- a/OpenAI_Tests/OpenAI_Tests.csproj
+++ b/OpenAI_Tests/OpenAI_Tests.csproj
@@ -1,13 +1,15 @@
- netcoreapp3.1
+ net7.0
false
+
+
diff --git a/README.md b/README.md
index b490204..8b73a01 100644
--- a/README.md
+++ b/README.md
@@ -43,11 +43,36 @@ Install package [`OpenAI` from Nuget](https://www.nuget.org/packages/OpenAI/).
Install-Package OpenAI
```
-### Authentication
+### Register for Dependency Injection
+If you are using Dependency Injection, you can register the API using `appsettings.json` configuration:
+
+```json
+"openAI": {
+ "key": "YOUR_API_KEY",
+ "org": "OPENAI_ORGANIZATION"
+}
+```
+
+Then in your `Program.cs` or `Startup.cs` add:
+
+```c#
+builder.Services.AddOpenAIService(builder.Configuration);
+```
+
+This registers `IOpenAI` for dependency injection using:
+- `[FromServices] IOpenAI? openAI` in your controllers.
+- `IOpenAI? openAI` in your constructor.
+
+#### Legacy constructors
+Previous versions of this package allowed direct instantiation of the service, but this causes connection pool exhaustion when multiple instances exist.
+
+It is still supported for backwards compatibility, but not recommended.
+
There are 3 ways to provide your API keys, in order of precedence:
1. Pass keys directly to `APIAuthentication(string key)` constructor
2. Set environment var for OPENAI_API_KEY (or OPENAI_KEY for backwards compatibility)
3. Include a config file in the local directory or in your user directory named `.openai` and containing the line:
+
```shell
OPENAI_API_KEY=sk-aaaabbbbbccccddddd
```