From a660b5d34aa26ebbf924da23d288a4600776db58 Mon Sep 17 00:00:00 2001 From: richardrigutins <47950599+richardrigutins@users.noreply.github.com> Date: Sun, 10 Dec 2023 22:49:44 +0100 Subject: [PATCH] Add support for both Azure OpenAI and non-Azure OpenAI --- README.md | 21 ++++++++++----- src/VirgilAgent.ChatService/Program.cs | 12 ++++----- ...IChatAIClient.cs => OpenAIChatAIClient.cs} | 23 +++++++++++----- src/VirgilAgent.ChatService/appsettings.json | 2 +- ...AzureOpenAIOptions.cs => OpenAIOptions.cs} | 4 +-- src/VirgilAgent.SuggestionsService/Program.cs | 12 ++++----- ...Client.cs => OpenAISuggestionsAIClient.cs} | 27 ++++++++++++------- .../appsettings.json | 2 +- 8 files changed, 64 insertions(+), 39 deletions(-) rename src/VirgilAgent.ChatService/Services/{AzureOpenAIChatAIClient.cs => OpenAIChatAIClient.cs} (91%) rename src/VirgilAgent.Core/{AzureOpenAIOptions.cs => OpenAIOptions.cs} (63%) rename src/VirgilAgent.SuggestionsService/Services/{AzureOpenAISuggestionsAIClient.cs => OpenAISuggestionsAIClient.cs} (89%) diff --git a/README.md b/README.md index df730ae..93f5baa 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,10 @@ Virgil also enriches chat messages by suggesting follow-up actions (e.g. opening Virgil Agent is powered by .NET 8, using [.NET Aspire](https://learn.microsoft.com/en-us/dotnet/aspire/get-started/aspire-overview) to build and deploy the application, and the [Microsoft Bot Framework](https://dev.botframework.com/) to provide the chatbot functionality. -The AI capabilities are provided by the [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service). +The AI capabilities can be provided by services such as: + +- [Azure OpenAI Service](https://azure.microsoft.com/en-us/products/ai-services/openai-service) +- [OpenAI](https://openai.com/) The application consists of multiple components: @@ -37,30 +40,34 @@ VirgilAgent does not currently include a frontend application, but, through the - [Docker](https://www.docker.com/) - [Bot Framework Emulator](https://github.com/microsoft/BotFramework-Emulator/blob/master/README.md) - .NET Aspire workload -- Azure OpenAI Service endpoint and key +- Azure OpenAI Service endpoint and key (if using Azure OpenAI), or an OpenAI API key (if using OpenAI) ### Required configuration -Before running the application, you need to configure the Azure OpenAI endpoint and key that will be used by the chat service and the suggestions service. +Before running the application, you need to configure the OpenAI endpoint and key that will be used by the chat service and the suggestions service. -To do so, open the `appsettings.json` file in both the `VirgilAgent.ChatService` and `VirgilAgent.SuggestionsService` projects, and set the following values to the endpoint and key of your Azure OpenAI service: +To do so, open the `appsettings.json` file in both the `VirgilAgent.ChatService` and `VirgilAgent.SuggestionsService` projects, and set the following values: ```json -"AzureOpenAI": { +"OpenAI": { "Endpoint": "YOUR_ENDPOINT_HERE", "Key": "YOUR_KEY_HERE", ... }, ``` +If you are using Azure OpenAI Service, you need to provide both the endpoint and the key; if you are using non-Azure OpenAI, leave the endpoint empty and provide only the API key. + +Make sure to also update the `DeploymentName` value in the same section to match the name of a valid model or deployment. + ### Additional configuration The application has a few additional configuration options that you can set in the `appsettings.json` files, to customize the behavior of the application and tweak the performance of the AI service. -For instance, the `AzureOpenAI` section can be used to set the parameters of the Azure OpenAI service (e.g. the temperature of the generated responses, the maximum number of tokens, etc.). +For instance, the `OpenAI` section can be used to set the parameters of the OpenAI service (e.g. the temperature of the generated responses, the maximum number of tokens, etc.). Instead, the `Chat` section can be used to set the maximum number of messages that are stored in the cache for a conversation (a greater number of messages will improve the quality of the responses, but will also increase the number of tokens used by the AI service). -> **Note**: be careful to set the parameters appropriately, to avoid exceeding the limits of the Azure OpenAI service (e.g. the maximum number of tokens for a conversation). +> **Note**: be careful to set the parameters appropriately, to avoid exceeding the limits of the OpenAI service (e.g. the maximum number of tokens for a conversation). ### Running the application diff --git a/src/VirgilAgent.ChatService/Program.cs b/src/VirgilAgent.ChatService/Program.cs index b225802..e63961b 100644 --- a/src/VirgilAgent.ChatService/Program.cs +++ b/src/VirgilAgent.ChatService/Program.cs @@ -19,11 +19,11 @@ var configuration = builder.Configuration; -// Bind AzureOpenAIOptions from configuration. -string azureOpenAIOptionsSectionName = "AzureOpenAI"; -AzureOpenAIOptions azureOpenAIOptions = configuration.GetSection(azureOpenAIOptionsSectionName).Get() - ?? throw new InvalidOperationException($"Missing configuration section: {azureOpenAIOptionsSectionName}"); -builder.Services.AddSingleton(azureOpenAIOptions); +// Bind OpenAIOptions from configuration. +string openAIOptionsSectionName = "OpenAI"; +OpenAIOptions openAIOptions = configuration.GetSection(openAIOptionsSectionName).Get() + ?? throw new InvalidOperationException($"Missing configuration section: {openAIOptionsSectionName}"); +builder.Services.AddSingleton(openAIOptions); // Add cache. builder.AddCache("Cache"); @@ -33,7 +33,7 @@ ChatOptions chatOptions = configuration.GetSection(chatOptionsSectionName).Get() ?? throw new InvalidOperationException($"Missing configuration section: {chatOptionsSectionName}"); builder.Services.AddSingleton(chatOptions); -builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddSingleton(); var app = builder.Build(); diff --git a/src/VirgilAgent.ChatService/Services/AzureOpenAIChatAIClient.cs b/src/VirgilAgent.ChatService/Services/OpenAIChatAIClient.cs similarity index 91% rename from src/VirgilAgent.ChatService/Services/AzureOpenAIChatAIClient.cs rename to src/VirgilAgent.ChatService/Services/OpenAIChatAIClient.cs index 097ff2e..3d093ac 100644 --- a/src/VirgilAgent.ChatService/Services/AzureOpenAIChatAIClient.cs +++ b/src/VirgilAgent.ChatService/Services/OpenAIChatAIClient.cs @@ -5,9 +5,9 @@ namespace VirgilAgent.ChatService.Services; /// -/// Azure OpenAI implementation of the interface. +/// OpenAI implementation of the interface. /// -internal class AzureOpenAIChatAIClient : IChatAIClient +internal class OpenAIChatAIClient : IChatAIClient { private const string SystemPrompt = @" Your name is Virgil. @@ -22,16 +22,25 @@ Give answers that are less than 200 words long. Reply that you don't know if you don't know how to answer a question."; private readonly OpenAIClient _openAIClient; - private readonly AzureOpenAIOptions _options; + private readonly OpenAIOptions _options; - public AzureOpenAIChatAIClient(AzureOpenAIOptions options) + public OpenAIChatAIClient(OpenAIOptions options) { _options = options; - Uri endpoint = new(_options.Endpoint); - AzureKeyCredential token = new(_options.Key); + if (string.IsNullOrWhiteSpace(_options.Endpoint)) + { + // Use a non-Azure OpenAI endpoint. + _openAIClient = new(_options.Key); + } + else + { + // Use the specified Azure OpenAI Service endpoint. + Uri endpoint = new(_options.Endpoint); + AzureKeyCredential token = new(_options.Key); - _openAIClient = new(endpoint, token); + _openAIClient = new(endpoint, token); + } } /// diff --git a/src/VirgilAgent.ChatService/appsettings.json b/src/VirgilAgent.ChatService/appsettings.json index 9c31a59..fcd2f34 100644 --- a/src/VirgilAgent.ChatService/appsettings.json +++ b/src/VirgilAgent.ChatService/appsettings.json @@ -6,7 +6,7 @@ } }, "AllowedHosts": "*", - "AzureOpenAI": { + "OpenAI": { "Endpoint": "YOUR_ENDPOINT_HERE", "Key": "YOUR_KEY_HERE", "DeploymentName": "gpt-35-turbo", diff --git a/src/VirgilAgent.Core/AzureOpenAIOptions.cs b/src/VirgilAgent.Core/OpenAIOptions.cs similarity index 63% rename from src/VirgilAgent.Core/AzureOpenAIOptions.cs rename to src/VirgilAgent.Core/OpenAIOptions.cs index b1bc3cb..39570cb 100644 --- a/src/VirgilAgent.Core/AzureOpenAIOptions.cs +++ b/src/VirgilAgent.Core/OpenAIOptions.cs @@ -1,9 +1,9 @@ namespace VirgilAgent.Core; /// -/// Options for configuring the Azure OpenAI service. +/// Options for configuring the OpenAI service. /// -public record AzureOpenAIOptions( +public record OpenAIOptions( string Endpoint, string Key, string DeploymentName, diff --git a/src/VirgilAgent.SuggestionsService/Program.cs b/src/VirgilAgent.SuggestionsService/Program.cs index 661d9ff..82fdf7a 100644 --- a/src/VirgilAgent.SuggestionsService/Program.cs +++ b/src/VirgilAgent.SuggestionsService/Program.cs @@ -19,14 +19,14 @@ var configuration = builder.Configuration; -// Bind AzureOpenAIOptions from configuration. -string azureOpenAIOptionsSection = "AzureOpenAI"; -var azureOpenAIOptions = configuration.GetSection(azureOpenAIOptionsSection).Get() - ?? throw new InvalidOperationException($"Missing configuration section: {azureOpenAIOptionsSection}"); -builder.Services.AddSingleton(azureOpenAIOptions); +// Bind OpenAIOptions from configuration. +string openAIOptionsSection = "OpenAI"; +var openAIOptions = configuration.GetSection(openAIOptionsSection).Get() + ?? throw new InvalidOperationException($"Missing configuration section: {openAIOptionsSection}"); +builder.Services.AddSingleton(openAIOptions); // Add AI services. -builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddSingleton(); var app = builder.Build(); diff --git a/src/VirgilAgent.SuggestionsService/Services/AzureOpenAISuggestionsAIClient.cs b/src/VirgilAgent.SuggestionsService/Services/OpenAISuggestionsAIClient.cs similarity index 89% rename from src/VirgilAgent.SuggestionsService/Services/AzureOpenAISuggestionsAIClient.cs rename to src/VirgilAgent.SuggestionsService/Services/OpenAISuggestionsAIClient.cs index 8da882f..177c2d9 100644 --- a/src/VirgilAgent.SuggestionsService/Services/AzureOpenAISuggestionsAIClient.cs +++ b/src/VirgilAgent.SuggestionsService/Services/OpenAISuggestionsAIClient.cs @@ -5,9 +5,9 @@ namespace VirgilAgent.SuggestionsService.Services; /// -/// Azure OpenAI implementation of the interface. +/// OpenAI implementation of the interface. /// -internal class AzureOpenAISuggestionsAIClient : ISuggestionsAIClient +internal class OpenAISuggestionsAIClient : ISuggestionsAIClient { private const string SystemPrompt = @" You suggest possible follow-up actions and questions to a given input message. @@ -41,18 +41,27 @@ What are some typical foods in Florence?|Reply| "; private readonly OpenAIClient _openAIClient; - private readonly AzureOpenAIOptions _options; - private readonly ILogger _logger; + private readonly OpenAIOptions _options; + private readonly ILogger _logger; - public AzureOpenAISuggestionsAIClient(AzureOpenAIOptions options, ILogger logger) + public OpenAISuggestionsAIClient(OpenAIOptions options, ILogger logger) { _options = options; + _logger = logger; - Uri endpoint = new(_options.Endpoint); - AzureKeyCredential token = new(_options.Key); + if (string.IsNullOrWhiteSpace(_options.Endpoint)) + { + // Use a non-Azure OpenAI endpoint. + _openAIClient = new(_options.Key); + } + else + { + // Use the specified Azure OpenAI Service endpoint. + Uri endpoint = new(_options.Endpoint); + AzureKeyCredential token = new(_options.Key); - _openAIClient = new(endpoint, token); - _logger = logger; + _openAIClient = new(endpoint, token); + } } /// diff --git a/src/VirgilAgent.SuggestionsService/appsettings.json b/src/VirgilAgent.SuggestionsService/appsettings.json index 193aeff..5162b75 100644 --- a/src/VirgilAgent.SuggestionsService/appsettings.json +++ b/src/VirgilAgent.SuggestionsService/appsettings.json @@ -6,7 +6,7 @@ } }, "AllowedHosts": "*", - "AzureOpenAI": { + "OpenAI": { "Endpoint": "YOUR_ENDPOINT_HERE", "Key": "YOUR_KEY_HERE", "DeploymentName": "gpt-35-turbo",