-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace IOpenAiClients with IAiClient and refactor clients
The 'IOpenAiClients' interface has been replaced by 'IAiClient', and all related classes have been refactored accordingly. `IAiClient` more accurately reflects that the client might not necessarily be specific to OpenAI and allows for broader API client development. Additional configuration files for AzureOpenAi and OpenRouter have also been added for more flexibility and future support. The version has been bumped to 4.0.0 due to this breaking change. Other changes include: - The Model property in ChatGPTConfig is not transformed anymore and assigned as it is. - Some variations in hard-coded strings have been fixed for consistency. - Documentation generation and some editor settings have been added to the build properties. These will help to better support various AI providers, allow more flexibility for developers and improve overall code quality. This update is a part of an ongoing effort to refactor and improve the codebase.
- Loading branch information
Showing
37 changed files
with
652 additions
and
237 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace OpenAI.ChatGpt.AspNetCore; | ||
|
||
internal class AiClientFactory | ||
{ | ||
private readonly IHttpClientFactory _httpClientFactory; | ||
private readonly OpenAICredentials _openAiCredentials; | ||
private readonly AzureOpenAICredentials _azureOpenAiCredentials; | ||
private readonly OpenRouterCredentials _openRouterCredentials; | ||
|
||
public AiClientFactory( | ||
IHttpClientFactory httpClientFactory, | ||
IOptions<OpenAICredentials> openAiCredentialsOptions, | ||
IOptions<AzureOpenAICredentials> azureOpenAiCredentialsOptions, | ||
IOptions<OpenRouterCredentials> openRouterCredentialsOptions) | ||
{ | ||
ArgumentNullException.ThrowIfNull(openAiCredentialsOptions); | ||
ArgumentNullException.ThrowIfNull(azureOpenAiCredentialsOptions); | ||
ArgumentNullException.ThrowIfNull(openRouterCredentialsOptions); | ||
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory)); | ||
_openAiCredentials = openAiCredentialsOptions.Value; | ||
_azureOpenAiCredentials = azureOpenAiCredentialsOptions.Value; | ||
_openRouterCredentials = openRouterCredentialsOptions.Value; | ||
} | ||
|
||
public OpenAiClient GetOpenAiClient() | ||
{ | ||
var httpClient = _httpClientFactory.CreateClient(nameof(OpenAiClient)); | ||
if (_openAiCredentials.ApiKey is null) | ||
{ | ||
throw new InvalidOperationException( | ||
$"OpenAI API key is not configured. Please configure it in {nameof(OpenAICredentials)}"); | ||
} | ||
_openAiCredentials.SetupHttpClient(httpClient); | ||
return new OpenAiClient(httpClient); | ||
} | ||
|
||
public AzureOpenAiClient GetAzureOpenAiClient() | ||
{ | ||
var httpClient = _httpClientFactory.CreateClient(nameof(AzureOpenAiClient)); | ||
if (_azureOpenAiCredentials.ApiKey is null) | ||
{ | ||
throw new InvalidOperationException( | ||
$"Azure OpenAI API key is not configured. Please configure it in {nameof(AzureOpenAICredentials)}"); | ||
} | ||
_azureOpenAiCredentials.SetupHttpClient(httpClient); | ||
return new AzureOpenAiClient(httpClient); | ||
} | ||
|
||
public OpenRouterClient GetOpenRouterClient() | ||
{ | ||
var httpClient = _httpClientFactory.CreateClient(nameof(OpenRouterClient)); | ||
if (_openRouterCredentials.ApiKey is null) | ||
{ | ||
throw new InvalidOperationException( | ||
$"OpenRouter API key is not configured. Please configure it in {nameof(OpenRouterCredentials)}"); | ||
} | ||
_openRouterCredentials.SetupHttpClient(httpClient); | ||
return new OpenRouterClient(httpClient); | ||
} | ||
} |
119 changes: 119 additions & 0 deletions
119
src/OpenAI.ChatGpt.AspNetCore/AiClientFromConfiguration.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
using Microsoft.Extensions.Configuration; | ||
|
||
namespace OpenAI.ChatGpt.AspNetCore; | ||
|
||
#pragma warning disable CS0618 // Type or member is obsolete | ||
internal class AiClientFromConfiguration : IAiClient, IOpenAiClient | ||
#pragma warning restore CS0618 // Type or member is obsolete | ||
{ | ||
private const string OpenAiProvider = "openai"; | ||
private const string AzureOpenAiProvider = "azure_openai"; | ||
private const string OpenRouterProvider = "openrouter"; | ||
|
||
private static readonly string[] Providers = | ||
{ | ||
OpenAiProvider, AzureOpenAiProvider, OpenRouterProvider | ||
}; | ||
private readonly IAiClient _client; | ||
|
||
public AiClientFromConfiguration( | ||
AiClientFactory clientFactory, | ||
IConfiguration configuration) | ||
{ | ||
ArgumentNullException.ThrowIfNull(clientFactory); | ||
ArgumentNullException.ThrowIfNull(configuration); | ||
var provider = configuration.GetValue<string>("AIProvider")?.ToLower(); | ||
provider ??= OpenAiProvider; | ||
if (!Providers.Contains(provider)) | ||
{ | ||
ThrowUnkownProviderException(provider); | ||
} | ||
_client = provider switch | ||
{ | ||
OpenAiProvider => clientFactory.GetOpenAiClient(), | ||
AzureOpenAiProvider => clientFactory.GetAzureOpenAiClient(), | ||
OpenRouterProvider => clientFactory.GetOpenRouterClient(), | ||
_ => throw new InvalidOperationException($"Unknown provider: {provider}") | ||
}; | ||
} | ||
|
||
|
||
private static void ThrowUnkownProviderException(string provider) | ||
{ | ||
throw new ArgumentException($"Unknown AI provider: {provider}. " + | ||
$"Supported providers: {string.Join(", ", Providers)}"); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task<string> GetChatCompletions(UserOrSystemMessage dialog, | ||
int maxTokens = ChatCompletionRequest.MaxTokensDefault, | ||
string model = ChatCompletionModels.Default, float temperature = ChatCompletionTemperatures.Default, | ||
string? user = null, bool jsonMode = false, long? seed = null, | ||
Action<ChatCompletionRequest>? requestModifier = null, | ||
Action<ChatCompletionResponse>? rawResponseGetter = null, CancellationToken cancellationToken = default) | ||
{ | ||
return _client.GetChatCompletions(dialog, maxTokens, model, temperature, user, jsonMode, seed, | ||
requestModifier, rawResponseGetter, cancellationToken); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task<string> GetChatCompletions(IEnumerable<ChatCompletionMessage> messages, | ||
int maxTokens = ChatCompletionRequest.MaxTokensDefault, | ||
string model = ChatCompletionModels.Default, float temperature = ChatCompletionTemperatures.Default, | ||
string? user = null, bool jsonMode = false, long? seed = null, | ||
Action<ChatCompletionRequest>? requestModifier = null, | ||
Action<ChatCompletionResponse>? rawResponseGetter = null, CancellationToken cancellationToken = default) | ||
{ | ||
return _client.GetChatCompletions(messages, maxTokens, model, temperature, user, jsonMode, seed, | ||
requestModifier, rawResponseGetter, cancellationToken); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public Task<ChatCompletionResponse> GetChatCompletionsRaw(IEnumerable<ChatCompletionMessage> messages, | ||
int maxTokens = ChatCompletionRequest.MaxTokensDefault, | ||
string model = ChatCompletionModels.Default, float temperature = ChatCompletionTemperatures.Default, | ||
string? user = null, bool jsonMode = false, long? seed = null, | ||
Action<ChatCompletionRequest>? requestModifier = null, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
return _client.GetChatCompletionsRaw(messages, maxTokens, model, temperature, user, jsonMode, seed, | ||
requestModifier, cancellationToken); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public IAsyncEnumerable<string> StreamChatCompletions(IEnumerable<ChatCompletionMessage> messages, | ||
int maxTokens = ChatCompletionRequest.MaxTokensDefault, | ||
string model = ChatCompletionModels.Default, float temperature = ChatCompletionTemperatures.Default, | ||
string? user = null, bool jsonMode = false, long? seed = null, | ||
Action<ChatCompletionRequest>? requestModifier = null, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
return _client.StreamChatCompletions(messages, maxTokens, model, temperature, user, jsonMode, seed, | ||
requestModifier, cancellationToken); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public IAsyncEnumerable<string> StreamChatCompletions(UserOrSystemMessage messages, | ||
int maxTokens = ChatCompletionRequest.MaxTokensDefault, string model = ChatCompletionModels.Default, | ||
float temperature = ChatCompletionTemperatures.Default, string? user = null, bool jsonMode = false, | ||
long? seed = null, Action<ChatCompletionRequest>? requestModifier = null, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
return _client.StreamChatCompletions(messages, maxTokens, model, temperature, user, jsonMode, seed, | ||
requestModifier, cancellationToken); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public IAsyncEnumerable<string> StreamChatCompletions(ChatCompletionRequest request, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
return _client.StreamChatCompletions(request, cancellationToken); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public IAsyncEnumerable<ChatCompletionResponse> StreamChatCompletionsRaw(ChatCompletionRequest request, | ||
CancellationToken cancellationToken = default) | ||
{ | ||
return _client.StreamChatCompletionsRaw(request, cancellationToken); | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/OpenAI.ChatGpt.AspNetCore/AiClientStartupValidationBackgroundService.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using Microsoft.Extensions.Hosting; | ||
|
||
namespace OpenAI.ChatGpt.AspNetCore; | ||
|
||
internal class AiClientStartupValidationBackgroundService : BackgroundService | ||
{ | ||
private readonly AiClientFromConfiguration _aiClient; | ||
|
||
public AiClientStartupValidationBackgroundService(AiClientFromConfiguration aiClient) | ||
{ | ||
_aiClient = aiClient ?? throw new ArgumentNullException(nameof(aiClient)); | ||
} | ||
|
||
protected override Task ExecuteAsync(CancellationToken stoppingToken) => Task.CompletedTask; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.