From b6ecccc4626cd6a3a1fd7e77bec1972f15a28cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20=C5=A0olt=C3=A9s?= Date: Fri, 28 Apr 2023 11:24:45 +0200 Subject: [PATCH] Add option to override the OpenAI api url --- Directory.Build.props | 2 +- README.md | 9 ++++--- src/ChatGPT.CLI/Chat.cs | 1 + src/ChatGPT.CLI/CliSettings.cs | 1 + src/ChatGPT.CLI/Program.cs | 10 ++++++- .../ViewModels/Chat/ChatSettingsViewModel.cs | 10 +++++++ .../ViewModels/Chat/ChatViewModel.cs | 1 + .../Views/Chat/ChatSettingsView.axaml | 21 +++++++++++++++ src/ChatGPT/Constants.cs | 4 +++ .../Model/Services/ChatServiceSettings.cs | 1 + .../Services/CompletionsServiceSettings.cs | 1 + src/ChatGPT/Model/Services/IChatService.cs | 1 - .../Model/Services/ICompletionsService.cs | 1 - src/ChatGPT/Services/ChatService.cs | 26 ++++++++++++++----- src/ChatGPT/Services/CompletionsService.cs | 26 ++++++++++++++----- 15 files changed, 93 insertions(+), 22 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 249f25f5..6b74ddf2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@  1.0.0 - preview.17 + preview.18 Wiesław Šoltés Wiesław Šoltés Copyright © Wiesław Šoltés 2023 diff --git a/README.md b/README.md index ce9012b9..e4f9c117 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,8 @@ You can try client using browser version [here](https://wieslawsoltes.github.io/ # Overriding OpenAI api url -```C# -var chat = Defaults.Locator.GetService(); -chat.SetApiUrl("you api url"); +```bash +To override the OpenAI api url set `OPENAI_API_URL_CHAT_COMPLETIONS` environment variable or set API url directly in app settings. ``` # OpenAI ChatGPT web version import @@ -95,6 +94,7 @@ Options: --frequencyPenalty Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. --maxTokens The maximum number of tokens to generate in the chat completion. --apiKey Override OpenAI api key. By default OPENAI_API_KEY environment variable is used. +--apiUrl Override OpenAI api url. By default OPENAI_API_URL_CHAT_COMPLETIONS environment variable is used. --model ID of the model to use. See the model endpoint compatibility table for details on which models work with the Chat API. --directions The system message (directions) helps set the behavior of the assistant. Typically, a conversation is formatted with a system message first, followed by alternating user and assistant messages. -t, --threads The number of parallel job threads @@ -155,7 +155,8 @@ dotnet run -- -d ./ -e md -p *.cs --directions "You are a technical documentatio "maxTokens": 2000, "apiKey": "", "model": "gpt-3.5-turbo", - "directions": "You are a helpful assistant." + "directions": "You are a helpful assistant.", + "apiUrl": "" } ``` diff --git a/src/ChatGPT.CLI/Chat.cs b/src/ChatGPT.CLI/Chat.cs index e5097138..50fdcea3 100644 --- a/src/ChatGPT.CLI/Chat.cs +++ b/src/ChatGPT.CLI/Chat.cs @@ -142,6 +142,7 @@ private static List GetJobs(CliSettings cliSettings, List pat ApiKey = cliSettings.ApiKey, Model = cliSettings.Model, Directions = cliSettings.Directions, + ApiUrl = cliSettings.ApiUrl, }; for (var i = 0; i < paths.Count; i++) diff --git a/src/ChatGPT.CLI/CliSettings.cs b/src/ChatGPT.CLI/CliSettings.cs index 391e7bae..3cb6032d 100644 --- a/src/ChatGPT.CLI/CliSettings.cs +++ b/src/ChatGPT.CLI/CliSettings.cs @@ -20,6 +20,7 @@ internal class CliSettings public string? ApiKey { get; set; } = null; public string? Model { get; set; } = "gpt-3.5-turbo"; public string? Directions { get; set; } = "You are a helpful assistant."; + public string? ApiUrl { get; set; } = null; // Job settings public int Threads { get; set; } = 1; public bool Quiet { get; set; } = false; diff --git a/src/ChatGPT.CLI/Program.cs b/src/ChatGPT.CLI/Program.cs index 0ee57952..14499f5f 100644 --- a/src/ChatGPT.CLI/Program.cs +++ b/src/ChatGPT.CLI/Program.cs @@ -106,7 +106,14 @@ RootCommand CreateRootCommand() { Argument = new Argument(getDefaultValue: () => null) }; - + + var optionApiUrl = new Option( + new[] { "--apiUrl" }, + "Override OpenAI api url. By default OPENAI_API_URL_CHAT_COMPLETIONS environment variable is used.") + { + Argument = new Argument(getDefaultValue: () => null) + }; + var optionModel = new Option( new[] { "--model" }, "ID of the model to use. See the model endpoint compatibility table for details on which models work with the Chat API.") @@ -150,6 +157,7 @@ RootCommand CreateRootCommand() rootCommand.AddOption(optionFrequencyPenalty); rootCommand.AddOption(optionMaxTokens); rootCommand.AddOption(optionApiKey); + rootCommand.AddOption(optionApiUrl); rootCommand.AddOption(optionModel); rootCommand.AddOption(optionDirections); diff --git a/src/ChatGPT.Core/ViewModels/Chat/ChatSettingsViewModel.cs b/src/ChatGPT.Core/ViewModels/Chat/ChatSettingsViewModel.cs index d5401440..31b9e36f 100644 --- a/src/ChatGPT.Core/ViewModels/Chat/ChatSettingsViewModel.cs +++ b/src/ChatGPT.Core/ViewModels/Chat/ChatSettingsViewModel.cs @@ -14,6 +14,7 @@ public partial class ChatSettingsViewModel : ObservableObject private string? _model; private string? _directions; private string? _format; + private string? _apiUrl; [JsonConstructor] public ChatSettingsViewModel() @@ -26,6 +27,7 @@ public ChatSettingsViewModel() _apiKey = null; _model = "gpt-3.5-turbo"; _directions = "You are a helpful assistant."; + _apiUrl = null; } [JsonPropertyName("temperature")] @@ -91,6 +93,13 @@ public string? Format set => SetProperty(ref _format, value); } + [JsonPropertyName("apiUrl")] + public string? ApiUrl + { + get => _apiUrl; + set => SetProperty(ref _apiUrl, value); + } + public ChatSettingsViewModel Copy() { return new ChatSettingsViewModel @@ -104,6 +113,7 @@ public ChatSettingsViewModel Copy() Model = _model, Directions = _directions, Format = _format, + ApiUrl = _apiUrl, }; } } diff --git a/src/ChatGPT.Core/ViewModels/Chat/ChatViewModel.cs b/src/ChatGPT.Core/ViewModels/Chat/ChatViewModel.cs index 093caf69..b71cb3c0 100644 --- a/src/ChatGPT.Core/ViewModels/Chat/ChatViewModel.cs +++ b/src/ChatGPT.Core/ViewModels/Chat/ChatViewModel.cs @@ -401,6 +401,7 @@ public ChatMessage[] CreateChatMessages() MaxTokens = Settings.MaxTokens, TopP = 1.0m, Stop = null, + ApiUrl = Settings.ApiUrl, }; var result = new ChatResultViewModel diff --git a/src/ChatGPT.UI/Views/Chat/ChatSettingsView.axaml b/src/ChatGPT.UI/Views/Chat/ChatSettingsView.axaml index 0ecc2264..58cc2695 100644 --- a/src/ChatGPT.UI/Views/Chat/ChatSettingsView.axaml +++ b/src/ChatGPT.UI/Views/Chat/ChatSettingsView.axaml @@ -152,6 +152,27 @@ ToolTip.Tip="Override OpenAI api key. By default OPENAI_API_KEY environment variable is used." /> + + + + + + + https://api.openai.com/v1/chat/completions + + + + + diff --git a/src/ChatGPT/Constants.cs b/src/ChatGPT/Constants.cs index d2c6965b..352fab2d 100644 --- a/src/ChatGPT/Constants.cs +++ b/src/ChatGPT/Constants.cs @@ -5,4 +5,8 @@ public static class Constants public const string EnvironmentVariableApiKey = "OPENAI_API_KEY"; public const string EnvironmentVariableApiModel = "OPENAI_API_MODEL"; + + public const string EnvironmentVariableApiUrlCompletions = "OPENAI_API_URL_COMPLETIONS"; + + public const string EnvironmentVariableApiUrlChatCompletions = "OPENAI_API_URL_CHAT_COMPLETIONS"; } diff --git a/src/ChatGPT/Model/Services/ChatServiceSettings.cs b/src/ChatGPT/Model/Services/ChatServiceSettings.cs index 449e82f3..cb767ce3 100644 --- a/src/ChatGPT/Model/Services/ChatServiceSettings.cs +++ b/src/ChatGPT/Model/Services/ChatServiceSettings.cs @@ -4,6 +4,7 @@ namespace AI.Model.Services; public class ChatServiceSettings { + public string? ApiUrl { get; set; } public string? Model { get; set; } public ChatMessage[]? Messages { get; set; } public string? Suffix { get; set; } diff --git a/src/ChatGPT/Model/Services/CompletionsServiceSettings.cs b/src/ChatGPT/Model/Services/CompletionsServiceSettings.cs index 65b6693c..99a421bc 100644 --- a/src/ChatGPT/Model/Services/CompletionsServiceSettings.cs +++ b/src/ChatGPT/Model/Services/CompletionsServiceSettings.cs @@ -2,6 +2,7 @@ namespace AI.Model.Services; public class CompletionsServiceSettings { + public string? Url { get; set; } public string? Model { get; set; } public string? Prompt { get; set; } public string? Suffix { get; set; } diff --git a/src/ChatGPT/Model/Services/IChatService.cs b/src/ChatGPT/Model/Services/IChatService.cs index 94105f36..78425671 100644 --- a/src/ChatGPT/Model/Services/IChatService.cs +++ b/src/ChatGPT/Model/Services/IChatService.cs @@ -6,6 +6,5 @@ namespace AI.Model.Services; public interface IChatService { - void SetApiUrl(string apiUrl); Task GetResponseDataAsync(ChatServiceSettings settings, CancellationToken token); } diff --git a/src/ChatGPT/Model/Services/ICompletionsService.cs b/src/ChatGPT/Model/Services/ICompletionsService.cs index d6d25c0a..131bc391 100644 --- a/src/ChatGPT/Model/Services/ICompletionsService.cs +++ b/src/ChatGPT/Model/Services/ICompletionsService.cs @@ -6,6 +6,5 @@ namespace AI.Model.Services; public interface ICompletionsService { - void SetApiUrl(string apiUrl); Task GetResponseDataAsync(CompletionsServiceSettings settings, CancellationToken token); } diff --git a/src/ChatGPT/Services/ChatService.cs b/src/ChatGPT/Services/ChatService.cs index 06477baf..83759f6e 100644 --- a/src/ChatGPT/Services/ChatService.cs +++ b/src/ChatGPT/Services/ChatService.cs @@ -12,7 +12,7 @@ namespace AI.Services; public class ChatService : IChatService { private static readonly HttpClient s_client; - private string _apiUrl = "https://api.openai.com/v1/chat/completions"; + private static readonly string s_apiUrl = "https://api.openai.com/v1/chat/completions"; private readonly IChatSerializer _serializer; static ChatService() @@ -101,11 +101,6 @@ private string GetRequestBodyJson(ChatServiceSettings settings) return _serializer.Deserialize(responseBody); } - public void SetApiUrl(string apiUrl) - { - _apiUrl = apiUrl; - } - public async Task GetResponseDataAsync(ChatServiceSettings settings, CancellationToken token) { // Set up the API URL and API key @@ -117,8 +112,25 @@ public void SetApiUrl(string apiUrl) // Get the request body JSON var requestBodyJson = GetRequestBodyJson(settings); + + var apiUrl = s_apiUrl; + var envApiUrl = Environment.GetEnvironmentVariable(Constants.EnvironmentVariableApiUrlChatCompletions); + if (!string.IsNullOrWhiteSpace(envApiUrl)) + { + apiUrl = envApiUrl; + } + + if (!string.IsNullOrWhiteSpace(settings.ApiUrl)) + { + apiUrl = settings.ApiUrl; + } + + if (apiUrl is null) + { + return null; + } // Send the API request and get the response data - return await SendApiRequestAsync(_apiUrl, apiKey, requestBodyJson, token); + return await SendApiRequestAsync(apiUrl, apiKey, requestBodyJson, token); } } diff --git a/src/ChatGPT/Services/CompletionsService.cs b/src/ChatGPT/Services/CompletionsService.cs index 5df358d1..4d06883f 100644 --- a/src/ChatGPT/Services/CompletionsService.cs +++ b/src/ChatGPT/Services/CompletionsService.cs @@ -12,7 +12,7 @@ namespace AI.Services; public class CompletionsService : ICompletionsService { private static readonly HttpClient s_client; - private string _apiUrl = "https://api.openai.com/v1/completions"; + private static readonly string s_apiUrl = "https://api.openai.com/v1/completions"; private readonly IChatSerializer _serializer; static CompletionsService() @@ -102,11 +102,6 @@ private string GetRequestBodyJson(CompletionsServiceSettings settings) return _serializer.Deserialize(responseBody); } - public void SetApiUrl(string apiUrl) - { - _apiUrl = apiUrl; - } - public async Task GetResponseDataAsync(CompletionsServiceSettings settings, CancellationToken token) { // Set up the API URL and API key @@ -118,8 +113,25 @@ public void SetApiUrl(string apiUrl) // Get the request body JSON var requestBodyJson = GetRequestBodyJson(settings); + + var apiUrl = s_apiUrl; + var envApiUrl = Environment.GetEnvironmentVariable(Constants.EnvironmentVariableApiUrlCompletions); + if (!string.IsNullOrWhiteSpace(envApiUrl)) + { + apiUrl = envApiUrl; + } + + if (!string.IsNullOrWhiteSpace(settings.Url)) + { + apiUrl = settings.Url; + } + + if (apiUrl is null) + { + return null; + } // Send the API request and get the response data - return await SendApiRequestAsync(_apiUrl, apiKey, requestBodyJson, token); + return await SendApiRequestAsync(apiUrl, apiKey, requestBodyJson, token); } }