mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-31 17:36:49 +01:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b62f642184 | ||
|
|
5367e77a9c | ||
|
|
1c3a6ffe63 | ||
|
|
3d63d499da | ||
|
|
bd2967806f |
@@ -12,9 +12,8 @@ namespace LanguageModelProvider;
|
||||
|
||||
public sealed class FoundryLocalModelProvider : ILanguageModelProvider
|
||||
{
|
||||
private IEnumerable<ModelDetails>? _downloadedModels;
|
||||
private IEnumerable<FoundryCatalogModel>? _catalogModels;
|
||||
private FoundryClient? _foundryClient;
|
||||
private IEnumerable<FoundryCatalogModel>? _catalogModels;
|
||||
private string? _serviceUrl;
|
||||
|
||||
public static FoundryLocalModelProvider Instance { get; } = new();
|
||||
@@ -43,15 +42,6 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
|
||||
throw new InvalidOperationException(errorMessage);
|
||||
}
|
||||
|
||||
// Check if model is cached
|
||||
var isInCache = _downloadedModels?.Any(m => m.ProviderModelDetails is FoundryCachedModel cached && cached.Name == modelId) ?? false;
|
||||
if (!isInCache)
|
||||
{
|
||||
var errorMessage = $"The requested model '{modelId}' is not cached. Please download it using Foundry Local.";
|
||||
Logger.LogError($"[FoundryLocal] {errorMessage}");
|
||||
throw new InvalidOperationException(errorMessage);
|
||||
}
|
||||
|
||||
// Ensure the model is loaded before returning chat client
|
||||
var isLoaded = _foundryClient!.EnsureModelLoaded(modelId).GetAwaiter().GetResult();
|
||||
if (!isLoaded)
|
||||
@@ -74,7 +64,7 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
|
||||
|
||||
return new OpenAIClient(
|
||||
new ApiKeyCredential("none"),
|
||||
new OpenAIClientOptions { Endpoint = endpointUri })
|
||||
new OpenAIClientOptions { Endpoint = endpointUri, NetworkTimeout = TimeSpan.FromMinutes(5) })
|
||||
.GetChatClient(modelId)
|
||||
.AsIChatClient();
|
||||
}
|
||||
@@ -100,30 +90,38 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
|
||||
return $"new OpenAIClient(new ApiKeyCredential(\"none\"), new OpenAIClientOptions{{ Endpoint = new Uri(\"{_serviceUrl}/v1\") }}).GetChatClient(\"{modelId}\").AsIChatClient()";
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ModelDetails>> GetModelsAsync(bool ignoreCached = false, CancellationToken cancelationToken = default)
|
||||
public async Task<IEnumerable<ModelDetails>> GetModelsAsync(CancellationToken cancelationToken = default)
|
||||
{
|
||||
if (ignoreCached)
|
||||
{
|
||||
Logger.LogInfo("[FoundryLocal] Ignoring cached models, resetting");
|
||||
Reset();
|
||||
}
|
||||
|
||||
await InitializeAsync(cancelationToken);
|
||||
|
||||
Logger.LogInfo($"[FoundryLocal] Returning {_downloadedModels?.Count() ?? 0} downloaded models");
|
||||
return _downloadedModels ?? [];
|
||||
}
|
||||
if (_foundryClient == null)
|
||||
{
|
||||
return Array.Empty<ModelDetails>();
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
_downloadedModels = null;
|
||||
_catalogModels = null;
|
||||
_ = InitializeAsync();
|
||||
var cachedModels = await _foundryClient.ListCachedModels();
|
||||
List<ModelDetails> downloadedModels = [];
|
||||
|
||||
foreach (var model in cachedModels)
|
||||
{
|
||||
Logger.LogInfo($"[FoundryLocal] Adding unmatched cached model: {model.Name}");
|
||||
downloadedModels.Add(new ModelDetails
|
||||
{
|
||||
Id = $"fl-{model.Name}",
|
||||
Name = model.Name,
|
||||
Url = $"fl://{model.Name}",
|
||||
Description = $"{model.Name} running locally with Foundry Local",
|
||||
HardwareAccelerators = [HardwareAccelerator.FOUNDRYLOCAL],
|
||||
ProviderModelDetails = model,
|
||||
});
|
||||
}
|
||||
|
||||
return downloadedModels;
|
||||
}
|
||||
|
||||
private async Task InitializeAsync(CancellationToken cancelationToken = default)
|
||||
{
|
||||
if (_foundryClient != null && _downloadedModels != null && _downloadedModels.Any() && _catalogModels != null && _catalogModels.Any())
|
||||
if (_foundryClient != null && _catalogModels != null && _catalogModels.Any())
|
||||
{
|
||||
await _foundryClient.EnsureRunning().ConfigureAwait(false);
|
||||
return;
|
||||
@@ -145,29 +143,6 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
|
||||
var catalogModels = await _foundryClient.ListCatalogModels();
|
||||
Logger.LogInfo($"[FoundryLocal] Found {catalogModels.Count} catalog models");
|
||||
_catalogModels = catalogModels;
|
||||
|
||||
var cachedModels = await _foundryClient.ListCachedModels();
|
||||
Logger.LogInfo($"[FoundryLocal] Found {cachedModels.Count} cached models");
|
||||
|
||||
List<ModelDetails> downloadedModels = [];
|
||||
|
||||
foreach (var model in cachedModels)
|
||||
{
|
||||
Logger.LogInfo($"[FoundryLocal] Adding unmatched cached model: {model.Name}");
|
||||
downloadedModels.Add(new ModelDetails
|
||||
{
|
||||
Id = $"fl-{model.Name}",
|
||||
Name = model.Name,
|
||||
Url = $"fl://{model.Name}",
|
||||
Description = $"{model.Name} running locally with Foundry Local",
|
||||
HardwareAccelerators = [HardwareAccelerator.FOUNDRYLOCAL],
|
||||
SupportedOnQualcomm = true,
|
||||
ProviderModelDetails = model,
|
||||
});
|
||||
}
|
||||
|
||||
_downloadedModels = downloadedModels;
|
||||
Logger.LogInfo($"[FoundryLocal] Initialization complete. Total downloaded models: {downloadedModels.Count}");
|
||||
}
|
||||
|
||||
public async Task<bool> IsAvailable()
|
||||
|
||||
@@ -12,7 +12,7 @@ public interface ILanguageModelProvider
|
||||
|
||||
string ProviderDescription { get; }
|
||||
|
||||
Task<IEnumerable<ModelDetails>> GetModelsAsync(bool ignoreCached = false, CancellationToken cancelationToken = default);
|
||||
Task<IEnumerable<ModelDetails>> GetModelsAsync(CancellationToken cancelationToken = default);
|
||||
|
||||
IChatClient? GetIChatClient(string modelId);
|
||||
|
||||
|
||||
@@ -24,8 +24,6 @@ public class ModelDetails
|
||||
|
||||
public List<HardwareAccelerator> HardwareAccelerators { get; set; } = [];
|
||||
|
||||
public bool SupportedOnQualcomm { get; set; }
|
||||
|
||||
public string License { get; set; } = string.Empty;
|
||||
|
||||
public object? ProviderModelDetails { get; set; }
|
||||
|
||||
@@ -215,7 +215,6 @@ public sealed class AdvancedAIKernelService : KernelServiceBase
|
||||
return new OpenAIPromptExecutionSettings
|
||||
{
|
||||
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(),
|
||||
Temperature = 0.01,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ public sealed class FoundryLocalPasteProvider : IPasteAIProvider
|
||||
var options = new ChatOptions
|
||||
{
|
||||
ModelId = modelReference,
|
||||
MaxOutputTokens = 2048,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(systemPrompt))
|
||||
|
||||
@@ -157,8 +157,6 @@ namespace AdvancedPaste.Services.CustomActions
|
||||
{
|
||||
AIServiceType.OpenAI or AIServiceType.AzureOpenAI => new OpenAIPromptExecutionSettings
|
||||
{
|
||||
Temperature = 0.01,
|
||||
MaxTokens = 2000,
|
||||
FunctionChoiceBehavior = null,
|
||||
},
|
||||
_ => new PromptExecutionSettings(),
|
||||
|
||||
@@ -350,7 +350,7 @@ namespace Awake.Core
|
||||
TrayHelper.TimedIcon,
|
||||
TrayIconAction.Update);
|
||||
},
|
||||
_ => HandleTimerCompletion("timed"),
|
||||
() => HandleTimerCompletion("timed"),
|
||||
_tokenSource.Token);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
<ApplicationIcon>Resources\ImageResizer.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- <PropertyGroup>
|
||||
<ApplicationManifest>ImageResizerUI.dev.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(CIBuild)'=='true'">
|
||||
<ApplicationManifest>ImageResizerUI.prod.manifest</ApplicationManifest>
|
||||
</PropertyGroup>
|
||||
</PropertyGroup> -->
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
|
||||
@@ -30,7 +30,7 @@ public sealed partial class FoundryLocalModelPicker : UserControl
|
||||
|
||||
public delegate void DownloadRequestedEventHandler(object sender, object payload);
|
||||
|
||||
public delegate void LoadRequestedEventHandler(object sender, FoundryLoadRequestedEventArgs args);
|
||||
public delegate void LoadRequestedEventHandler(object sender);
|
||||
|
||||
public event ModelSelectionChangedEventHandler SelectionChanged;
|
||||
|
||||
@@ -94,7 +94,7 @@ public sealed partial class FoundryLocalModelPicker : UserControl
|
||||
|
||||
public bool HasDownloadableModels => DownloadableModels?.Cast<object>().Any() ?? false;
|
||||
|
||||
public void RequestLoad(bool refresh)
|
||||
public void RequestLoad()
|
||||
{
|
||||
if (IsLoading)
|
||||
{
|
||||
@@ -107,7 +107,7 @@ public sealed partial class FoundryLocalModelPicker : UserControl
|
||||
|
||||
IsAvailable = false;
|
||||
StatusText = "Loading Foundry Local status...";
|
||||
LoadRequested?.Invoke(this, new FoundryLoadRequestedEventArgs(refresh));
|
||||
LoadRequested?.Invoke(this);
|
||||
}
|
||||
|
||||
private static void OnCachedModelsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
@@ -310,7 +310,7 @@ public sealed partial class FoundryLocalModelPicker : UserControl
|
||||
|
||||
private void RefreshModelsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RequestLoad(refresh: true);
|
||||
RequestLoad();
|
||||
}
|
||||
|
||||
private void UpdateVisualStates()
|
||||
@@ -444,14 +444,4 @@ public sealed partial class FoundryLocalModelPicker : UserControl
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(license) ? Visibility.Collapsed : Visibility.Visible;
|
||||
}
|
||||
|
||||
public sealed class FoundryLoadRequestedEventArgs : EventArgs
|
||||
{
|
||||
public FoundryLoadRequestedEventArgs(bool refresh)
|
||||
{
|
||||
Refresh = refresh;
|
||||
}
|
||||
|
||||
public bool Refresh { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
{
|
||||
ViewModel.RefreshEnabledState();
|
||||
UpdatePasteAIUIVisibility();
|
||||
_ = UpdateFoundryLocalUIAsync(refreshFoundry: true);
|
||||
_ = UpdateFoundryLocalUIAsync();
|
||||
}
|
||||
|
||||
private void EnableAdvancedPasteAI() => ViewModel.EnableAI();
|
||||
@@ -384,7 +384,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
}
|
||||
}
|
||||
|
||||
private Task UpdateFoundryLocalUIAsync(bool refreshFoundry = false)
|
||||
private Task UpdateFoundryLocalUIAsync()
|
||||
{
|
||||
string selectedType = ViewModel?.PasteAIProviderDraft?.ServiceType ?? string.Empty;
|
||||
bool isFoundryLocal = string.Equals(selectedType, "FoundryLocal", StringComparison.OrdinalIgnoreCase);
|
||||
@@ -419,12 +419,12 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
PasteAIProviderConfigurationDialog.IsPrimaryButtonEnabled = false;
|
||||
}
|
||||
|
||||
FoundryLocalPicker?.RequestLoad(refreshFoundry);
|
||||
FoundryLocalPicker?.RequestLoad();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task LoadFoundryLocalModelsAsync(bool refresh = false)
|
||||
private async Task LoadFoundryLocalModelsAsync()
|
||||
{
|
||||
if (FoundryLocalPanel is null)
|
||||
{
|
||||
@@ -456,9 +456,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<ModelDetails> cachedModelsEnumerable = refresh
|
||||
? await provider.GetModelsAsync(ignoreCached: true, cancelationToken: cancellationToken)
|
||||
: await provider.GetModelsAsync(cancelationToken: cancellationToken);
|
||||
IEnumerable<ModelDetails> cachedModelsEnumerable = await provider.GetModelsAsync(cancelationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
@@ -467,9 +465,12 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
|
||||
var cachedModels = cachedModelsEnumerable?.ToList() ?? new List<ModelDetails>();
|
||||
|
||||
UpdateFoundryCollections(cachedModels);
|
||||
ShowFoundryAvailableState();
|
||||
RestoreFoundrySelection(cachedModels);
|
||||
DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
UpdateFoundryCollections(cachedModels);
|
||||
ShowFoundryAvailableState();
|
||||
RestoreFoundrySelection(cachedModels);
|
||||
});
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -478,12 +479,18 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorMessage = $"Unable to load Foundry Local models. {ex.Message}";
|
||||
ShowFoundryUnavailableState(errorMessage);
|
||||
System.Diagnostics.Debug.WriteLine($"[AdvancedPastePage] Failed to load Foundry Local models: {ex}");
|
||||
DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
ShowFoundryUnavailableState(errorMessage);
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
UpdateFoundrySaveButtonState();
|
||||
DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
UpdateFoundrySaveButtonState();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -672,9 +679,9 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
UpdateFoundrySaveButtonState();
|
||||
}
|
||||
|
||||
private async void FoundryLocalPicker_LoadRequested(object sender, FoundryLocalModelPicker.FoundryLoadRequestedEventArgs args)
|
||||
private async void FoundryLocalPicker_LoadRequested(object sender)
|
||||
{
|
||||
await LoadFoundryLocalModelsAsync(args?.Refresh ?? false);
|
||||
await LoadFoundryLocalModelsAsync();
|
||||
}
|
||||
|
||||
private sealed class FoundryDownloadableModel : INotifyPropertyChanged
|
||||
@@ -1089,7 +1096,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
PasteAIProviderConfigurationDialog.Title = $"{displayName} provider configuration";
|
||||
}
|
||||
|
||||
await UpdateFoundryLocalUIAsync(refreshFoundry: true);
|
||||
await UpdateFoundryLocalUIAsync();
|
||||
UpdatePasteAIUIVisibility();
|
||||
RefreshDialogBindings();
|
||||
|
||||
@@ -1118,7 +1125,7 @@ namespace Microsoft.PowerToys.Settings.UI.Views
|
||||
: $"{titlePrefix} provider configuration";
|
||||
|
||||
UpdatePasteAIUIVisibility();
|
||||
await UpdateFoundryLocalUIAsync(refreshFoundry: false);
|
||||
await UpdateFoundryLocalUIAsync();
|
||||
RefreshDialogBindings();
|
||||
PasteAIApiKeyPasswordBox.Password = ViewModel.GetPasteAIApiKey(provider.Id, provider.ServiceType);
|
||||
await PasteAIProviderConfigurationDialog.ShowAsync();
|
||||
|
||||
Reference in New Issue
Block a user