Skip to content
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

Open
wants to merge 38 commits into
base: feature/llm
Choose a base branch
from

Conversation

PankajBhojwani
Copy link
Contributor

@PankajBhojwani PankajBhojwani commented Jul 9, 2024

Summary of the Pull Request

  • Implements OpenAILLMProvider, which is an implementation of ILMProvider that uses OpenAI
  • Implements an AIConfig 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 in TerminalPage for how we update the current active provider based on settings changes

Validation Steps Performed

  • Able to set OpenAI as the active provider
  • OpenAI works in Terminal Chat

PR Checklist

  • Closes #xxx
  • Tests added/passed
  • Documentation updated
    • If checked, please file a pull request on our docs repo and link it here: #xxx
  • Schema updated (if necessary)

@@ -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

[AILLM](#security-tab) is not a recognized word. \(unrecognized-spelling\)
_NotifyChanges(L"IsOpenAIKeySet");
}

bool AISettingsViewModel::AzureOpenAIIsActive()

Check failure

Code scanning / check-spelling

Unrecognized Spelling

[AIIs](#security-tab) is not a recognized word. \(unrecognized-spelling\)
@@ -0,0 +1,129 @@
// Copyright (c) Microsoft Corporation.

Check failure

Code scanning / check-spelling

Check File Path

[AILLM](#security-tab) is not a recognized word. \(check-file-path\)
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation.

Check failure

Code scanning / check-spelling

Check File Path

[AILLM](#security-tab) is not a recognized word. \(check-file-path\)
@@ -0,0 +1,17 @@
// Copyright (c) Microsoft Corporation.

Check failure

Code scanning / check-spelling

Check File Path

[AILLM](#security-tab) is not a recognized word. \(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

[AILLM](#security-tab) is not a recognized word. \(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

[AIIs](#security-tab) is not a recognized word. \(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

[AIIs](#security-tab) is not a recognized word. \(unrecognized-spelling\)
void AISettingsViewModel::SetAzureOpenAIActive()
{
_Settings.GlobalSettings().AIInfo().ActiveProvider(Model::LLMProvider::AzureOpenAI);
_NotifyChanges(L"AzureOpenAIIsActive");

Check failure

Code scanning / check-spelling

Unrecognized Spelling

[AIIs](#security-tab) is not a recognized word. \(unrecognized-spelling\)
{
_Settings.GlobalSettings().AIInfo().ActiveProvider(Model::LLMProvider::AzureOpenAI);
_NotifyChanges(L"AzureOpenAIIsActive");
_NotifyChanges(L"OpenAIIsActive");

Check failure

Code scanning / check-spelling

Unrecognized Spelling

[AIIs](#security-tab) is not a recognized word. \(unrecognized-spelling\)

This comment has been minimized.

Comment on lines 180 to 185
const auto val{ _getActiveProviderImpl() };
if (val)
{
// an active provider was explicitly set, return that
return *val;
}
Copy link
Contributor Author

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.

@PankajBhojwani PankajBhojwani marked this pull request as ready for review July 9, 2024 23:54

This comment has been minimized.

This comment has been minimized.

This comment has been minimized.

This comment has been minimized.

Copy link
Member

@carlos-zamora carlos-zamora left a 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"
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

@zadjii-msft zadjii-msft added this to the Terminal v1.23 milestone Aug 22, 2024
Base automatically changed from dev/pabhoj/llm_provider_interface to feature/llm September 6, 2024 03:01
@PankajBhojwani PankajBhojwani mentioned this pull request Oct 11, 2024
6 tasks
Copy link
Member

@lhecker lhecker left a 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;
Copy link
Member

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.)

Copy link
Member

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();
Copy link
Member

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();
Copy link
Member

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))
Copy link
Member

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?

Comment on lines +110 to +113
if (!OpenAIKeyInputBox().Password().empty())
{
_ViewModel.OpenAIKey(OpenAIKeyInputBox().Password());
OpenAIKeyInputBox().Password(L"");
Copy link
Member

@lhecker lhecker Oct 16, 2024

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).

Comment on lines +129 to +140
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();
Copy link
Member

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants