Skip to content

Commit

Permalink
move more things to ai info
Browse files Browse the repository at this point in the history
  • Loading branch information
PankajBhojwani committed Jul 4, 2024
1 parent 1934b30 commit 96a489a
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 163 deletions.
13 changes: 8 additions & 5 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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&&) {
Expand Down
27 changes: 18 additions & 9 deletions src/cascadia/TerminalSettingsEditor/AISettingsViewModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
3 changes: 2 additions & 1 deletion src/cascadia/TerminalSettingsEditor/AISettingsViewModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 0 additions & 3 deletions src/cascadia/TerminalSettingsEditor/AISettingsViewModel.idl
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,5 @@ namespace Microsoft.Terminal.Settings.Editor

Boolean IsOpenAIKeySet { get; };
String OpenAIKey;

IInspectable CurrentActiveProvider;
Windows.Foundation.Collections.IObservableVector<Microsoft.Terminal.Settings.Editor.EnumEntry> ActiveProviderList { get; };
}
}
157 changes: 157 additions & 0 deletions src/cascadia/TerminalSettingsModel/AIConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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> AIConfig::CopyAIConfig(const AIConfig* source)
{
Expand Down Expand Up @@ -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;
}
19 changes: 15 additions & 4 deletions src/cascadia/TerminalSettingsModel/AIConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<LLMProvider>, ActiveProvider);
};
}
4 changes: 4 additions & 0 deletions src/cascadia/TerminalSettingsModel/AIConfig.idl
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@ namespace Microsoft.Terminal.Settings.Model

[default_interface] runtimeclass AIConfig {
INHERITABLE_SETTING(LLMProvider, ActiveProvider);

String AzureOpenAIEndpoint;
String AzureOpenAIKey;
String OpenAIKey;
}
}
Loading

1 comment on commit 96a489a

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@check-spelling-bot Report

🔴 Please review

See the 📜action log or 📝 job summary for details.

Unrecognized words (2)

AIIs
AILLM

Previously acknowledged words that are now absent CRLFs Redir wcsicmp 🫥
To accept these unrecognized words as correct and remove the previously acknowledged and now absent words, you could run the following commands

... in a clone of the [email protected]:microsoft/terminal.git repository
on the dev/pabhoj/featurellm_openai branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.22/apply.pl' |
perl - 'https://github.com/microsoft/terminal/actions/runs/9787070606/attempts/1'
Available 📚 dictionaries could cover words (expected and unrecognized) not in the 📘 dictionary

This includes both expected items (2213) from .github/actions/spelling/expect/04cdb9b77d6827c0202f51acd4205b017015bfff.txt
.github/actions/spelling/expect/alphabet.txt
.github/actions/spelling/expect/expect.txt
.github/actions/spelling/expect/web.txt and unrecognized words (2)

Dictionary Entries Covers Uniquely
cspell:cpp/src/lang-jargon.txt 11 1 1
cspell:swift/src/swift.txt 53 1 1
cspell:gaming-terms/dict/gaming-terms.txt 59 1 1
cspell:monkeyc/src/monkeyc_keywords.txt 123 1 1
cspell:cryptocurrencies/cryptocurrencies.txt 125 1 1

Consider adding them (in .github/workflows/spelling2.yml) for uses: check-spelling/[email protected] in its with:

      with:
        extra_dictionaries:
          cspell:cpp/src/lang-jargon.txt
          cspell:swift/src/swift.txt
          cspell:gaming-terms/dict/gaming-terms.txt
          cspell:monkeyc/src/monkeyc_keywords.txt
          cspell:cryptocurrencies/cryptocurrencies.txt

To stop checking additional dictionaries, add (in .github/workflows/spelling2.yml) for uses: check-spelling/[email protected] in its with:

check_extra_dictionaries: ''
Errors (2)

See the 📜action log or 📝 job summary for details.

❌ Errors Count
❌ check-file-path 3
❌ ignored-expect-variant 3

See ❌ Event descriptions for more information.

✏️ Contributor please read this

By default the command suggestion will generate a file named based on your commit. That's generally ok as long as you add the file to your commit. Someone can reorganize it later.

If the listed items are:

  • ... misspelled, then please correct them instead of using the command.
  • ... names, please add them to .github/actions/spelling/allow/names.txt.
  • ... APIs, you can add them to a file in .github/actions/spelling/allow/.
  • ... just things you're using, please add them to an appropriate file in .github/actions/spelling/expect/.
  • ... tokens you only need in one place and shouldn't generally be used, you can add an item in an appropriate file in .github/actions/spelling/patterns/.

See the README.md in each directory for more information.

🔬 You can test your commits without appending to a PR by creating a new branch with that extra change and pushing it to your fork. The check-spelling action will run in response to your push -- it doesn't require an open pull request. By using such a branch, you can limit the number of typos your peers see you make. 😉

If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Please sign in to comment.