diff --git a/samples/ChatGptBlazor.Wasm/Components/ChatGptStream.razor b/samples/ChatGptBlazor.Wasm/Components/ChatGptStream.razor index 4d73032..15d95b8 100644 --- a/samples/ChatGptBlazor.Wasm/Components/ChatGptStream.razor +++ b/samples/ChatGptBlazor.Wasm/Components/ChatGptStream.razor @@ -1,14 +1,33 @@ @using ChatGptNet.Extensions; @using ChatGptNet.Models; @using ChatGptNet; +@using ChatGptNet.ServiceConfigurations @using Markdig; -@inject IChatGptClient chatGptClient +@inject IChatGptClientFactory chatGptClientFactory @inject IJSRuntime jsRuntime
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
@foreach (var message in messages) @@ -62,6 +81,12 @@ private string? message; private bool isAsking; + private string azureResource; + private string apiKey; + private string model; + + private IChatGptClient? chatGptClient = null; + private bool CanSendMessage() => !string.IsNullOrWhiteSpace(message) && !isAsking; @@ -73,6 +98,17 @@ } } + private void Connect() + { + chatGptClient = chatGptClientFactory.CreateClient(options => + { + options.UseAzure(azureResource, apiKey, authenticationType: AzureAuthenticationType.ApiKey); + options.DefaultModel = model; + options.MessageLimit = 16; // Default: 10 + options.MessageExpiration = TimeSpan.FromMinutes(5); // Default: 1 hour + }); + } + private async Task AskAsync() { if (!isAsking) diff --git a/samples/ChatGptBlazor.Wasm/Program.cs b/samples/ChatGptBlazor.Wasm/Program.cs index 447738a..595caf9 100644 --- a/samples/ChatGptBlazor.Wasm/Program.cs +++ b/samples/ChatGptBlazor.Wasm/Program.cs @@ -1,5 +1,6 @@ using ChatGptBlazor.Wasm; using ChatGptNet; +using ChatGptNet.ServiceConfigurations; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; @@ -10,27 +11,6 @@ builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); // Adds ChatGPT service and configure options via code. -builder.Services.AddChatGpt(options => -{ - // OpenAI. - //options.UseOpenAI(apiKey: "", organization: ""); - - // Azure OpenAI Service. - //options.UseAzure(resourceName: "", apiKey: "", authenticationType: AzureAuthenticationType.ApiKey); - - options.DefaultModel = "my-model"; - options.MessageLimit = 16; // Default: 10 - options.MessageExpiration = TimeSpan.FromMinutes(5); // Default: 1 hour -}, -httpClient => -{ - // Configures retry policy on the inner HttpClient using Polly. - httpClient.AddStandardResilienceHandler(options => - { - options.AttemptTimeout.Timeout = TimeSpan.FromMinutes(1); - options.CircuitBreaker.SamplingDuration = TimeSpan.FromMinutes(3); - options.TotalRequestTimeout.Timeout = TimeSpan.FromMinutes(3); - }); -}); +builder.Services.AddChatGptClientFactory(); await builder.Build().RunAsync(); diff --git a/samples/ChatGptBlazor.Wasm/Properties/launchSettings.json b/samples/ChatGptBlazor.Wasm/Properties/launchSettings.json index f9af232..deec97b 100644 --- a/samples/ChatGptBlazor.Wasm/Properties/launchSettings.json +++ b/samples/ChatGptBlazor.Wasm/Properties/launchSettings.json @@ -1,40 +1,32 @@ { - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:8206", - "sslPort": 44385 - } - }, "profiles": { "http": { "commandName": "Project", - "dotnetRunMessages": true, "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "http://localhost:5297", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "dotnetRunMessages": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "http://localhost:5297" }, "https": { "commandName": "Project", - "dotnetRunMessages": true, "launchBrowser": true, - "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:7021;http://localhost:5297", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, + }, + "dotnetRunMessages": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } + "applicationUrl": "https://localhost:7021;http://localhost:5297" + } + }, + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:8206", + "sslPort": 44385 } } -} +} \ No newline at end of file diff --git a/src/ChatGptNet/ChatGptClientFactory.cs b/src/ChatGptNet/ChatGptClientFactory.cs new file mode 100644 index 0000000..3d0736a --- /dev/null +++ b/src/ChatGptNet/ChatGptClientFactory.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.Caching.Memory; + +namespace ChatGptNet; + +internal class ChatGptClientFactory : IChatGptClientFactory +{ + private readonly IServiceProvider services; + private readonly IChatGptCache chatGptCache; + private readonly ChatGptOptionsBuilder defaultOptions; + + public ChatGptClientFactory(IServiceProvider services, IChatGptCache chatGptCache, ChatGptOptionsBuilder defaultOptions) + { + this.services = services; + this.chatGptCache = chatGptCache; + this.defaultOptions = defaultOptions; + } + + public IChatGptClient CreateClient(Action? setupAction) + { + var options = defaultOptions with { }; + + if (setupAction is not null) + setupAction(services, options); + + return new ChatGptClient(new HttpClient(), chatGptCache, options.Build()); + } + public IChatGptClient CreateClient(Action? setupAction) + { + if (setupAction is null) + return CreateClient(); + + return CreateClient((s, o) => setupAction(o)); + } + public IChatGptClient CreateClient() + { + return CreateClient((Action?)null); + } +} diff --git a/src/ChatGptNet/ChatGptFactoryServiceCollectionExtensions.cs b/src/ChatGptNet/ChatGptFactoryServiceCollectionExtensions.cs new file mode 100644 index 0000000..4523299 --- /dev/null +++ b/src/ChatGptNet/ChatGptFactoryServiceCollectionExtensions.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ChatGptNet.Models; +using ChatGptNet.ServiceConfigurations; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace ChatGptNet; +/// +/// Provides extension methods for adding ChatGPT Client Factory support in NET applications. +/// +public static class ChatGptFactoryServiceCollectionExtensions +{ + /// + /// Registers a instance with the specified options. + /// + /// The to add services to. + /// The to configure the provided . + /// A reference to this instance after the operation has completed. + public static IServiceCollection AddChatGptClientFactory(this IServiceCollection services, Action builder) + { + ArgumentNullException.ThrowIfNull(services); + + var options = new ChatGptOptionsBuilder(); + if (builder is not null) + builder.Invoke(options); + + return AddChatGptClientFactoryCore(services, options); + } + + /// + /// Registers a instance. + /// + /// The to add services to. + /// A reference to this instance after the operation has completed. + public static IServiceCollection AddChatGptClientFactory(this IServiceCollection services) + { + return services.AddChatGptClientFactory(null); + } + + private static IServiceCollection AddChatGptClientFactoryCore(this IServiceCollection services, ChatGptOptionsBuilder deafultOptions) + { + services.AddMemoryCache(); + services.AddSingleton(); + + var httpClientBuilder = services.AddHttpClient(); + services.AddSingleton( + s => new ChatGptClientFactory(s, s.GetRequiredService(), deafultOptions) + ); + + return services; + } + + private static void SetMissingDefaults(ChatGptOptions options) + { + // If the provider is OpenAI and no default model has been specified, uses gpt-3.5-turbo by default. + if (options.ServiceConfiguration is OpenAIChatGptServiceConfiguration && string.IsNullOrWhiteSpace(options.DefaultModel)) + { + options.DefaultModel = OpenAIChatGptModels.Gpt35Turbo; + } + } +} + +/// +/// Represents the default builder for configuring ChatGPT client factory. +/// +/// "/> +public class ChatGptClientFactoryBuilder : IChatGptClientFactoryBuilder +{ + /// + /// Gets the where ChatGPT services are registered. + /// + public IServiceCollection Services { get; } + + /// + /// + /// Gets the used to configure the used by ChatGPT. + /// + public IHttpClientBuilder HttpClientBuilder { get; } + + internal ChatGptClientFactoryBuilder(IServiceCollection services, IHttpClientBuilder httpClientBuilder) + { + Services = services; + HttpClientBuilder = httpClientBuilder; + } +} + +/// +/// Represents a builder for configuring ChatGPT client factory. +/// +public interface IChatGptClientFactoryBuilder +{ + /// + /// Gets the where ChatGPT services are registered. + /// + IServiceCollection Services { get; } + + /// + /// Gets the used to configure the used by ChatGPT. + /// + IHttpClientBuilder HttpClientBuilder { get; } +} diff --git a/src/ChatGptNet/ChatGptOptions.cs b/src/ChatGptNet/ChatGptOptions.cs index 7d291c5..77004d8 100644 --- a/src/ChatGptNet/ChatGptOptions.cs +++ b/src/ChatGptNet/ChatGptOptions.cs @@ -8,7 +8,7 @@ namespace ChatGptNet; /// /// Options class that provides settings for configuring ChatGPT. /// -public class ChatGptOptions +public record ChatGptOptions { /// /// Gets or sets the configuration settings for accessing the service. diff --git a/src/ChatGptNet/ChatGptOptionsBuilder.cs b/src/ChatGptNet/ChatGptOptionsBuilder.cs index 721a801..5b13769 100644 --- a/src/ChatGptNet/ChatGptOptionsBuilder.cs +++ b/src/ChatGptNet/ChatGptOptionsBuilder.cs @@ -8,7 +8,7 @@ namespace ChatGptNet; /// /// Builder class to define settings for configuring ChatGPT. /// -public class ChatGptOptionsBuilder +public record ChatGptOptionsBuilder { /// /// Gets or sets the configuration settings for accessing the service. diff --git a/src/ChatGptNet/IChatGptClientFactory.cs b/src/ChatGptNet/IChatGptClientFactory.cs new file mode 100644 index 0000000..ed5f31b --- /dev/null +++ b/src/ChatGptNet/IChatGptClientFactory.cs @@ -0,0 +1,28 @@ +namespace ChatGptNet; + +/// +/// Provides methods to create new instances of at runtime +/// +public interface IChatGptClientFactory +{ + /// + /// Creates a new instance of a ChatGptClient configured with the supplied action. + /// + /// The to configure the provided . + /// A new + IChatGptClient CreateClient(Action? setupAction); + + /// + /// Creates a new instance of a ChatGptClient configured with the supplied action. + /// + /// The to configure the provided . + /// A new + IChatGptClient CreateClient(Action? setupAction); + + /// + /// Creates a new instance of a ChatGptClient. + /// + /// A new + IChatGptClient CreateClient(); + +}