mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 10:46:33 +02:00
Fix advanced paste settings migration issue (#43459)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request This pull request introduces a robust migration system for legacy Advanced Paste AI provider settings and credentials, refactoring both the migration logic and credential handling to be more maintainable and reliable. It also standardizes the default model names for AI providers and updates related UI placeholders to reflect these changes. The migration logic is now centralized in a new helper class, and legacy credential migration is handled more cleanly in both the settings and view model layers. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] Closes: #43456 - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed --------- Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com> Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
This commit is contained in:
@@ -163,28 +163,143 @@ namespace AdvancedPaste.Settings
|
||||
return false;
|
||||
}
|
||||
|
||||
if (settings.Properties.IsAIEnabled || !LegacyOpenAIKeyExists())
|
||||
var properties = settings.Properties;
|
||||
var configuration = properties.PasteAIConfiguration;
|
||||
|
||||
if (configuration is null)
|
||||
{
|
||||
configuration = new PasteAIConfiguration();
|
||||
properties.PasteAIConfiguration = configuration;
|
||||
}
|
||||
|
||||
bool hasLegacyProviders = configuration.LegacyProviderConfigurations is { Count: > 0 };
|
||||
bool legacyAdvancedAIConsumed = properties.TryConsumeLegacyAdvancedAIEnabled(out var advancedFlag);
|
||||
bool legacyAdvancedAIEnabled = legacyAdvancedAIConsumed && advancedFlag;
|
||||
PasswordCredential legacyCredential = TryGetLegacyOpenAICredential();
|
||||
|
||||
if (!hasLegacyProviders && legacyCredential is null && !legacyAdvancedAIConsumed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
settings.Properties.IsAIEnabled = true;
|
||||
return true;
|
||||
bool configurationUpdated = false;
|
||||
|
||||
if (hasLegacyProviders)
|
||||
{
|
||||
configurationUpdated |= AdvancedPasteMigrationHelper.MigrateLegacyProviderConfigurations(configuration);
|
||||
}
|
||||
|
||||
PasteAIProviderDefinition openAIProvider = null;
|
||||
if (legacyCredential is not null || hasLegacyProviders || legacyAdvancedAIConsumed)
|
||||
{
|
||||
var ensureResult = AdvancedPasteMigrationHelper.EnsureOpenAIProvider(configuration);
|
||||
openAIProvider = ensureResult.Provider;
|
||||
configurationUpdated |= ensureResult.Updated;
|
||||
}
|
||||
|
||||
if (legacyAdvancedAIConsumed && openAIProvider is not null && openAIProvider.EnableAdvancedAI != legacyAdvancedAIEnabled)
|
||||
{
|
||||
openAIProvider.EnableAdvancedAI = legacyAdvancedAIEnabled;
|
||||
configurationUpdated = true;
|
||||
}
|
||||
|
||||
if (legacyCredential is not null && openAIProvider is not null)
|
||||
{
|
||||
StoreMigratedOpenAICredential(openAIProvider.Id, openAIProvider.ServiceType, legacyCredential.Password);
|
||||
RemoveLegacyOpenAICredential();
|
||||
}
|
||||
|
||||
bool enabledUpdated = false;
|
||||
if (!properties.IsAIEnabled && legacyCredential is not null)
|
||||
{
|
||||
properties.IsAIEnabled = true;
|
||||
enabledUpdated = true;
|
||||
}
|
||||
|
||||
return configurationUpdated || enabledUpdated || legacyAdvancedAIConsumed;
|
||||
}
|
||||
|
||||
private static bool LegacyOpenAIKeyExists()
|
||||
private static PasswordCredential TryGetLegacyOpenAICredential()
|
||||
{
|
||||
try
|
||||
{
|
||||
PasswordVault vault = new();
|
||||
return vault.Retrieve("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey") is not null;
|
||||
var credential = vault.Retrieve("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey");
|
||||
credential?.RetrievePassword();
|
||||
return credential;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemoveLegacyOpenAICredential()
|
||||
{
|
||||
try
|
||||
{
|
||||
PasswordVault vault = new();
|
||||
TryRemoveCredential(vault, "https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void StoreMigratedOpenAICredential(string providerId, string serviceType, string password)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var serviceKind = serviceType.ToAIServiceType();
|
||||
if (serviceKind != AIServiceType.OpenAI)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string resource = "https://platform.openai.com/api-keys";
|
||||
string username = $"PowerToys_AdvancedPaste_PasteAI_openai_{NormalizeProviderIdentifier(providerId)}";
|
||||
|
||||
PasswordVault vault = new();
|
||||
TryRemoveCredential(vault, resource, username);
|
||||
|
||||
PasswordCredential credential = new(resource, username, password);
|
||||
vault.Add(credential);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to migrate legacy OpenAI credential", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryRemoveCredential(PasswordVault vault, string credentialResource, string credentialUserName)
|
||||
{
|
||||
try
|
||||
{
|
||||
PasswordCredential existingCred = vault.Retrieve(credentialResource, credentialUserName);
|
||||
vault.Remove(existingCred);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Credential doesn't exist, which is fine
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizeProviderIdentifier(string providerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(providerId))
|
||||
{
|
||||
return "default";
|
||||
}
|
||||
|
||||
var filtered = new string(providerId.Where(char.IsLetterOrDigit).ToArray());
|
||||
return string.IsNullOrWhiteSpace(filtered) ? "default" : filtered.ToLowerInvariant();
|
||||
}
|
||||
|
||||
public async Task SetActiveAIProviderAsync(string providerId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(providerId))
|
||||
|
||||
Reference in New Issue
Block a user