From 67a013f72997eae4b57f04e10fda545564373c9f Mon Sep 17 00:00:00 2001 From: Kai Tao <69313318+vanzue@users.noreply.github.com> Date: Tue, 10 Feb 2026 15:31:23 +0800 Subject: [PATCH] Advanced Paste: Handle Foundry local Port change on the fly (#45362) ## Summary of the Pull Request Foundry Local returns 400 Bad Request if a manual port change made for foundry local. Fix #45340 ## PR Checklist - [X] Closes: #45340 - [ ] **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 ## Detailed Description of the Pull Request / Additional comments ## Validation Steps Performed Follow steps described in the issue, and the advanced paste can work without having to restart powertoys itself --- .../FoundryLocalModelProvider.cs | 92 ++++++++++++++++++- 1 file changed, 88 insertions(+), 4 deletions(-) diff --git a/src/common/LanguageModelProvider/FoundryLocalModelProvider.cs b/src/common/LanguageModelProvider/FoundryLocalModelProvider.cs index 5158e4334e..ae39080706 100644 --- a/src/common/LanguageModelProvider/FoundryLocalModelProvider.cs +++ b/src/common/LanguageModelProvider/FoundryLocalModelProvider.cs @@ -34,8 +34,7 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider } // Check if model is in catalog - var isInCatalog = _catalogModels?.Any(m => m.Name == modelId) ?? false; - if (!isInCatalog) + if (!EnsureModelInCatalog(modelId)) { var errorMessage = $"{modelId} is not supported in Foundry Local. Please configure supported models in Settings."; Logger.LogError($"[FoundryLocal] {errorMessage}"); @@ -43,15 +42,28 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider } // Ensure the model is loaded before returning chat client - var isLoaded = _foundryClient!.EnsureModelLoaded(modelId).GetAwaiter().GetResult(); + var isLoaded = EnsureModelLoadedWithRefresh(modelId); if (!isLoaded) { Logger.LogError($"[FoundryLocal] Failed to load model: {modelId}"); throw new InvalidOperationException($"Failed to load the model '{modelId}'."); } + var client = _foundryClient; + if (client == null) + { + const string message = "Foundry Local client could not be created. Please make sure Foundry Local is installed and running."; + Logger.LogError($"[FoundryLocal] {message}"); + throw new InvalidOperationException(message); + } + // Use ServiceUri instead of Endpoint since Endpoint already includes /v1 - var baseUri = _foundryClient.GetServiceUri(); + var baseUri = client.GetServiceUri(); + if (baseUri == null && TryRefreshClient("Service URI was not available")) + { + baseUri = _foundryClient?.GetServiceUri(); + } + if (baseUri == null) { const string message = "Foundry Local service URL is not available. Please make sure Foundry Local is installed and running."; @@ -124,6 +136,7 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider if (_foundryClient != null && _catalogModels != null && _catalogModels.Any()) { await _foundryClient.EnsureRunning().ConfigureAwait(false); + _serviceUrl = await _foundryClient.GetServiceUrl().ConfigureAwait(false); return; } @@ -153,4 +166,75 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider Logger.LogInfo($"[FoundryLocal] Available: {available}"); return available; } + + private bool EnsureModelInCatalog(string modelId) + { + var isInCatalog = _catalogModels?.Any(m => m.Name == modelId) ?? false; + if (isInCatalog) + { + return true; + } + + Logger.LogWarning($"[FoundryLocal] Model not found in catalog. Refreshing client for model: {modelId}"); + if (!TryRefreshClient("Model not in catalog")) + { + return false; + } + + return _catalogModels?.Any(m => m.Name == modelId) ?? false; + } + + private bool EnsureModelLoadedWithRefresh(string modelId) + { + var isLoaded = false; + + try + { + isLoaded = _foundryClient!.EnsureModelLoaded(modelId).GetAwaiter().GetResult(); + } + catch (Exception ex) + { + Logger.LogWarning($"[FoundryLocal] EnsureModelLoaded failed: {ex.Message}"); + } + + if (isLoaded) + { + return true; + } + + if (!TryRefreshClient("EnsureModelLoaded failed")) + { + return false; + } + + try + { + return _foundryClient!.EnsureModelLoaded(modelId).GetAwaiter().GetResult(); + } + catch (Exception ex) + { + Logger.LogError($"[FoundryLocal] EnsureModelLoaded failed after refresh: {ex.Message}", ex); + return false; + } + } + + private bool TryRefreshClient(string reason) + { + Logger.LogInfo($"[FoundryLocal] Refreshing Foundry Local client: {reason}"); + + try + { + _foundryClient = null; + _catalogModels = null; + _serviceUrl = null; + + InitializeAsync().GetAwaiter().GetResult(); + return _foundryClient != null; + } + catch (Exception ex) + { + Logger.LogError($"[FoundryLocal] Failed to refresh Foundry Local client: {ex.Message}", ex); + return false; + } + } }