From 96a489acd7b7ad5ad106746d8cad80339efc0297 Mon Sep 17 00:00:00 2001 From: Pankaj Bhojwani Date: Fri, 21 Jun 2024 15:32:47 -0700 Subject: [PATCH] move more things to ai info --- src/cascadia/TerminalApp/TerminalPage.cpp | 13 +- .../AISettingsViewModel.cpp | 27 ++- .../AISettingsViewModel.h | 3 +- .../AISettingsViewModel.idl | 3 - .../TerminalSettingsModel/AIConfig.cpp | 157 ++++++++++++++++++ src/cascadia/TerminalSettingsModel/AIConfig.h | 19 ++- .../TerminalSettingsModel/AIConfig.idl | 4 + .../CascadiaSettings.cpp | 129 -------------- .../TerminalSettingsModel/CascadiaSettings.h | 8 - .../CascadiaSettings.idl | 4 - 10 files changed, 204 insertions(+), 163 deletions(-) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 49612c9f58c..7d2b120a076 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -5275,12 +5275,15 @@ namespace winrt::TerminalApp::implementation } winrt::Microsoft::Terminal::Query::Extension::ILLMProvider llmProvider{ nullptr }; - // since we only support one type of llmProvider for now, just instantiate that one (the AzureLLMProvider) - // in the future, we would need to query the settings here for which LLMProvider to use - if (!_settings.AIEndpoint().empty() && !_settings.AIKey().empty()) + const auto settingsAIInfo = _settings.GlobalSettings().AIInfo(); + // create the correct llm provider + if (settingsAIInfo.ActiveProvider() == LLMProvider::OpenAI) { - //llmProvider = winrt::Microsoft::Terminal::Query::Extension::AzureLLMProvider(_settings.AIEndpoint(), _settings.AIKey()); - llmProvider = winrt::Microsoft::Terminal::Query::Extension::OpenAILLMProvider(_settings.OpenAIKey()); + llmProvider = winrt::Microsoft::Terminal::Query::Extension::OpenAILLMProvider(settingsAIInfo.OpenAIKey()); + } + else if (settingsAIInfo.ActiveProvider() == LLMProvider::AzureOpenAI) + { + llmProvider = winrt::Microsoft::Terminal::Query::Extension::AzureLLMProvider(settingsAIInfo.AzureOpenAIEndpoint(), settingsAIInfo.AzureOpenAIKey()); } _extensionPalette = winrt::Microsoft::Terminal::Query::Extension::ExtensionPalette(llmProvider); _extensionPalette.RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [&](auto&&, auto&&) { diff --git a/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.cpp b/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.cpp index 578fbf9e0f9..f3fe32f305d 100644 --- a/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.cpp +++ b/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.cpp @@ -21,49 +21,58 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation AISettingsViewModel::AISettingsViewModel(Model::CascadiaSettings settings) : _Settings{ settings } { - INITIALIZE_BINDABLE_ENUM_SETTING(ActiveProvider, LLMProvider, Model::LLMProvider, L"Globals_LLMProvider", L"Content"); } bool AISettingsViewModel::AreAzureOpenAIKeyAndEndpointSet() { - return !_Settings.AIKey().empty() && !_Settings.AIEndpoint().empty(); + return !_Settings.GlobalSettings().AIInfo().AzureOpenAIKey().empty() && !_Settings.GlobalSettings().AIInfo().AzureOpenAIEndpoint().empty(); } winrt::hstring AISettingsViewModel::AzureOpenAIEndpoint() { - return _Settings.AIEndpoint(); + return _Settings.GlobalSettings().AIInfo().AzureOpenAIEndpoint(); } void AISettingsViewModel::AzureOpenAIEndpoint(winrt::hstring endpoint) { - _Settings.AIEndpoint(endpoint); + _Settings.GlobalSettings().AIInfo().AzureOpenAIEndpoint(endpoint); _NotifyChanges(L"AreAzureOpenAIKeyAndEndpointSet"); } winrt::hstring AISettingsViewModel::AzureOpenAIKey() { - return _Settings.AIKey(); + return _Settings.GlobalSettings().AIInfo().AzureOpenAIKey(); } void AISettingsViewModel::AzureOpenAIKey(winrt::hstring key) { - _Settings.AIKey(key); + _Settings.GlobalSettings().AIInfo().AzureOpenAIKey(key); _NotifyChanges(L"AreAzureOpenAIKeyAndEndpointSet"); } bool AISettingsViewModel::IsOpenAIKeySet() { - return !_Settings.OpenAIKey().empty(); + return !_Settings.GlobalSettings().AIInfo().OpenAIKey().empty(); } winrt::hstring AISettingsViewModel::OpenAIKey() { - return _Settings.OpenAIKey(); + return _Settings.GlobalSettings().AIInfo().OpenAIKey(); } void AISettingsViewModel::OpenAIKey(winrt::hstring key) { - _Settings.OpenAIKey(key); + _Settings.GlobalSettings().AIInfo().OpenAIKey(key); _NotifyChanges(L"IsOpenAIKeySet"); } + + bool AISettingsViewModel::AzureOpenAIIsActive() + { + return _Settings.GlobalSettings().AIInfo().ActiveProvider() == Model::LLMProvider::AzureOpenAI; + } + + bool AISettingsViewModel::OpenAIIsActive() + { + return _Settings.GlobalSettings().AIInfo().ActiveProvider() == Model::LLMProvider::OpenAI; + } } diff --git a/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.h b/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.h index c62e874817f..2551a9ac490 100644 --- a/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.h @@ -27,7 +27,8 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation winrt::hstring OpenAIKey(); void OpenAIKey(winrt::hstring key); - GETSET_BINDABLE_ENUM_SETTING(ActiveProvider, Model::LLMProvider, _Settings.GlobalSettings().AIInfo().ActiveProvider); + bool AzureOpenAIIsActive(); + bool OpenAIIsActive(); private: Model::CascadiaSettings _Settings; diff --git a/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.idl b/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.idl index e8de6a185a4..6f359eed030 100644 --- a/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/AISettingsViewModel.idl @@ -17,8 +17,5 @@ namespace Microsoft.Terminal.Settings.Editor Boolean IsOpenAIKeySet { get; }; String OpenAIKey; - - IInspectable CurrentActiveProvider; - Windows.Foundation.Collections.IObservableVector ActiveProviderList { get; }; } } diff --git a/src/cascadia/TerminalSettingsModel/AIConfig.cpp b/src/cascadia/TerminalSettingsModel/AIConfig.cpp index a465b643c85..df94413ff29 100644 --- a/src/cascadia/TerminalSettingsModel/AIConfig.cpp +++ b/src/cascadia/TerminalSettingsModel/AIConfig.cpp @@ -10,8 +10,13 @@ using namespace Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; +using namespace winrt::Windows::Security::Credentials; static constexpr std::string_view AIConfigKey{ "aiConfig" }; +static constexpr std::wstring_view PasswordVaultResourceName = L"TerminalAI"; +static constexpr std::wstring_view PasswordVaultAIKey = L"TerminalAIKey"; +static constexpr std::wstring_view PasswordVaultAIEndpoint = L"TerminalAIEndpoint"; +static constexpr std::wstring_view PasswordVaultOpenAIKey = L"TerminalOpenAIKey"; winrt::com_ptr AIConfig::CopyAIConfig(const AIConfig* source) { @@ -46,3 +51,155 @@ void AIConfig::LayerJson(const Json::Value& json) MTSM_AI_SETTINGS(AI_SETTINGS_LAYER_JSON) #undef AI_SETTINGS_LAYER_JSON } + +winrt::hstring AIConfig::AzureOpenAIEndpoint() noexcept +{ + PasswordVault vault; + PasswordCredential cred; + // Retrieve throws an exception if there are no credentials stored under the given resource so we wrap it in a try-catch block + try + { + cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultAIEndpoint); + } + catch (...) + { + return L""; + } + return cred.Password(); +} + +void AIConfig::AzureOpenAIEndpoint(const winrt::hstring& endpoint) noexcept +{ + PasswordVault vault; + if (endpoint.empty()) + { + // an empty string indicates that we should clear the key + PasswordCredential cred; + try + { + cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultAIEndpoint); + } + catch (...) + { + // there was nothing to remove, just return + return; + } + vault.Remove(cred); + } + else + { + PasswordCredential newCredential{ PasswordVaultResourceName, PasswordVaultAIEndpoint, endpoint }; + vault.Add(newCredential); + } +} + +winrt::hstring AIConfig::AzureOpenAIKey() noexcept +{ + PasswordVault vault; + PasswordCredential cred; + // Retrieve throws an exception if there are no credentials stored under the given resource so we wrap it in a try-catch block + try + { + cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultAIKey); + } + catch (...) + { + return L""; + } + return cred.Password(); +} + +void AIConfig::AzureOpenAIKey(const winrt::hstring& key) noexcept +{ + PasswordVault vault; + if (key.empty()) + { + // the user has entered an empty string, that indicates that we should clear the key + PasswordCredential cred; + try + { + cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultAIKey); + } + catch (...) + { + // there was nothing to remove, just return + return; + } + vault.Remove(cred); + } + else + { + PasswordCredential newCredential{ PasswordVaultResourceName, PasswordVaultAIKey, key }; + vault.Add(newCredential); + } +} + +winrt::hstring AIConfig::OpenAIKey() noexcept +{ + PasswordVault vault; + PasswordCredential cred; + // Retrieve throws an exception if there are no credentials stored under the given resource so we wrap it in a try-catch block + try + { + cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultOpenAIKey); + } + catch (...) + { + return L""; + } + return cred.Password(); +} + +void AIConfig::OpenAIKey(const winrt::hstring& key) noexcept +{ + PasswordVault vault; + if (key.empty()) + { + // the user has entered an empty string, that indicates that we should clear the key + PasswordCredential cred; + try + { + cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultOpenAIKey); + } + catch (...) + { + // there was nothing to remove, just return + return; + } + vault.Remove(cred); + } + else + { + PasswordCredential newCredential{ PasswordVaultResourceName, PasswordVaultOpenAIKey, key }; + vault.Add(newCredential); + } +} + +winrt::Microsoft::Terminal::Settings::Model::LLMProvider AIConfig::ActiveProvider() +{ + const auto val{ _getActiveProviderImpl() }; + if (val) + { + // an active provider was explicitly set, return that + return *val; + } + else if (!AzureOpenAIEndpoint().empty() && !AzureOpenAIKey().empty()) + { + // no explicitly set provider but we have an azure open ai key and endpoint, use that + return LLMProvider::AzureOpenAI; + } + else if (!OpenAIKey().empty()) + { + // no explicitly set provider but we have an open ai key, use that + return LLMProvider::OpenAI; + } + else + { + return LLMProvider{}; + } +} + +void AIConfig::ActiveProvider(const LLMProvider& provider) +{ + _ActiveProvider = provider; +} diff --git a/src/cascadia/TerminalSettingsModel/AIConfig.h b/src/cascadia/TerminalSettingsModel/AIConfig.h index 5cba6236932..ca033177a91 100644 --- a/src/cascadia/TerminalSettingsModel/AIConfig.h +++ b/src/cascadia/TerminalSettingsModel/AIConfig.h @@ -33,9 +33,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Json::Value ToJson() const; void LayerJson(const Json::Value& json); -#define AI_SETTINGS_INITIALIZE(type, name, jsonKey, ...) \ - INHERITABLE_SETTING(Model::AIConfig, type, name, ##__VA_ARGS__) - MTSM_AI_SETTINGS(AI_SETTINGS_INITIALIZE) -#undef AI_SETTINGS_INITIALIZE + // Key and endpoint storage + // These are not written to the json, they are stored in the Windows Security Storage Vault + winrt::hstring AzureOpenAIEndpoint() noexcept; + void AzureOpenAIEndpoint(const winrt::hstring& endpoint) noexcept; + winrt::hstring AzureOpenAIKey() noexcept; + void AzureOpenAIKey(const winrt::hstring& key) noexcept; + winrt::hstring OpenAIKey() noexcept; + void OpenAIKey(const winrt::hstring& key) noexcept; + + // we cannot just use INHERITABLE_SETTING here because we try to be smart about what the ActiveProvider is + // i.e. even if there's no ActiveProvider explicitly set, if there's only the key stored for one of the providers + // then that is the active one + LLMProvider ActiveProvider(); + void ActiveProvider(const LLMProvider& provider); + _BASE_INHERITABLE_SETTING(Model::AIConfig, std::optional, ActiveProvider); }; } diff --git a/src/cascadia/TerminalSettingsModel/AIConfig.idl b/src/cascadia/TerminalSettingsModel/AIConfig.idl index 4ee7563d7f9..fff8b71f5d3 100644 --- a/src/cascadia/TerminalSettingsModel/AIConfig.idl +++ b/src/cascadia/TerminalSettingsModel/AIConfig.idl @@ -13,5 +13,9 @@ namespace Microsoft.Terminal.Settings.Model [default_interface] runtimeclass AIConfig { INHERITABLE_SETTING(LLMProvider, ActiveProvider); + + String AzureOpenAIEndpoint; + String AzureOpenAIKey; + String OpenAIKey; } } diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp index 7b83d2a2089..e22905d3652 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp @@ -21,14 +21,8 @@ using namespace winrt::Microsoft::Terminal::Settings; using namespace winrt::Microsoft::Terminal::Settings::Model::implementation; using namespace winrt::Microsoft::Terminal::Control; using namespace winrt::Windows::Foundation::Collections; -using namespace winrt::Windows::Security::Credentials; using namespace Microsoft::Console; -static constexpr std::wstring_view PasswordVaultResourceName = L"TerminalAI"; -static constexpr std::wstring_view PasswordVaultAIKey = L"TerminalAIKey"; -static constexpr std::wstring_view PasswordVaultAIEndpoint = L"TerminalAIEndpoint"; -static constexpr std::wstring_view PasswordVaultOpenAIKey = L"TerminalOpenAIKey"; - // Creating a child of a profile requires us to copy certain // required attributes. This method handles those attributes. // @@ -1062,129 +1056,6 @@ void CascadiaSettings::CurrentDefaultTerminal(const Model::DefaultTerminal& term _currentDefaultTerminal = terminal; } -winrt::hstring CascadiaSettings::AIEndpoint() noexcept -{ - PasswordVault vault; - PasswordCredential cred; - // Retrieve throws an exception if there are no credentials stored under the given resource so we wrap it in a try-catch block - try - { - cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultAIEndpoint); - } - catch (...) - { - return L""; - } - return cred.Password(); -} - -void CascadiaSettings::AIEndpoint(const winrt::hstring& endpoint) noexcept -{ - PasswordVault vault; - if (endpoint.empty()) - { - // an empty string indicates that we should clear the key - PasswordCredential cred; - try - { - cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultAIEndpoint); - } - catch (...) - { - // there was nothing to remove, just return - return; - } - vault.Remove(cred); - } - else - { - PasswordCredential newCredential{ PasswordVaultResourceName, PasswordVaultAIEndpoint, endpoint }; - vault.Add(newCredential); - } -} - -winrt::hstring CascadiaSettings::AIKey() noexcept -{ - PasswordVault vault; - PasswordCredential cred; - // Retrieve throws an exception if there are no credentials stored under the given resource so we wrap it in a try-catch block - try - { - cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultAIKey); - } - catch (...) - { - return L""; - } - return cred.Password(); -} - -void CascadiaSettings::AIKey(const winrt::hstring& key) noexcept -{ - PasswordVault vault; - if (key.empty()) - { - // the user has entered an empty string, that indicates that we should clear the key - PasswordCredential cred; - try - { - cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultAIKey); - } - catch (...) - { - // there was nothing to remove, just return - return; - } - vault.Remove(cred); - } - else - { - PasswordCredential newCredential{ PasswordVaultResourceName, PasswordVaultAIKey, key }; - vault.Add(newCredential); - } -} - -winrt::hstring CascadiaSettings::OpenAIKey() noexcept -{ - PasswordVault vault; - PasswordCredential cred; - // Retrieve throws an exception if there are no credentials stored under the given resource so we wrap it in a try-catch block - try - { - cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultOpenAIKey); - } - catch (...) - { - return L""; - } - return cred.Password(); -} - -void CascadiaSettings::OpenAIKey(const winrt::hstring& key) noexcept -{ - PasswordVault vault; - if (key.empty()) - { - // the user has entered an empty string, that indicates that we should clear the key - PasswordCredential cred; - try - { - cred = vault.Retrieve(PasswordVaultResourceName, PasswordVaultOpenAIKey); - } - catch (...) - { - // there was nothing to remove, just return - return; - } - vault.Remove(cred); - } - else - { - PasswordCredential newCredential{ PasswordVaultResourceName, PasswordVaultOpenAIKey, key }; - vault.Add(newCredential); - } -} - // This function is implicitly called by DefaultTerminals/CurrentDefaultTerminal(). // It reloads the selection of available, installed terminals and caches them. // WinUI requires us that the `SelectedItem` of a collection is member of the list given to `ItemsSource`. diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h index e9cf3518ea7..03553c8b73d 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.h +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.h @@ -149,14 +149,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation Model::DefaultTerminal CurrentDefaultTerminal() noexcept; void CurrentDefaultTerminal(const Model::DefaultTerminal& terminal); - // AI Settings - winrt::hstring AIEndpoint() noexcept; - void AIEndpoint(const winrt::hstring& endpoint) noexcept; - winrt::hstring AIKey() noexcept; - void AIKey(const winrt::hstring& key) noexcept; - winrt::hstring OpenAIKey() noexcept; - void OpenAIKey(const winrt::hstring& key) noexcept; - void ExpandCommands(); private: diff --git a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl index c69ace59339..165a7fed9fa 100644 --- a/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl +++ b/src/cascadia/TerminalSettingsModel/CascadiaSettings.idl @@ -55,10 +55,6 @@ namespace Microsoft.Terminal.Settings.Model IObservableVector DefaultTerminals { get; }; DefaultTerminal CurrentDefaultTerminal; - String AIEndpoint; - String AIKey; - String OpenAIKey; - void ExpandCommands(); } }