-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow OpenAI to be used with Terminal Chat #17540
base: feature/llm
Are you sure you want to change the base?
Conversation
@@ -56,6 +56,9 @@ | |||
<ClInclude Include="AzureLLMProvider.h"> | |||
<DependentUpon>AzureLLMProvider.idl</DependentUpon> | |||
</ClInclude> | |||
<ClInclude Include="OpenAILLMProvider.h"> |
Check failure
Code scanning / check-spelling
Unrecognized Spelling
_NotifyChanges(L"IsOpenAIKeySet"); | ||
} | ||
|
||
bool AISettingsViewModel::AzureOpenAIIsActive() |
Check failure
Code scanning / check-spelling
Unrecognized Spelling
@@ -0,0 +1,129 @@ | |||
// Copyright (c) Microsoft Corporation. |
Check failure
Code scanning / check-spelling
Check File Path
@@ -0,0 +1,48 @@ | |||
// Copyright (c) Microsoft Corporation. |
Check failure
Code scanning / check-spelling
Check File Path
@@ -0,0 +1,17 @@ | |||
// Copyright (c) Microsoft Corporation. |
Check failure
Code scanning / check-spelling
Check File Path
@@ -96,6 +102,9 @@ | |||
<Midl Include="AzureLLMProvider.idl"> | |||
<SubType>Code</SubType> | |||
</Midl> | |||
<Midl Include="OpenAILLMProvider.idl"> |
Check failure
Code scanning / check-spelling
Unrecognized Spelling
</Button> | ||
<Button Click="SetAzureOpenAIActive_Click" | ||
HorizontalAlignment="Center" | ||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(ViewModel.AzureOpenAIIsActive), Mode=OneWay}"> |
Check failure
Code scanning / check-spelling
Unrecognized Spelling
</Button> | ||
<Button Click="SetOpenAIActive_Click" | ||
HorizontalAlignment="Center" | ||
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(ViewModel.OpenAIIsActive), Mode=OneWay}"> |
Check failure
Code scanning / check-spelling
Unrecognized Spelling
void AISettingsViewModel::SetAzureOpenAIActive() | ||
{ | ||
_Settings.GlobalSettings().AIInfo().ActiveProvider(Model::LLMProvider::AzureOpenAI); | ||
_NotifyChanges(L"AzureOpenAIIsActive"); |
Check failure
Code scanning / check-spelling
Unrecognized Spelling
{ | ||
_Settings.GlobalSettings().AIInfo().ActiveProvider(Model::LLMProvider::AzureOpenAI); | ||
_NotifyChanges(L"AzureOpenAIIsActive"); | ||
_NotifyChanges(L"OpenAIIsActive"); |
Check failure
Code scanning / check-spelling
Unrecognized Spelling
This comment has been minimized.
This comment has been minimized.
const auto val{ _getActiveProviderImpl() }; | ||
if (val) | ||
{ | ||
// an active provider was explicitly set, return that | ||
return *val; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does mean that you can set an active provider even if you have not configured that provider - in this case when you try to use the Chat the provider will return the appropriate error. For example, the OpenAI provider will still make the http request and we get an error message back that no api key was provided.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…m/microsoft/terminal into dev/pabhoj/featurellm_openai
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Nice work!
<RichTextBlock TextWrapping="WrapWholeWords" | ||
MaxWidth="900"> | ||
<Paragraph> | ||
<Run x:Name="AISettings_OpenAIDescriptionPart1" /><Hyperlink NavigateUri="https://go.microsoft.com/fwlink/?linkid=2251839" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the OpenAI case, would we want to / need to link out to their terms?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe @nguyen-dows said he'll hunt down the ToU once we get closer to release!
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…nto dev/pabhoj/llm_provider_interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Biggest concern: The strong-this issue. Otherwise, it seems fine - just some smaller issues.
|
||
void OpenAILLMProvider::SetContext(const Extension::IContext context) | ||
{ | ||
_context = context; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is where we'd ideally take the argument by-copy and then move it into place. This lets the caller move the instance into the argument which you then move into the member.
(A very minor nit.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh and: Moving only works if the argument is not const. If it's const it'll not fail to compile, because... C++ reasons. Instead, it'll silently just create a copy. Bu With high enough compiler warnings it'll warn you about it though, but idk if it's enabled for this project.
hstring message{}; | ||
|
||
// Make sure we are on the background thread for the http request | ||
co_await winrt::resume_background(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also needs the strong-this pointer for safety. You can use the weak/strong handling that we do elsewhere or hold onto a strong pointer the entire time. That's pretty much entirely up to you (the latter is probably better though).
// Send the request | ||
try | ||
{ | ||
const auto response = _httpClient.SendRequestAsync(request).get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
co_await?
@@ -129,6 +129,13 @@ namespace winrt::TerminalApp::implementation | |||
p.SetActionMap(_settings.ActionMap()); | |||
} | |||
|
|||
// If the active LLMProvider changed, make sure we reinitialize the provider | |||
const auto newProviderType = _settings.GlobalSettings().AIInfo().ActiveProvider(); | |||
if (_lmProvider && (newProviderType != _currentProvider)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this depend on the existence of _lmProvider
?
if (!OpenAIKeyInputBox().Password().empty()) | ||
{ | ||
_ViewModel.OpenAIKey(OpenAIKeyInputBox().Password()); | ||
OpenAIKeyInputBox().Password(L""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Avoid calling getters repeatedly and instead assign them to variables. Aids in debugging and avoids unknown side-effects if the getter is non-trivial (e.g. computes the return value, or worse, fetches it from a database, etc.; the PasswordVault
getter is a good example).
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, credential); | ||
} | ||
catch (...) | ||
{ | ||
return L""; | ||
} | ||
return cred.Password(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems expensive. Why not cache it on first load? Should the error be logged to aid in debugging?
Summary of the Pull Request
OpenAILLMProvider
, which is an implementation ofILMProvider
that uses OpenAIAIConfig
on the settings model side, to allow the user to specify which AI provider to use as their current active one (in the case that they have configured more than one LMProvider)Note to reviewers:
The OpenAI implementation is largely the same as the Azure OpenAI one. The more "new" change in this PR is the
AIConfig
struct on the settings model side that allows the user to specify which provider is the active one, as well as the logic inTerminalPage
for how we update the current active provider based on settings changesValidation Steps Performed
PR Checklist