diff --git a/README.md b/README.md index 571c5c6..e2c7c6c 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,29 @@ builder.Services.AddChatGpt((services, options) => }); ``` +### Configuring HTTP Client + +**ChatGptNet** uses an [HttpClient](https://docs.microsoft.com/dotnet/api/system.net.http.httpclient) to call the chat completion and embedding APIs. If you need to customize it, you can use the overload of the **AddChatGpt** method that accepts an [Action<IHttpClientBuiler>](https://learn.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.ihttpclientbuilder) as argument. For example, if you want to add resiliency to the HTTP client (let's say a retry policy), you can use [Polly](https://github.com/App-vNext/Polly): + +```csharp +// using Microsoft.Extensions.DependencyInjection; +// Requires: Microsoft.Extensions.Http.Resilience + +builder.Services.AddChatGpt(context.Configuration, + 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); + }); + }) +``` + +More information about this topic is available on the [official documentation](https://learn.microsoft.com/dotnet/core/resilience/http-resilience). + ## Usage The library can be used in any .NET application built with .NET 6.0 or later. For example, we can create a Minimal API in this way: diff --git a/docs/ChatGptNet/ChatGptServiceCollectionExtensions.md b/docs/ChatGptNet/ChatGptServiceCollectionExtensions.md index 084e54f..32538dd 100644 --- a/docs/ChatGptNet/ChatGptServiceCollectionExtensions.md +++ b/docs/ChatGptNet/ChatGptServiceCollectionExtensions.md @@ -10,7 +10,7 @@ public static class ChatGptServiceCollectionExtensions | name | description | | --- | --- | -| static [AddChatGpt](ChatGptServiceCollectionExtensions/AddChatGpt.md)(…) | Registers a ChatGptClient instance with the specified options. (3 methods) | +| static [AddChatGpt](ChatGptServiceCollectionExtensions/AddChatGpt.md)(…) | Registers a ChatGptClient instance with the specified options. (5 methods) | ## See Also diff --git a/docs/ChatGptNet/ChatGptServiceCollectionExtensions/AddChatGpt.md b/docs/ChatGptNet/ChatGptServiceCollectionExtensions/AddChatGpt.md index 390063b..200e272 100644 --- a/docs/ChatGptNet/ChatGptServiceCollectionExtensions/AddChatGpt.md +++ b/docs/ChatGptNet/ChatGptServiceCollectionExtensions/AddChatGpt.md @@ -1,16 +1,17 @@ -# ChatGptServiceCollectionExtensions.AddChatGpt method (1 of 3) +# ChatGptServiceCollectionExtensions.AddChatGpt method (1 of 5) Registers a ChatGptClient instance with the specified options. ```csharp public static IChatGptBuilder AddChatGpt(this IServiceCollection services, - Action builder) + Action builder, Action? httpClientBuilder = null) ``` | parameter | description | | --- | --- | | services | The IServiceCollection to add services to. | | builder | The [`ChatGptOptionsBuilder`](../ChatGptOptionsBuilder.md) to configure options. | +| httpClientBuilder | The IHttpClientBuilder to configure the HTTP client used to make HTTP requests. | ## Return Value @@ -29,19 +30,21 @@ This method automatically adds a MemoryCache that is used to save conversation h --- -# ChatGptServiceCollectionExtensions.AddChatGpt method (2 of 3) +# ChatGptServiceCollectionExtensions.AddChatGpt method (2 of 5) Registers a ChatGptClient instance using dynamic options. ```csharp public static IChatGptBuilder AddChatGpt(this IServiceCollection services, - Action builder) + Action builder, + Action? httpClientBuilder = null) ``` | parameter | description | | --- | --- | | services | The IServiceCollection to add services to. | | builder | The [`ChatGptOptionsBuilder`](../ChatGptOptionsBuilder.md) to configure options. | +| httpClientBuilder | The IHttpClientBuilder to configure the HTTP client used to make HTTP requests. | ## Return Value @@ -61,20 +64,78 @@ Use this this method if it is necessary to dynamically set options (for example, --- -# ChatGptServiceCollectionExtensions.AddChatGpt method (3 of 3) +# ChatGptServiceCollectionExtensions.AddChatGpt method (3 of 5) + +Registers a ChatGptClient instance reading configuration from the specified IConfiguration source, searching for the ChatGPT section. + +```csharp +public static IChatGptBuilder AddChatGpt(this IServiceCollection services, + IConfiguration configuration, Action? httpClientBuilder = null) +``` + +| parameter | description | +| --- | --- | +| services | The IServiceCollection to add services to. | +| configuration | The IConfiguration being bound. | +| httpClientBuilder | The IHttpClientBuilder to configure the HTTP client used to make HTTP requests. | + +## Remarks + +This method automatically adds a MemoryCache that is used to save conversation history for chat completion. It is possibile to use [`WithCache`](../IChatGptBuilderExtensions/WithCache.md) to specify another cache implementation. + +## See Also + +* class [ChatGptOptions](../ChatGptOptions.md) +* interface [IChatGptBuilder](../IChatGptBuilder.md) +* class [ChatGptServiceCollectionExtensions](../ChatGptServiceCollectionExtensions.md) +* namespace [ChatGptNet](../../ChatGptNet.md) + +--- + +# ChatGptServiceCollectionExtensions.AddChatGpt method (4 of 5) + +Registers a ChatGptClient instance reading configuration from the specified IConfiguration source. + +```csharp +public static IChatGptBuilder AddChatGpt(this IServiceCollection services, + IConfiguration configuration, string sectionName) +``` + +| parameter | description | +| --- | --- | +| services | The IServiceCollection to add services to. | +| configuration | The IConfiguration being bound. | +| sectionName | The name of the configuration section that holds ChatGPT settings. | + +## Remarks + +This method automatically adds a MemoryCache that is used to save conversation history for chat completion. It is possibile to use [`WithCache`](../IChatGptBuilderExtensions/WithCache.md) to specify another cache implementation. + +## See Also + +* class [ChatGptOptions](../ChatGptOptions.md) +* interface [IChatGptBuilder](../IChatGptBuilder.md) +* class [ChatGptServiceCollectionExtensions](../ChatGptServiceCollectionExtensions.md) +* namespace [ChatGptNet](../../ChatGptNet.md) + +--- + +# ChatGptServiceCollectionExtensions.AddChatGpt method (5 of 5) Registers a ChatGptClient instance reading configuration from the specified IConfiguration source. ```csharp public static IChatGptBuilder AddChatGpt(this IServiceCollection services, - IConfiguration configuration, string sectionName = "ChatGPT") + IConfiguration configuration, string sectionName, + Action? httpClientBuilder = null) ``` | parameter | description | | --- | --- | | services | The IServiceCollection to add services to. | | configuration | The IConfiguration being bound. | -| sectionName | The name of the configuration section that holds ChatGPT settings (default: ChatGPT). | +| sectionName | The name of the configuration section that holds ChatGPT settings. | +| httpClientBuilder | The IHttpClientBuilder to configure the HTTP client used to make HTTP requests. | ## Return Value diff --git a/docs/README.md b/docs/README.md index 8b26f5e..e2a9ae3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -55,7 +55,7 @@ | class [ChatGptTool](./ChatGptNet.Models/ChatGptTool.md) | Represents a tool that the model may call. | | class [ChatGptToolCall](./ChatGptNet.Models/ChatGptToolCall.md) | A tool call generated by the model, such as a function call. | | static class [ChatGptToolChoices](./ChatGptNet.Models/ChatGptToolChoices.md) | Contains constants for ChatGPT function call types. | -| class [ChatGptToolParameters](./ChatGptNet.Models/ChatGptToolParameters.md) | Contains parameters about the tools calls that are available for ChatGPT. | +| class [ChatGptToolParameters](./ChatGptNet.Models/ChatGptToolParameters.md) | Contains parameters about the tool calls that are available for ChatGPT. | | static class [ChatGptToolTypes](./ChatGptNet.Models/ChatGptToolTypes.md) | Contains constants for ChatGPT tool types. | | class [ChatGptUsage](./ChatGptNet.Models/ChatGptUsage.md) | Contains information about the API usage. | | static class [OpenAIChatGptModels](./ChatGptNet.Models/OpenAIChatGptModels.md) | Contains all the chat completion models that are currently supported by OpenAI. | diff --git a/samples/ChatGptApi/ChatGptApi.csproj b/samples/ChatGptApi/ChatGptApi.csproj index 7840b4c..54659ec 100644 --- a/samples/ChatGptApi/ChatGptApi.csproj +++ b/samples/ChatGptApi/ChatGptApi.csproj @@ -1,18 +1,20 @@ - - net8.0 - enable - enable - + + net8.0 + enable + enable + - - - - + + + + + + - - - + + + diff --git a/samples/ChatGptApi/Program.cs b/samples/ChatGptApi/Program.cs index 9164020..325ffd5 100644 --- a/samples/ChatGptApi/Program.cs +++ b/samples/ChatGptApi/Program.cs @@ -25,7 +25,17 @@ //}); // Adds ChatGPT service using settings from IConfiguration. -builder.Services.AddChatGpt(builder.Configuration); +builder.Services.AddChatGpt(builder.Configuration, + 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.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); diff --git a/samples/ChatGptBlazor.Wasm/ChatGptBlazor.Wasm.csproj b/samples/ChatGptBlazor.Wasm/ChatGptBlazor.Wasm.csproj index ca361a7..15e93d1 100644 --- a/samples/ChatGptBlazor.Wasm/ChatGptBlazor.Wasm.csproj +++ b/samples/ChatGptBlazor.Wasm/ChatGptBlazor.Wasm.csproj @@ -1,24 +1,26 @@ - - net8.0 - enable - enable - service-worker-assets.js - + + net8.0 + enable + enable + service-worker-assets.js + - - - - - + + + + + + + - - - + + + - - - + + + diff --git a/samples/ChatGptBlazor.Wasm/Program.cs b/samples/ChatGptBlazor.Wasm/Program.cs index 6de8179..447738a 100644 --- a/samples/ChatGptBlazor.Wasm/Program.cs +++ b/samples/ChatGptBlazor.Wasm/Program.cs @@ -21,6 +21,16 @@ 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); + }); }); await builder.Build().RunAsync(); diff --git a/samples/ChatGptConsole/Application.cs b/samples/ChatGptConsole/Application.cs index 4e44a76..7098fa9 100644 --- a/samples/ChatGptConsole/Application.cs +++ b/samples/ChatGptConsole/Application.cs @@ -4,15 +4,8 @@ namespace ChatGptConsole; -internal class Application +internal class Application(IChatGptClient chatGptClient) { - private readonly IChatGptClient chatGptClient; - - public Application(IChatGptClient chatGptClient) - { - this.chatGptClient = chatGptClient; - } - public async Task ExecuteAsync() { string? message = null; diff --git a/samples/ChatGptConsole/ChatGptConsole.csproj b/samples/ChatGptConsole/ChatGptConsole.csproj index 99a79e0..3d6f605 100644 --- a/samples/ChatGptConsole/ChatGptConsole.csproj +++ b/samples/ChatGptConsole/ChatGptConsole.csproj @@ -1,24 +1,26 @@ - + - - Exe - net8.0 - enable - enable - + + Exe + net8.0 + enable + enable + - - - + + + + + - - - + + + - - - Always - - + + + Always + + diff --git a/samples/ChatGptConsole/Program.cs b/samples/ChatGptConsole/Program.cs index 4aca551..ba1b984 100644 --- a/samples/ChatGptConsole/Program.cs +++ b/samples/ChatGptConsole/Program.cs @@ -15,7 +15,17 @@ static void ConfigureServices(HostBuilderContext context, IServiceCollection ser services.AddSingleton(); // Adds ChatGPT service using settings from IConfiguration. - services.AddChatGpt(context.Configuration) + services.AddChatGpt(context.Configuration, + 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); + }); + }) //.WithCache() // Uncomment this line to use a custom cache implementation instead of the default MemoryCache. ; diff --git a/samples/ChatGptFunctionCallingConsole/ChatGptFunctionCallingConsole.csproj b/samples/ChatGptFunctionCallingConsole/ChatGptFunctionCallingConsole.csproj index 99a79e0..d74f6bc 100644 --- a/samples/ChatGptFunctionCallingConsole/ChatGptFunctionCallingConsole.csproj +++ b/samples/ChatGptFunctionCallingConsole/ChatGptFunctionCallingConsole.csproj @@ -1,24 +1,26 @@ - - Exe - net8.0 - enable - enable - + + Exe + net8.0 + enable + enable + - - - + + + + + - - - + + + - - - Always - - + + + Always + + diff --git a/samples/ChatGptFunctionCallingConsole/Program.cs b/samples/ChatGptFunctionCallingConsole/Program.cs index 3a4494b..8d98718 100644 --- a/samples/ChatGptFunctionCallingConsole/Program.cs +++ b/samples/ChatGptFunctionCallingConsole/Program.cs @@ -15,7 +15,17 @@ static void ConfigureServices(HostBuilderContext context, IServiceCollection ser services.AddSingleton(); // Adds ChatGPT service using settings from IConfiguration. - services.AddChatGpt(context.Configuration) + services.AddChatGpt(context.Configuration, + 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); + }); + }) //.WithCache() // Uncomment this line to use a custom cache implementation instead of the default MemoryCache. ; @@ -45,7 +55,7 @@ static void ConfigureServices(HostBuilderContext context, IServiceCollection ser public class LocalMessageCache : IChatGptCache { - private readonly Dictionary> localCache = new(); + private readonly Dictionary> localCache = []; public Task SetAsync(Guid conversationId, IEnumerable messages, TimeSpan expiration, CancellationToken cancellationToken = default) { diff --git a/samples/ChatGptStreamConsole/ChatGptStreamConsole.csproj b/samples/ChatGptStreamConsole/ChatGptStreamConsole.csproj index e39714d..23d12aa 100644 --- a/samples/ChatGptStreamConsole/ChatGptStreamConsole.csproj +++ b/samples/ChatGptStreamConsole/ChatGptStreamConsole.csproj @@ -1,24 +1,26 @@ - - Exe - net8.0 - enable - enable - + + Exe + net8.0 + enable + enable + + + - - - + + + - - - PreserveNewest - - + + + PreserveNewest + + diff --git a/samples/ChatGptStreamConsole/Program.cs b/samples/ChatGptStreamConsole/Program.cs index 6e0725e..793e91c 100644 --- a/samples/ChatGptStreamConsole/Program.cs +++ b/samples/ChatGptStreamConsole/Program.cs @@ -28,5 +28,15 @@ static void ConfigureServices(HostBuilderContext context, IServiceCollection ser //}); // Adds ChatGPT service using settings from IConfiguration. - services.AddChatGpt(context.Configuration); + services.AddChatGpt(context.Configuration, + 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); + }); + }); } diff --git a/src/ChatGptNet/ChatGptNet.csproj b/src/ChatGptNet/ChatGptNet.csproj index d368455..7378e71 100644 --- a/src/ChatGptNet/ChatGptNet.csproj +++ b/src/ChatGptNet/ChatGptNet.csproj @@ -41,7 +41,7 @@ - + diff --git a/src/ChatGptNet/ChatGptServiceCollectionExtensions.cs b/src/ChatGptNet/ChatGptServiceCollectionExtensions.cs index 3b408b8..a96f549 100644 --- a/src/ChatGptNet/ChatGptServiceCollectionExtensions.cs +++ b/src/ChatGptNet/ChatGptServiceCollectionExtensions.cs @@ -17,6 +17,7 @@ public static class ChatGptServiceCollectionExtensions /// /// The to add services to. /// The to configure options. + /// The to configure the HTTP client used to make HTTP requests. /// A that can be used to further customize ChatGPT. /// This method automatically adds a that is used to save conversation history for chat completion. /// It is possibile to use to specify another cache implementation. @@ -24,7 +25,8 @@ public static class ChatGptServiceCollectionExtensions /// /// /// - public static IChatGptBuilder AddChatGpt(this IServiceCollection services, Action builder) + /// + public static IChatGptBuilder AddChatGpt(this IServiceCollection services, Action builder, Action? httpClientBuilder = null) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(builder); @@ -37,7 +39,10 @@ public static IChatGptBuilder AddChatGpt(this IServiceCollection services, Actio SetMissingDefaults(options); services.AddSingleton(options.Build()); - return AddChatGptCore(services); + var chatGptBuilder = AddChatGptCore(services); + httpClientBuilder?.Invoke(chatGptBuilder.HttpClientBuilder); + + return chatGptBuilder; } /// @@ -45,8 +50,7 @@ public static IChatGptBuilder AddChatGpt(this IServiceCollection services, Actio /// /// The to add services to. /// The being bound. - /// The name of the configuration section that holds ChatGPT settings (default: ChatGPT). - /// A that can be used to further customize ChatGPT. + /// The name of the configuration section that holds ChatGPT settings. /// This method automatically adds a that is used to save conversation history for chat completion. /// It is possibile to use to specify another cache implementation. /// @@ -54,7 +58,42 @@ public static IChatGptBuilder AddChatGpt(this IServiceCollection services, Actio /// /// /// - public static IChatGptBuilder AddChatGpt(this IServiceCollection services, IConfiguration configuration, string sectionName = "ChatGPT") + public static IChatGptBuilder AddChatGpt(this IServiceCollection services, IConfiguration configuration, string sectionName) + => services.AddChatGpt(configuration, sectionName, null); + + /// + /// Registers a instance reading configuration from the specified source, searching for the ChatGPT section. + /// + /// The to add services to. + /// The being bound. + /// The to configure the HTTP client used to make HTTP requests. + /// This method automatically adds a that is used to save conversation history for chat completion. + /// It is possibile to use to specify another cache implementation. + /// + /// + /// + /// + /// + /// + public static IChatGptBuilder AddChatGpt(this IServiceCollection services, IConfiguration configuration, Action? httpClientBuilder = null) + => services.AddChatGpt(configuration, "ChatGPT", httpClientBuilder); + + /// + /// Registers a instance reading configuration from the specified source. + /// + /// The to add services to. + /// The being bound. + /// The name of the configuration section that holds ChatGPT settings. + /// The to configure the HTTP client used to make HTTP requests./// A that can be used to further customize ChatGPT. + /// This method automatically adds a that is used to save conversation history for chat completion. + /// It is possibile to use to specify another cache implementation. + /// + /// + /// + /// + /// + /// + public static IChatGptBuilder AddChatGpt(this IServiceCollection services, IConfiguration configuration, string sectionName, Action? httpClientBuilder = null) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(configuration); @@ -65,7 +104,10 @@ public static IChatGptBuilder AddChatGpt(this IServiceCollection services, IConf SetMissingDefaults(options); services.AddSingleton(options.Build()); - return AddChatGptCore(services); + var chatGptBuilder = AddChatGptCore(services); + httpClientBuilder?.Invoke(chatGptBuilder.HttpClientBuilder); + + return chatGptBuilder; } /// @@ -73,6 +115,7 @@ public static IChatGptBuilder AddChatGpt(this IServiceCollection services, IConf /// /// The to add services to. /// The to configure options. + /// The to configure the HTTP client used to make HTTP requests. /// A that can be used to further customize ChatGPT. /// Use this this method if it is necessary to dynamically set options (for example, using other services via dependency injection). /// This method automatically adds a that is used to save conversation history for chat completion. @@ -82,7 +125,8 @@ public static IChatGptBuilder AddChatGpt(this IServiceCollection services, IConf /// /// /// - public static IChatGptBuilder AddChatGpt(this IServiceCollection services, Action builder) + /// + public static IChatGptBuilder AddChatGpt(this IServiceCollection services, Action builder, Action? httpClientBuilder = null) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(builder); @@ -98,10 +142,13 @@ public static IChatGptBuilder AddChatGpt(this IServiceCollection services, Actio return options.Build(); }); - return AddChatGptCore(services); + var chatGptBuilder = AddChatGptCore(services); + httpClientBuilder?.Invoke(chatGptBuilder.HttpClientBuilder); + + return chatGptBuilder; } - private static IChatGptBuilder AddChatGptCore(IServiceCollection services) + private static ChatGptBuilder AddChatGptCore(IServiceCollection services) { // Uses MemoryCache by default. services.AddMemoryCache(); diff --git a/src/ChatGptNet/version.json b/src/ChatGptNet/version.json index 1bfe5f7..3222e9d 100644 --- a/src/ChatGptNet/version.json +++ b/src/ChatGptNet/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "3.0", + "version": "3.1", "publicReleaseRefSpec": [ "^refs/heads/master$" // we release out of master ],