diff --git a/CHANGELOG.md b/CHANGELOG.md index 964c766d7..56ab8d95e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ All notable changes to Stability Matrix will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html). +## v2.15.4 +### Changed +- Updated Early Access indicators in the Civitai Details page to be more visible +- Updated error message when attempting to download a website-generation-only model from Civitai +- Updated nunchaku installer to 1.0.2 +- Updated Package Import dialog to have Python version selector +### Fixed +- Fixed [#1435](https://github.com/LykosAI/StabilityMatrix/issues/1435) - SwarmUI not launching due to missing dotnet +- Fixed various install and Inference issues with ComfyUI-Zluda - big thanks to @neural_fault for the PRs! +- Fixed sageattention version getting out of sync after torch updates in ComfyUI +- Potentially fixed issue where uv-managed Python versions would not appear in the version selector +### Supporters +#### 🌟 Visionaries +Our heartfelt thanks to the driving force behind our progress, our Visionaries: **Waterclouds**, **JungleDragon**, **bluepopsicle**, **Bob S**, and **whudunit**! Your incredible support is the fuel that powers our development, allowing us to tackle bugs and push forward with confidence. +#### 🚀 Pioneers +A huge shoutout to our amazing Pioneers, who keep the momentum going strong! Thank you for being our trusted crew on this journey: **Szir777**, **Noah M**, **USATechDude**, **Thom**, **SeraphOfSalem**, **Desert Viber**, **Tundra Everquill**, **Adam**, **Droolguy**, **Philip R.**, **ACTUALLY_the_Real_Willem_Dafoe**, **takyamtom**, and **robek**! + ## v2.15.3 ### Changed - Updated fallback rocm index for InvokeAI to rocm6.3 @@ -21,7 +38,6 @@ Our deepest gratitude to our Visionaries for their foundational support: **Water #### 🚀 Pioneers A huge thank you to our incredible Pioneers for keeping the project on track! Your support is vital for these important refinement updates. Thank you to **Szir777**, **Noah M**, **USATechDude**, **Thom**, **SeraphOfSalem**, **Desert Viber**, **Tundra Everquill**, **Adam**, **Droolguy**, **Philip R.**, **ACTUALLY_the_Real_Willem_Dafoe**, **takyamtom**, and a warm welcome to our newest Pioneer, **robek**! - ## v2.15.2 ### Changed - Updated Avalonia to 11.3.7 diff --git a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs index b70175ee2..bed6978bb 100644 --- a/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs @@ -60,8 +60,6 @@ private bool IsPythonVersionInstalled(PyVersion version) => private DirectoryPath NodeDir => AssetsDir.JoinDir("nodejs"); private string NpmPath => Path.Combine(NodeDir, "bin", "npm"); private bool IsNodeInstalled => File.Exists(NpmPath); - - private DirectoryPath DotnetDir => AssetsDir.JoinDir("dotnet"); private string DotnetPath => Path.Combine(DotnetDir, "dotnet"); private string Dotnet7SdkExistsPath => Path.Combine(DotnetDir, "sdk", "7.0.405"); private string Dotnet8SdkExistsPath => Path.Combine(DotnetDir, "sdk", "8.0.101"); @@ -77,13 +75,15 @@ private bool IsPythonVersionInstalled(PyVersion version) => // Cached store of whether or not git is installed private bool? isGitInstalled; + private string ExpectedUvVersion => "0.8.4"; + public bool IsVcBuildToolsInstalled => false; public bool IsHipSdkInstalled => false; private string UvDownloadPath => Path.Combine(AssetsDir, "uv.tar.gz"); private string UvExtractPath => Path.Combine(AssetsDir, "uv"); public string UvExePath => Path.Combine(UvExtractPath, "uv"); public bool IsUvInstalled => File.Exists(UvExePath); - private string ExpectedUvVersion => "0.8.4"; + public DirectoryPath DotnetDir => AssetsDir.JoinDir("dotnet"); // Helper method to get Python download URL for a specific version private RemoteResource GetPythonDownloadResource(PyVersion version) diff --git a/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs b/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs index bf6b76f65..26c281ee2 100644 --- a/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs +++ b/StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs @@ -93,10 +93,9 @@ private string GetPythonLibraryZipPath(PyVersion version) => private string NodeDownloadPath => Path.Combine(AssetsDir, "nodejs.zip"); private string Dotnet7DownloadPath => Path.Combine(AssetsDir, "dotnet-sdk-7.0.405-win-x64.zip"); private string Dotnet8DownloadPath => Path.Combine(AssetsDir, "dotnet-sdk-8.0.101-win-x64.zip"); - private string DotnetExtractPath => Path.Combine(AssetsDir, "dotnet"); - private string DotnetExistsPath => Path.Combine(DotnetExtractPath, "dotnet.exe"); - private string Dotnet7SdkExistsPath => Path.Combine(DotnetExtractPath, "sdk", "7.0.405"); - private string Dotnet8SdkExistsPath => Path.Combine(DotnetExtractPath, "sdk", "8.0.101"); + private string DotnetExistsPath => Path.Combine(DotnetDir, "dotnet.exe"); + private string Dotnet7SdkExistsPath => Path.Combine(DotnetDir, "sdk", "7.0.405"); + private string Dotnet8SdkExistsPath => Path.Combine(DotnetDir, "sdk", "8.0.101"); private string VcBuildToolsDownloadPath => Path.Combine(AssetsDir, "vs_BuildTools.exe"); private string VcBuildToolsExistsPath => @@ -121,6 +120,7 @@ private string GetPythonLibraryZipPath(PyVersion version) => public string GitBinPath => Path.Combine(PortableGitInstallDir, "bin"); public bool IsVcBuildToolsInstalled => Directory.Exists(VcBuildToolsExistsPath); public bool IsHipSdkInstalled => Directory.Exists(HipInstalledPath); + public DirectoryPath DotnetDir => Path.Combine(AssetsDir, "dotnet"); // Check if a specific Python version is installed public bool IsPythonVersionInstalled(PyVersion version) => File.Exists(GetPythonDllPath(version)); @@ -740,7 +740,7 @@ await DownloadAndExtractPrerequisite( progress, Dotnet7DownloadUrl, Dotnet7DownloadPath, - DotnetExtractPath + DotnetDir ); } @@ -750,7 +750,7 @@ await DownloadAndExtractPrerequisite( progress, Dotnet8DownloadUrl, Dotnet8DownloadPath, - DotnetExtractPath + DotnetDir ); } } @@ -782,18 +782,18 @@ await downloadService.DownloadToFileAsync( var process = ProcessRunner.StartAnsiProcess( VcBuildToolsDownloadPath, - "--quiet --wait " + - "--add Microsoft.VisualStudio.Workload.VCTools " + - "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 " + - "--add Microsoft.VisualStudio.Component.VC.CMake.Project " + - "--add Microsoft.VisualStudio.Component.VC.Llvm.Clang " + - "--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang " + - "--add Microsoft.VisualStudio.Component.Windows10SDK.18362 " + - "--add Microsoft.VisualStudio.Component.Windows10SDK.19041 " + - "--add Microsoft.VisualStudio.Component.Windows10SDK.20348 " + - "--add Microsoft.VisualStudio.Component.Windows11SDK.22000 " + - "--add Microsoft.VisualStudio.Component.Windows11SDK.22621 " + - "--add Microsoft.VisualStudio.Component.Windows11SDK.26100", + "--quiet --wait " + + "--add Microsoft.VisualStudio.Workload.VCTools " + + "--add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 " + + "--add Microsoft.VisualStudio.Component.VC.CMake.Project " + + "--add Microsoft.VisualStudio.Component.VC.Llvm.Clang " + + "--add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Llvm.Clang " + + "--add Microsoft.VisualStudio.Component.Windows10SDK.18362 " + + "--add Microsoft.VisualStudio.Component.Windows10SDK.19041 " + + "--add Microsoft.VisualStudio.Component.Windows10SDK.20348 " + + "--add Microsoft.VisualStudio.Component.Windows11SDK.22000 " + + "--add Microsoft.VisualStudio.Component.Windows11SDK.22621 " + + "--add Microsoft.VisualStudio.Component.Windows11SDK.26100", outputDataReceived: output => progress?.Report( new ProgressReport( diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs index ecab4e7ba..0c60c7e4c 100644 --- a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitAiBrowserViewModel.cs @@ -359,6 +359,9 @@ protected override async Task OnInitialLoadedAsync() public override async Task OnLoadedAsync() { + if (Design.IsDesignMode) + return; + var baseModels = await baseModelTypeService.GetBaseModelTypes(includeAllOption: false); baseModels = baseModels.Except(settingsManager.Settings.DisabledBaseModelTypes).ToList(); if (baseModels.Count == 0) diff --git a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitDetailsPageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitDetailsPageViewModel.cs index c29c1dbce..d308160e8 100644 --- a/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitDetailsPageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CivitDetailsPageViewModel.cs @@ -109,7 +109,8 @@ IModelImportService modelImportService nameof(LastUpdated), nameof(ShortSha256), nameof(BaseModelType), - nameof(ModelFileNameFormat) + nameof(ModelFileNameFormat), + nameof(IsEarlyAccess) )] public partial ModelVersionViewModel? SelectedVersion { get; set; } @@ -169,6 +170,8 @@ IModelImportService modelImportService public string BaseModelType => SelectedVersion?.ModelVersion.BaseModel?.Trim() ?? string.Empty; + public bool IsEarlyAccess => SelectedVersion?.ModelVersion.IsEarlyAccess ?? false; + public string CivitUrl => $@"https://civitai.com/models/{CivitModel.Id}"; public int DescriptionRowSpan => string.IsNullOrWhiteSpace(ModelVersionDescription) ? 3 : 1; diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs index cd4430bfb..668080724 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/PackageImportViewModel.cs @@ -14,6 +14,7 @@ using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.Views.Dialogs; using StabilityMatrix.Core.Attributes; +using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.Database; @@ -27,12 +28,14 @@ namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; [View(typeof(PackageImportDialog))] [ManagedService] [RegisterTransient] -public partial class PackageImportViewModel : ContentDialogViewModelBase +public partial class PackageImportViewModel( + IPackageFactory packageFactory, + ISettingsManager settingsManager, + IPyInstallationManager pyInstallationManager +) : ContentDialogViewModelBase { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - - private readonly IPackageFactory packageFactory; - private readonly ISettingsManager settingsManager; + private bool venvDetected = false; [ObservableProperty] private DirectoryPath? packagePath; @@ -40,8 +43,7 @@ public partial class PackageImportViewModel : ContentDialogViewModelBase [ObservableProperty] private BasePackage? selectedBasePackage; - public IReadOnlyList AvailablePackages => - packageFactory.GetAllAvailablePackages().ToImmutableArray(); + public IReadOnlyList AvailablePackages => [.. packageFactory.GetAllAvailablePackages()]; [ObservableProperty] private PackageVersion? selectedVersion; @@ -64,6 +66,9 @@ public partial class PackageImportViewModel : ContentDialogViewModelBase [ObservableProperty] private bool showCustomCommitSha; + [ObservableProperty] + public partial bool ShowPythonVersionSelection { get; set; } = true; + // Version types (release or commit) [ObservableProperty] [NotifyPropertyChangedFor(nameof(ReleaseLabelText), nameof(IsReleaseMode), nameof(SelectedVersion))] @@ -73,6 +78,13 @@ public partial class PackageImportViewModel : ContentDialogViewModelBase [NotifyPropertyChangedFor(nameof(IsReleaseModeAvailable))] private PackageVersionType availableVersionTypes = PackageVersionType.GithubRelease | PackageVersionType.Commit; + + [ObservableProperty] + public partial ObservableCollection AvailablePythonVersions { get; set; } = []; + + [ObservableProperty] + public partial UvPythonInfo? SelectedPythonVersion { get; set; } + public string ReleaseLabelText => IsReleaseMode ? "Version" : "Branch"; public bool IsReleaseMode { @@ -82,12 +94,6 @@ public bool IsReleaseMode public bool IsReleaseModeAvailable => AvailableVersionTypes.HasFlag(PackageVersionType.GithubRelease); - public PackageImportViewModel(IPackageFactory packageFactory, ISettingsManager settingsManager) - { - this.packageFactory = packageFactory; - this.settingsManager = settingsManager; - } - public override async Task OnLoadedAsync() { SelectedBasePackage ??= AvailablePackages[0]; @@ -115,6 +121,24 @@ public override async Task OnLoadedAsync() ); UpdateSelectedVersionToLatestMain(); } + + var pythonVersions = await pyInstallationManager.GetAllAvailablePythonsAsync(); + AvailablePythonVersions = new ObservableCollection(pythonVersions); + + if ( + PackagePath is not null + && Utilities.TryGetPyVenvVersion(PackagePath.FullPath, out var venvPyVersion) + ) + { + var matchingVenvPy = AvailablePythonVersions.FirstOrDefault(x => x.Version == venvPyVersion); + if (matchingVenvPy != default) + { + SelectedPythonVersion = matchingVenvPy; + venvDetected = true; + } + } + + SelectedPythonVersion ??= GetRecommendedPyVersion() ?? AvailablePythonVersions.LastOrDefault(); } catch (Exception e) { @@ -155,7 +179,7 @@ partial void OnSelectedVersionChanged(PackageVersion? value) if (commits is null || commits.Count == 0) return; - commits = [..commits, new GitCommit { Sha = "Custom..." }]; + commits = [.. commits, new GitCommit { Sha = "Custom..." }]; AvailableCommits = new ObservableCollection(commits); SelectedCommit = AvailableCommits[0]; @@ -205,12 +229,18 @@ partial void OnSelectedBasePackageChanged(BasePackage? value) if (commits is null || commits.Count == 0) return; - commits = [..commits, new GitCommit { Sha = "Custom..." }]; + commits = [.. commits, new GitCommit { Sha = "Custom..." }]; AvailableCommits = new ObservableCollection(commits); SelectedCommit = AvailableCommits[0]; UpdateSelectedVersionToLatestMain(); } + + if (!venvDetected) + { + SelectedPythonVersion = + GetRecommendedPyVersion() ?? AvailablePythonVersions.FirstOrDefault(); + } }) .SafeFireAndForget(); } @@ -267,7 +297,9 @@ public async Task AddPackageWithCurrentInputs() LastUpdateCheck = DateTimeOffset.Now, PreferredTorchIndex = torchVersion, PreferredSharedFolderMethod = sharedFolderRecommendation, - PythonVersion = PyInstallationManager.Python_3_10_11.StringValue + PythonVersion = + SelectedPythonVersion?.Version.StringValue + ?? PyInstallationManager.Python_3_10_17.StringValue, }; // Recreate venv if it's a BaseGitPackage @@ -300,4 +332,10 @@ await gitPackage.SetupVenv( settingsManager.Transaction(s => s.InstalledPackages.Add(package)); } + + private UvPythonInfo? GetRecommendedPyVersion() => + AvailablePythonVersions.LastOrDefault(x => + x.Version.Major.Equals(SelectedBasePackage?.RecommendedPythonVersion.Major) + && x.Version.Minor.Equals(SelectedBasePackage?.RecommendedPythonVersion.Minor) + ); } diff --git a/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs index 24d386a6a..bd932933f 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs @@ -145,6 +145,24 @@ exception is HuggingFaceLoginRequiredException ShowHuggingFaceLoginRequiredDialog(); return; } + else if ( + exception is CivitDownloadDisabledException + || exception.InnerException is CivitDownloadDisabledException + ) + { + Dispatcher.UIThread.InvokeAsync(async () => + await notificationService.ShowPersistentAsync( + NotificationKey.Download_Failed, + new Notification + { + Title = "Download Disabled", + Body = + $"The creator of {e.FileName} has disabled downloads on this file", + } + ) + ); + return; + } } Dispatcher.UIThread.InvokeAsync(async () => diff --git a/StabilityMatrix.Avalonia/Views/CivitDetailsPage.axaml b/StabilityMatrix.Avalonia/Views/CivitDetailsPage.axaml index 37a373b6e..15e73887e 100644 --- a/StabilityMatrix.Avalonia/Views/CivitDetailsPage.axaml +++ b/StabilityMatrix.Avalonia/Views/CivitDetailsPage.axaml @@ -268,10 +268,9 @@ Margin="0,2,8,0" HorizontalAlignment="Left" VerticalAlignment="Center" - Foreground="Orange" IsVisible="{Binding ModelVersion.IsEarlyAccess}" ToolTip.Tip="Early Access" - Value="fa-regular fa-credit-card" /> + Value="fa-solid fa-coins" /> - + + + + + SelectedItem="{Binding SelectedBasePackage}" /> - - + + + OnContent="{x:Static lang:Resources.Label_Releases}" /> - + - - + + SelectedItem="{Binding SelectedCommit}" /> - + - + + + + + + + + + + + + + + + + + + + + + + diff --git a/StabilityMatrix.Core/Exceptions/CivitDownloadDisabledException.cs b/StabilityMatrix.Core/Exceptions/CivitDownloadDisabledException.cs new file mode 100644 index 000000000..025111d45 --- /dev/null +++ b/StabilityMatrix.Core/Exceptions/CivitDownloadDisabledException.cs @@ -0,0 +1,3 @@ +namespace StabilityMatrix.Core.Exceptions; + +public class CivitDownloadDisabledException : UnauthorizedAccessException; diff --git a/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs b/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs index 27f15f7fd..02c17638c 100644 --- a/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs +++ b/StabilityMatrix.Core/Helper/IPrerequisiteHelper.cs @@ -22,6 +22,7 @@ public interface IPrerequisiteHelper Task InstallUvIfNecessary(IProgress? progress = null); string UvExePath { get; } bool IsUvInstalled { get; } + DirectoryPath DotnetDir { get; } Task UnpackResourcesIfNecessary(IProgress? progress = null); Task InstallGitIfNecessary(IProgress? progress = null); Task InstallPythonIfNecessary(IProgress? progress = null); diff --git a/StabilityMatrix.Core/Helper/Utilities.cs b/StabilityMatrix.Core/Helper/Utilities.cs index e98d55da8..6e7f6d7d2 100644 --- a/StabilityMatrix.Core/Helper/Utilities.cs +++ b/StabilityMatrix.Core/Helper/Utilities.cs @@ -1,6 +1,7 @@ using System.Diagnostics; using System.Reflection; using System.Text.RegularExpressions; +using StabilityMatrix.Core.Python; namespace StabilityMatrix.Core.Helper; @@ -106,6 +107,86 @@ public static string RemoveHtml(string? stringWithHtml) return pruned; } + /// + /// Try to find pyvenv.cfg in common locations and parse its version into PyVersion. + /// + public static bool TryGetPyVenvVersion(string packageLocation, out PyVersion version) + { + version = default; + + if (string.IsNullOrWhiteSpace(packageLocation)) + return false; + + // Common placements + var candidates = new[] + { + Path.Combine(packageLocation, "pyvenv.cfg"), + Path.Combine(packageLocation, "venv", "pyvenv.cfg"), + Path.Combine(packageLocation, ".venv", "pyvenv.cfg"), + }; + + foreach (var candidate in candidates) + { + if (!File.Exists(candidate)) + continue; + + if (!TryReadVersionFromCfg(candidate, out var pyv)) + continue; + + version = pyv; + return true; + } + + return false; + } + + private static bool TryReadVersionFromCfg(string cfgFile, out PyVersion version) + { + version = default; + + var kv = ReadKeyValues(cfgFile); + + // Prefer version_info (e.g., "3.10.11.final.0"), fall back to version (e.g., "3.12.0") + if (!kv.TryGetValue("version_info", out var raw) || string.IsNullOrWhiteSpace(raw)) + kv.TryGetValue("version", out raw); + + if (string.IsNullOrWhiteSpace(raw)) + return false; + + // Grab the first 1–3 dot-separated integers and ignore anything after (e.g., ".final.0", ".rc1") + // Examples matched: "3", "3.12", "3.10.11" (from "3.10.11.final.0") + var m = Regex.Match(raw, @"(? ReadKeyValues(string path) + { + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var line in File.ReadAllLines(path)) + { + var trimmed = line.Trim(); + if (trimmed.Length == 0 || trimmed.StartsWith("#")) + continue; + + var idx = trimmed.IndexOf('='); + if (idx <= 0) + continue; + + var key = trimmed[..idx].Trim(); + var val = trimmed[(idx + 1)..].Trim(); + dict[key] = val; + } + return dict; + } + /// /// Returns the simplified aspect ratio as a tuple: (widthRatio, heightRatio). /// e.g. GetAspectRatio(1920,1080) -> (16,9) diff --git a/StabilityMatrix.Core/Models/PackageModification/InstallNunchakuStep.cs b/StabilityMatrix.Core/Models/PackageModification/InstallNunchakuStep.cs index e6d570e9a..381bcb24a 100644 --- a/StabilityMatrix.Core/Models/PackageModification/InstallNunchakuStep.cs +++ b/StabilityMatrix.Core/Models/PackageModification/InstallNunchakuStep.cs @@ -62,17 +62,17 @@ await pyInstallationManager.GetInstallationAsync(pyVersion).ConfigureAwait(false var torchVersion = torchInfo.Version switch { - var v when v.StartsWith("2.5") => "2.5", - var v when v.StartsWith("2.6") => "2.6", var v when v.StartsWith("2.7") => "2.7", var v when v.StartsWith("2.8") => "2.8", + var v when v.StartsWith("2.9") => "2.9", var v when v.StartsWith("2.10") => "2.10", _ => throw new InvalidOperationException( "No compatible torch version found in the virtual environment." ), }; + var downloadUrl = - $"https://github.com/nunchaku-tech/nunchaku/releases/download/v1.0.1/nunchaku-1.0.1+torch{torchVersion}-{shortPythonVersionString}-{shortPythonVersionString}-{platform}.whl"; + $"https://github.com/nunchaku-tech/nunchaku/releases/download/v1.0.2/nunchaku-1.0.2+torch{torchVersion}-{shortPythonVersionString}-{shortPythonVersionString}-{platform}.whl"; progress?.Report( new ProgressReport(-1f, message: "Installing Nunchaku backend", isIndeterminate: true) ); diff --git a/StabilityMatrix.Core/Models/Packages/BasePackage.cs b/StabilityMatrix.Core/Models/Packages/BasePackage.cs index 10779e8bb..155ae2887 100644 --- a/StabilityMatrix.Core/Models/Packages/BasePackage.cs +++ b/StabilityMatrix.Core/Models/Packages/BasePackage.cs @@ -298,12 +298,7 @@ public virtual TorchIndex GetRecommendedTorchVersion() : PackageVersionType.GithubRelease | PackageVersionType.Commit; public virtual IEnumerable Prerequisites => - [ - PackagePrerequisite.Git, - PackagePrerequisite.Python310, - PackagePrerequisite.VcRedist, - PackagePrerequisite.VcBuildTools, - ]; + [PackagePrerequisite.Git, PackagePrerequisite.Python310, PackagePrerequisite.VcRedist]; public abstract Task GetUpdate(InstalledPackage installedPackage); diff --git a/StabilityMatrix.Core/Models/Packages/ComfyUI.cs b/StabilityMatrix.Core/Models/Packages/ComfyUI.cs index 4c5982aeb..4eba9569c 100644 --- a/StabilityMatrix.Core/Models/Packages/ComfyUI.cs +++ b/StabilityMatrix.Core/Models/Packages/ComfyUI.cs @@ -431,6 +431,49 @@ await StandardPipInstallProcessAsync( .ConfigureAwait(false); } + try + { + var sageVersion = await venvRunner.PipShow("sageattention").ConfigureAwait(false); + var torchVersion = await venvRunner.PipShow("torch").ConfigureAwait(false); + + if (torchVersion is not null && sageVersion is not null) + { + var version = torchVersion.Version; + var plusPos = version.IndexOf('+'); + var index = plusPos >= 0 ? version[(plusPos + 1)..] : string.Empty; + var versionWithoutIndex = plusPos >= 0 ? version[..plusPos] : version; + + if ( + !sageVersion.Version.Contains(index) || !sageVersion.Version.Contains(versionWithoutIndex) + ) + { + progress?.Report( + new ProgressReport(-1f, "Updating SageAttention...", isIndeterminate: true) + ); + + var step = new InstallSageAttentionStep( + downloadService, + prerequisiteHelper, + pyInstallationManager + ) + { + InstalledPackage = installedPackage, + IsBlackwellGpu = + SettingsManager.Settings.PreferredGpu?.IsBlackwellGpu() + ?? HardwareHelper.HasBlackwellGpu(), + WorkingDirectory = installLocation, + EnvironmentVariables = GetEnvVars(venvRunner.EnvironmentVariables), + }; + + await step.ExecuteAsync(progress).ConfigureAwait(false); + } + } + } + catch (Exception e) + { + Logger.Error(e, "Failed to verify/update SageAttention after installation"); + } + progress?.Report(new ProgressReport(1, "Install complete", isIndeterminate: false)); } diff --git a/StabilityMatrix.Core/Models/Packages/StableSwarm.cs b/StabilityMatrix.Core/Models/Packages/StableSwarm.cs index 4ba902a7a..af83af171 100644 --- a/StabilityMatrix.Core/Models/Packages/StableSwarm.cs +++ b/StabilityMatrix.Core/Models/Packages/StableSwarm.cs @@ -375,23 +375,30 @@ public override async Task RunPackage( ) { var portableGitBin = new DirectoryPath(PrerequisiteHelper.GitBinPath); + var dotnetDir = PrerequisiteHelper.DotnetDir; var aspEnvVars = new Dictionary { ["ASPNETCORE_ENVIRONMENT"] = "Production", ["ASPNETCORE_URLS"] = "http://*:7801", ["GIT"] = portableGitBin.JoinFile("git.exe"), + ["DOTNET_ROOT"] = dotnetDir.FullPath, }; - aspEnvVars.Update(settingsManager.Settings.EnvironmentVariables); if (aspEnvVars.TryGetValue("PATH", out var pathValue)) { - aspEnvVars["PATH"] = Compat.GetEnvPathWithExtensions(portableGitBin, pathValue); + aspEnvVars["PATH"] = Compat.GetEnvPathWithExtensions( + dotnetDir.FullPath, + portableGitBin, + pathValue + ); } else { - aspEnvVars["PATH"] = Compat.GetEnvPathWithExtensions(portableGitBin); + aspEnvVars["PATH"] = Compat.GetEnvPathWithExtensions(dotnetDir.FullPath, portableGitBin); } + aspEnvVars.Update(settingsManager.Settings.EnvironmentVariables); + void HandleConsoleOutput(ProcessOutput s) { onConsoleOutput?.Invoke(s); diff --git a/StabilityMatrix.Core/Python/UvManager.cs b/StabilityMatrix.Core/Python/UvManager.cs index db76a1f0a..8c7cd9ebc 100644 --- a/StabilityMatrix.Core/Python/UvManager.cs +++ b/StabilityMatrix.Core/Python/UvManager.cs @@ -108,7 +108,7 @@ public async Task> ListAvailablePythonsAsync( Compat.IsWindows ? "uv.exe" : "uv" ); - var args = new ProcessArgsBuilder("python", "list", "--output-format", "json"); + var args = new ProcessArgsBuilder("python", "list", "--managed-python", "--output-format", "json"); if (settingsManager.Settings.ShowAllAvailablePythonVersions) { args = args.AddArg("--all-versions"); diff --git a/StabilityMatrix.Core/Services/DownloadService.cs b/StabilityMatrix.Core/Services/DownloadService.cs index 5cf42d262..55426bac1 100644 --- a/StabilityMatrix.Core/Services/DownloadService.cs +++ b/StabilityMatrix.Core/Services/DownloadService.cs @@ -211,6 +211,14 @@ var delay in Backoff.DecorrelatedJitterBackoffV2(TimeSpan.FromMilliseconds(50), ) == true ) { + var responseContent = await noRedirectResponse + .Content.ReadAsStringAsync(cancellationToken) + .ConfigureAwait(false); + if (responseContent.Contains("The creator of this asset has disabled downloads")) + { + throw new CivitDownloadDisabledException(); + } + throw new CivitLoginRequiredException(); }