Browse Source

Merge branch 'dev' into inference-modules

pull/333/head
Ionite 1 year ago
parent
commit
cfd1d08033
No known key found for this signature in database
  1. 13
      CHANGELOG.md
  2. 3
      StabilityMatrix.Avalonia/App.axaml.cs
  3. 7
      StabilityMatrix.Avalonia/DesignData/DesignData.cs
  4. 6
      StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs
  5. 86
      StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs
  6. 151
      StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs
  7. 20
      StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs
  8. 4
      StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs
  9. 6
      StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs
  10. 17
      StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs
  11. 137
      StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml
  12. 128
      StabilityMatrix.Avalonia/Views/Dialogs/OneClickInstallDialog.axaml
  13. 5
      StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml
  14. 1
      StabilityMatrix.Core/Models/PackageModification/IPackageModificationRunner.cs
  15. 2
      StabilityMatrix.Core/Models/PackageModification/InstallPackageStep.cs
  16. 1
      StabilityMatrix.Core/Models/PackageModification/PackageModificationRunner.cs
  17. 6
      StabilityMatrix.Core/Models/Packages/A3WebUI.cs
  18. 2
      StabilityMatrix.Core/Models/Packages/BasePackage.cs
  19. 28
      StabilityMatrix.Core/Models/Packages/ComfyUI.cs
  20. 60
      StabilityMatrix.Core/Models/Packages/Fooocus.cs
  21. 247
      StabilityMatrix.Core/Models/Packages/StableDiffusionDirectMl.cs
  22. 23
      StabilityMatrix.Core/Models/Packages/StableDiffusionUx.cs
  23. 2
      StabilityMatrix.Core/Models/Packages/VladAutomatic.cs
  24. 3
      StabilityMatrix.Core/Models/Packages/VoltaML.cs
  25. 45
      StabilityMatrix.Core/Python/PyVenvRunner.cs

13
CHANGELOG.md

@ -7,14 +7,23 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2
## v2.6.0
### Added
- Added "Output Sharing" option for all packages in the three-dots menu on the Packages page
- Added **Output Sharing** option for all packages in the three-dots menu on the Packages page
- This will link the package's output folders to the relevant subfolders in the "Outputs" directory
- When a package only has a generic "outputs" folder, all generated images from that package will be linked to the "Outputs\Text2Img" folder when this option is enabled
- Added "Outputs" page for viewing generated images from any package, or the shared output folder
- Added **Outputs page** for viewing generated images from any package, or the shared output folder
- Added [Stable Diffusion WebUI/UX](https://github.com/anapnoe/stable-diffusion-webui-ux) package
- Added [Stable Diffusion WebUI-DirectML](https://github.com/lshqqytiger/stable-diffusion-webui-directml) package
- Added GPU compatibility badges to the installers
- Added filtering of "incompatible" packages (ones that do not support your GPU) to all installers
- This can be overridden by checking the new "Show All Packages" checkbox
- Added more launch options for Fooocus, such as the `--preset` option
### Changed
- If ComfyUI for Inference is chosen during the One-Click Installer, the Inference page will be opened after installation instead of the Launch page
- Changed all package installs & updates to use git commands instead of downloading zip files
- The One-Click Installer now uses the new progress dialog with console
- NVIDIA GPU users will be updated to use CUDA 12.1 for ComfyUI & Fooocus packages for a slight performance improvement
- Update will occur the next time the package is updated, or on a fresh install
- Note: CUDA 12.1 is only available on Maxwell (GTX 900 series) and newer GPUs
### Fixed
- Fixed crash when clicking Inference gallery image after the image is deleted externally in file explorer
- Fixed Inference popup Install button not working on One-Click Installer

3
StabilityMatrix.Avalonia/App.axaml.cs

@ -444,9 +444,10 @@ public sealed class App : Application
{
services.AddSingleton<BasePackage, A3WebUI>();
services.AddSingleton<BasePackage, StableDiffusionUx>();
services.AddSingleton<BasePackage, StableDiffusionDirectMl>();
services.AddSingleton<BasePackage, Fooocus>();
services.AddSingleton<BasePackage, InvokeAI>();
services.AddSingleton<BasePackage, ComfyUI>();
services.AddSingleton<BasePackage, Fooocus>();
services.AddSingleton<BasePackage, VladAutomatic>();
services.AddSingleton<BasePackage, VoltaML>();
services.AddSingleton<BasePackage, FooocusMre>();

7
StabilityMatrix.Avalonia/DesignData/DesignData.cs

@ -33,6 +33,7 @@ using StabilityMatrix.Core.Models.Api;
using StabilityMatrix.Core.Models.Api.Comfy;
using StabilityMatrix.Core.Models.Database;
using StabilityMatrix.Core.Models.PackageModification;
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
@ -175,9 +176,9 @@ public static class DesignData
LaunchOptionsViewModel.UpdateFilterCards();
InstallerViewModel = Services.GetRequiredService<InstallerViewModel>();
InstallerViewModel.AvailablePackages = packageFactory
.GetAllAvailablePackages()
.ToImmutableArray();
InstallerViewModel.AvailablePackages = new ObservableCollectionExtended<BasePackage>(
packageFactory.GetAllAvailablePackages()
);
InstallerViewModel.SelectedPackage = InstallerViewModel.AvailablePackages[0];
InstallerViewModel.ReleaseNotes = "## Release Notes\nThis is a test release note.";

6
StabilityMatrix.Avalonia/ViewModels/Base/ContentDialogProgressViewModelBase.cs

@ -1,10 +1,14 @@
using System;
using CommunityToolkit.Mvvm.ComponentModel;
using FluentAvalonia.UI.Controls;
namespace StabilityMatrix.Avalonia.ViewModels.Base;
public class ContentDialogProgressViewModelBase : ConsoleProgressViewModel
public partial class ContentDialogProgressViewModelBase : ConsoleProgressViewModel
{
[ObservableProperty]
private bool hideCloseButton;
public event EventHandler<ContentDialogResult>? PrimaryButtonClick;
public event EventHandler<ContentDialogResult>? SecondaryButtonClick;
public event EventHandler<ContentDialogResult>? CloseButtonClick;

86
StabilityMatrix.Avalonia/ViewModels/Dialogs/InstallerViewModel.cs

@ -36,6 +36,7 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private readonly ISettingsManager settingsManager;
private readonly IPackageFactory packageFactory;
private readonly IPyRunner pyRunner;
private readonly IDownloadService downloadService;
private readonly INotificationService notificationService;
@ -47,15 +48,15 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
[ObservableProperty]
private PackageVersion? selectedVersion;
[ObservableProperty]
private IReadOnlyList<BasePackage>? availablePackages;
[ObservableProperty]
private ObservableCollection<GitCommit>? availableCommits;
[ObservableProperty]
private ObservableCollection<PackageVersion>? availableVersions;
[ObservableProperty]
private ObservableCollection<BasePackage> availablePackages;
[ObservableProperty]
private GitCommit? selectedCommit;
@ -69,6 +70,9 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
[NotifyPropertyChangedFor(nameof(CanInstall))]
private bool showDuplicateWarning;
[ObservableProperty]
private bool showIncompatiblePackages;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanInstall))]
private string? installName;
@ -115,7 +119,6 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
public bool CanInstall =>
!string.IsNullOrWhiteSpace(InstallName) && !ShowDuplicateWarning && !IsLoading;
public ProgressViewModel InstallProgress { get; } = new();
public IEnumerable<IPackageStep> Steps { get; set; }
public InstallerViewModel(
@ -128,22 +131,25 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
)
{
this.settingsManager = settingsManager;
this.packageFactory = packageFactory;
this.pyRunner = pyRunner;
this.downloadService = downloadService;
this.notificationService = notificationService;
this.prerequisiteHelper = prerequisiteHelper;
// AvailablePackages and SelectedPackage
var filtered = packageFactory.GetAllAvailablePackages().Where(p => p.IsCompatible).ToList();
AvailablePackages = new ObservableCollection<BasePackage>(
packageFactory.GetAllAvailablePackages()
filtered.Any() ? filtered : packageFactory.GetAllAvailablePackages()
);
SelectedPackage = AvailablePackages[0];
ShowIncompatiblePackages = !filtered.Any();
}
public override void OnLoaded()
{
if (AvailablePackages == null)
return;
IsReleaseMode = !SelectedPackage.ShouldIgnoreReleases;
}
@ -309,6 +315,19 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
OnCloseButtonClick();
}
partial void OnShowIncompatiblePackagesChanged(bool value)
{
var filtered = packageFactory
.GetAllAvailablePackages()
.Where(p => ShowIncompatiblePackages || p.IsCompatible)
.ToList();
AvailablePackages = new ObservableCollection<BasePackage>(
filtered.Any() ? filtered : packageFactory.GetAllAvailablePackages()
);
SelectedPackage = AvailablePackages[0];
}
private void UpdateSelectedVersionToLatestMain()
{
if (AvailableVersions is null)
@ -370,41 +389,33 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
// When changing branch / release modes, refresh
// ReSharper disable once UnusedParameterInPartialMethod
partial void OnSelectedVersionTypeChanged(PackageVersionType value) =>
OnSelectedPackageChanged(SelectedPackage);
partial void OnSelectedPackageChanged(BasePackage value)
partial void OnSelectedVersionTypeChanged(PackageVersionType value)
{
IsLoading = true;
ReleaseNotes = string.Empty;
AvailableVersions?.Clear();
AvailableCommits?.Clear();
AvailableVersionTypes = SelectedPackage.ShouldIgnoreReleases
? PackageVersionType.Commit
: PackageVersionType.GithubRelease | PackageVersionType.Commit;
SelectedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod;
SelectedTorchVersion = SelectedPackage.GetRecommendedTorchVersion();
if (Design.IsDesignMode)
if (SelectedPackage is null || Design.IsDesignMode)
return;
Dispatcher.UIThread
.InvokeAsync(async () =>
{
Logger.Debug($"Release mode: {IsReleaseMode}");
var versionOptions = await value.GetAllVersionOptions();
var versionOptions = await SelectedPackage.GetAllVersionOptions();
AvailableVersions = IsReleaseMode
? new ObservableCollection<PackageVersion>(versionOptions.AvailableVersions)
: new ObservableCollection<PackageVersion>(versionOptions.AvailableBranches);
SelectedVersion = AvailableVersions.First(x => !x.IsPrerelease);
SelectedVersion = AvailableVersions?.FirstOrDefault(x => !x.IsPrerelease);
if (SelectedVersion is null)
return;
ReleaseNotes = SelectedVersion.ReleaseNotesMarkdown;
Logger.Debug($"Loaded release notes for {ReleaseNotes}");
if (!IsReleaseMode)
{
var commits = (await value.GetAllCommits(SelectedVersion.TagName))?.ToList();
var commits = (
await SelectedPackage.GetAllCommits(SelectedVersion.TagName)
)?.ToList();
if (commits is null || commits.Count == 0)
return;
@ -420,6 +431,29 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
.SafeFireAndForget();
}
partial void OnSelectedPackageChanged(BasePackage? value)
{
IsLoading = true;
ReleaseNotes = string.Empty;
AvailableVersions?.Clear();
AvailableCommits?.Clear();
if (value == null)
return;
AvailableVersionTypes = SelectedPackage.ShouldIgnoreReleases
? PackageVersionType.Commit
: PackageVersionType.GithubRelease | PackageVersionType.Commit;
IsReleaseMode = !SelectedPackage.ShouldIgnoreReleases;
SelectedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod;
SelectedTorchVersion = SelectedPackage.GetRecommendedTorchVersion();
SelectedVersionType = SelectedPackage.ShouldIgnoreReleases
? PackageVersionType.Commit
: PackageVersionType.GithubRelease;
OnSelectedVersionTypeChanged(SelectedVersionType);
}
partial void OnInstallNameChanged(string? value)
{
ShowDuplicateWarning = settingsManager.Settings.InstalledPackages.Any(
@ -430,7 +464,7 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
partial void OnSelectedVersionChanged(PackageVersion? value)
{
ReleaseNotes = value?.ReleaseNotesMarkdown ?? string.Empty;
if (value == null)
if (value == null || Design.IsDesignMode)
return;
SelectedCommit = null;

151
StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
@ -12,6 +13,7 @@ using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Factory;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.PackageModification;
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Python;
@ -19,14 +21,13 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels.Dialogs;
public partial class OneClickInstallViewModel : ViewModelBase
public partial class OneClickInstallViewModel : ContentDialogViewModelBase
{
private readonly ISettingsManager settingsManager;
private readonly IPackageFactory packageFactory;
private readonly IPrerequisiteHelper prerequisiteHelper;
private readonly ILogger<OneClickInstallViewModel> logger;
private readonly IPyRunner pyRunner;
private readonly ISharedFolders sharedFolders;
private readonly INavigationService navigationService;
private const string DefaultPackageName = "stable-diffusion-webui";
@ -45,6 +46,9 @@ public partial class OneClickInstallViewModel : ViewModelBase
[ObservableProperty]
private bool isIndeterminate;
[ObservableProperty]
private bool showIncompatiblePackages;
[ObservableProperty]
private ObservableCollection<BasePackage> allPackages;
@ -55,17 +59,16 @@ public partial class OneClickInstallViewModel : ViewModelBase
[NotifyPropertyChangedFor(nameof(IsProgressBarVisible))]
private int oneClickInstallProgress;
public bool IsProgressBarVisible => OneClickInstallProgress > 0 || IsIndeterminate;
private bool isInferenceInstall;
public bool IsProgressBarVisible => OneClickInstallProgress > 0 || IsIndeterminate;
public OneClickInstallViewModel(
ISettingsManager settingsManager,
IPackageFactory packageFactory,
IPrerequisiteHelper prerequisiteHelper,
ILogger<OneClickInstallViewModel> logger,
IPyRunner pyRunner,
ISharedFolders sharedFolders,
INavigationService navigationService
)
{
@ -74,14 +77,21 @@ public partial class OneClickInstallViewModel : ViewModelBase
this.prerequisiteHelper = prerequisiteHelper;
this.logger = logger;
this.pyRunner = pyRunner;
this.sharedFolders = sharedFolders;
this.navigationService = navigationService;
HeaderText = Resources.Text_WelcomeToStabilityMatrix;
SubHeaderText = Resources.Text_OneClickInstaller_SubHeader;
ShowInstallButton = true;
var filteredPackages = this.packageFactory
.GetAllAvailablePackages()
.Where(p => p is { OfferInOneClickInstaller: true, IsCompatible: true })
.ToList();
AllPackages = new ObservableCollection<BasePackage>(
this.packageFactory.GetAllAvailablePackages().Where(p => p.OfferInOneClickInstaller)
filteredPackages.Any()
? filteredPackages
: this.packageFactory.GetAllAvailablePackages()
);
SelectedPackage = AllPackages[0];
}
@ -115,35 +125,18 @@ public partial class OneClickInstallViewModel : ViewModelBase
private async Task DoInstall()
{
HeaderText = $"{Resources.Label_Installing} {SelectedPackage.DisplayName}";
var progressHandler = new Progress<ProgressReport>(progress =>
var steps = new List<IPackageStep>
{
SubHeaderText = $"{progress.Title} {progress.Percentage:N0}%";
IsIndeterminate = progress.IsIndeterminate;
OneClickInstallProgress = Convert.ToInt32(progress.Percentage);
});
await prerequisiteHelper.InstallAllIfNecessary(progressHandler);
SubHeaderText = Resources.Progress_InstallingPrerequisites;
IsIndeterminate = true;
if (!PyRunner.PipInstalled)
{
await pyRunner.SetupPip();
}
if (!PyRunner.VenvInstalled)
{
await pyRunner.InstallPackage("virtualenv");
}
IsIndeterminate = false;
var libraryDir = settingsManager.LibraryDir;
new SetPackageInstallingStep(settingsManager, SelectedPackage.Name),
new SetupPrerequisitesStep(prerequisiteHelper, pyRunner)
};
// get latest version & download & install
var installLocation = Path.Combine(libraryDir, "Packages", SelectedPackage.Name);
var installLocation = Path.Combine(
settingsManager.LibraryDir,
"Packages",
SelectedPackage.Name
);
var downloadVersion = new DownloadPackageVersionOptions { IsLatest = true };
var installedVersion = new InstalledPackageVersion();
@ -167,12 +160,29 @@ public partial class OneClickInstallViewModel : ViewModelBase
}
var torchVersion = SelectedPackage.GetRecommendedTorchVersion();
var recommendedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod;
var downloadStep = new DownloadPackageVersionStep(
SelectedPackage,
installLocation,
downloadVersion
);
steps.Add(downloadStep);
await DownloadPackage(installLocation, downloadVersion);
await InstallPackage(installLocation, torchVersion, downloadVersion);
var installStep = new InstallPackageStep(
SelectedPackage,
torchVersion,
downloadVersion,
installLocation
);
steps.Add(installStep);
var recommendedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod;
await SelectedPackage.SetupModelFolders(installLocation, recommendedSharedFolderMethod);
var setupModelFoldersStep = new SetupModelFoldersStep(
SelectedPackage,
recommendedSharedFolderMethod,
installLocation
);
steps.Add(setupModelFoldersStep);
var installedPackage = new InstalledPackage
{
@ -186,15 +196,23 @@ public partial class OneClickInstallViewModel : ViewModelBase
PreferredTorchVersion = torchVersion,
PreferredSharedFolderMethod = recommendedSharedFolderMethod
};
await using var st = settingsManager.BeginTransaction();
st.Settings.InstalledPackages.Add(installedPackage);
st.Settings.ActiveInstalledPackageId = installedPackage.Id;
EventManager.Instance.OnInstalledPackagesChanged();
HeaderText = Resources.Progress_InstallationComplete;
SubSubHeaderText = string.Empty;
OneClickInstallProgress = 100;
var addInstalledPackageStep = new AddInstalledPackageStep(
settingsManager,
installedPackage
);
steps.Add(addInstalledPackageStep);
var runner = new PackageModificationRunner
{
ShowDialogOnStart = true,
HideCloseButton = true,
};
EventManager.Instance.OnPackageInstallProgressAdded(runner);
await runner.ExecuteSteps(steps);
EventManager.Instance.OnInstalledPackagesChanged();
HeaderText = $"{SelectedPackage.DisplayName} installed successfully";
for (var i = 3; i > 0; i--)
{
SubHeaderText = $"{Resources.Text_ProceedingToLaunchPage} ({i}s)";
@ -209,45 +227,16 @@ public partial class OneClickInstallViewModel : ViewModelBase
}
}
private async Task DownloadPackage(
string installLocation,
DownloadPackageVersionOptions versionOptions
)
partial void OnShowIncompatiblePackagesChanged(bool value)
{
SubHeaderText = Resources.Progress_DownloadingPackage;
var filteredPackages = packageFactory
.GetAllAvailablePackages()
.Where(p => p.OfferInOneClickInstaller && (ShowIncompatiblePackages || p.IsCompatible))
.ToList();
var progress = new Progress<ProgressReport>(progress =>
{
IsIndeterminate = progress.IsIndeterminate;
OneClickInstallProgress = Convert.ToInt32(progress.Percentage);
EventManager.Instance.OnGlobalProgressChanged(OneClickInstallProgress);
});
await SelectedPackage.DownloadPackage(installLocation, versionOptions, progress);
SubHeaderText = Resources.Progress_DownloadComplete;
OneClickInstallProgress = 100;
}
private async Task InstallPackage(
string installLocation,
TorchVersion torchVersion,
DownloadPackageVersionOptions versionOptions
)
{
var progress = new Progress<ProgressReport>(progress =>
{
SubHeaderText = Resources.Progress_InstallingPackageRequirements;
IsIndeterminate = progress.IsIndeterminate;
OneClickInstallProgress = Convert.ToInt32(progress.Percentage);
EventManager.Instance.OnGlobalProgressChanged(OneClickInstallProgress);
});
await SelectedPackage.InstallPackage(
installLocation,
torchVersion,
versionOptions,
progress,
(output) => SubSubHeaderText = output.Text
AllPackages = new ObservableCollection<BasePackage>(
filteredPackages.Any() ? filteredPackages : packageFactory.GetAllAvailablePackages()
);
SelectedPackage = AllPackages[0];
}
}

20
StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs

@ -43,6 +43,7 @@ namespace StabilityMatrix.Avalonia.ViewModels;
public partial class OutputsPageViewModel : PageViewModelBase
{
private readonly ISettingsManager settingsManager;
private readonly IPackageFactory packageFactory;
private readonly INotificationService notificationService;
private readonly INavigationService navigationService;
private readonly ILogger<OutputsPageViewModel> logger;
@ -76,7 +77,8 @@ public partial class OutputsPageViewModel : PageViewModelBase
[ObservableProperty]
private string searchQuery;
public bool CanShowOutputTypes => SelectedCategory.Name.Equals("Shared Output Folder");
public bool CanShowOutputTypes =>
SelectedCategory?.Name?.Equals("Shared Output Folder") ?? false;
public string NumImagesSelected =>
NumItemsSelected == 1
@ -92,6 +94,7 @@ public partial class OutputsPageViewModel : PageViewModelBase
)
{
this.settingsManager = settingsManager;
this.packageFactory = packageFactory;
this.notificationService = notificationService;
this.navigationService = navigationService;
this.logger = logger;
@ -131,10 +134,17 @@ public partial class OutputsPageViewModel : PageViewModelBase
{
NumItemsSelected = Outputs.Count(o => o.IsSelected);
});
}
if (!settingsManager.IsLibraryDirSet || Design.IsDesignMode)
public override void OnLoaded()
{
if (Design.IsDesignMode)
return;
if (!settingsManager.IsLibraryDirSet)
return;
Directory.CreateDirectory(settingsManager.ImagesDirectory);
var packageCategories = settingsManager.Settings.InstalledPackages
.Where(x => !x.UseSharedOutputFolder)
.Select(p =>
@ -164,12 +174,6 @@ public partial class OutputsPageViewModel : PageViewModelBase
SelectedCategory = Categories.First();
SelectedOutputType = SharedOutputType.All;
SearchQuery = string.Empty;
}
public override void OnLoaded()
{
if (Design.IsDesignMode)
return;
var path =
CanShowOutputTypes && SelectedOutputType != SharedOutputType.All

4
StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using AsyncAwaitBestPractices;
using Avalonia.Controls;
using Avalonia.Controls.Notifications;
using Avalonia.Controls.Primitives;
using DynamicData;
using DynamicData.Binding;
using FluentAvalonia.UI.Controls;
@ -122,17 +123,18 @@ public partial class PackageManagerViewModel : PageViewModelBase
public async Task ShowInstallDialog(BasePackage? selectedPackage = null)
{
var viewModel = dialogFactory.Get<InstallerViewModel>();
viewModel.AvailablePackages = packageFactory.GetAllAvailablePackages().ToImmutableArray();
viewModel.SelectedPackage = selectedPackage ?? viewModel.AvailablePackages[0];
var dialog = new BetterContentDialog
{
MaxDialogWidth = 900,
MinDialogWidth = 900,
FullSizeDesired = true,
DefaultButton = ContentDialogButton.Close,
IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false,
IsFooterVisible = false,
ContentVerticalScrollBarVisibility = ScrollBarVisibility.Disabled,
Content = new InstallerDialog { DataContext = viewModel }
};

6
StabilityMatrix.Avalonia/ViewModels/Progress/PackageInstallProgressItemViewModel.cs

@ -17,7 +17,10 @@ public class PackageInstallProgressItemViewModel : ProgressItemViewModelBase
private readonly IPackageModificationRunner packageModificationRunner;
private BetterContentDialog? dialog;
public PackageInstallProgressItemViewModel(IPackageModificationRunner packageModificationRunner)
public PackageInstallProgressItemViewModel(
IPackageModificationRunner packageModificationRunner,
bool hideCloseButton = false
)
{
this.packageModificationRunner = packageModificationRunner;
Id = packageModificationRunner.Id;
@ -25,6 +28,7 @@ public class PackageInstallProgressItemViewModel : ProgressItemViewModelBase
Progress.Value = packageModificationRunner.CurrentProgress.Percentage;
Progress.Text = packageModificationRunner.ConsoleOutput.LastOrDefault();
Progress.IsIndeterminate = packageModificationRunner.CurrentProgress.IsIndeterminate;
Progress.HideCloseButton = hideCloseButton;
Progress.Console.StartUpdates();

17
StabilityMatrix.Avalonia/ViewModels/Progress/ProgressManagerViewModel.cs

@ -120,19 +120,22 @@ public partial class ProgressManagerViewModel : PageViewModelBase
}
}
private async Task AddPackageInstall(IPackageModificationRunner packageModificationRunner)
private Task AddPackageInstall(IPackageModificationRunner packageModificationRunner)
{
if (ProgressItems.Any(vm => vm.Id == packageModificationRunner.Id))
{
return;
return Task.CompletedTask;
}
var vm = new PackageInstallProgressItemViewModel(packageModificationRunner);
var vm = new PackageInstallProgressItemViewModel(
packageModificationRunner,
packageModificationRunner.HideCloseButton
);
ProgressItems.Add(vm);
if (packageModificationRunner.ShowDialogOnStart)
{
await vm.ShowProgressDialog();
}
return packageModificationRunner.ShowDialogOnStart
? vm.ShowProgressDialog()
: Task.CompletedTask;
}
private void ShowFailedNotification(string title, string message)

137
StabilityMatrix.Avalonia/Views/Dialogs/InstallerDialog.axaml

@ -16,12 +16,12 @@
x:Class="StabilityMatrix.Avalonia.Views.Dialogs.InstallerDialog">
<Grid MaxHeight="900"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto"
RowDefinitions="Auto,Auto,Auto,2*,*"
ColumnDefinitions="*,Auto">
<!-- Title and image -->
<Grid Grid.Row="0" Grid.Column="0"
Margin="16, 0, 0, 0"
RowDefinitions="Auto, Auto, Auto, Auto, Auto">
RowDefinitions="Auto, Auto, Auto, Auto, *, Auto">
<TextBlock Grid.Row="0" Text="{Binding SelectedPackage.DisplayName}"
FontSize="24"
Margin="0, 16, 0, 4" />
@ -33,18 +33,123 @@
TextWrapping="Wrap"
Foreground="{DynamicResource ThemeRedColor}"
IsVisible="{Binding SelectedPackage.Disclaimer, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
<ItemsRepeater Grid.Row="3"
Margin="0, 0, 0, 8"
ItemsSource="{Binding SelectedPackage.AvailableTorchVersions}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Horizontal" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="{x:Type models:TorchVersion}">
<controls:Card
Tag="{Binding }"
HorizontalAlignment="Left"
Padding="4"
Margin="0,8,8,0"
VerticalAlignment="Top">
<controls:Card.Styles>
<Style Selector="controls|Card[Tag=Cuda]">
<Setter Property="Background" Value="{DynamicResource ThemeGreenColorTransparent}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeGreenColorTransparent}" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="NVIDIA"
TextAlignment="Center"
VerticalAlignment="Center" />
</Template>
</Setter>
</Style>
<Style Selector="controls|Card[Tag=Rocm]">
<Setter Property="Background" Value="{DynamicResource ThemeDarkRedColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkRedColor}" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="AMD (Linux)"
TextAlignment="Center"
VerticalAlignment="Center"
ToolTip.Tip="For AMD GPUs that support ROCm on Linux" />
</Template>
</Setter>
</Style>
<Style Selector="controls|Card[Tag=DirectMl]">
<Setter Property="Background" Value="{DynamicResource ThemeDarkBlueColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkBlueColor}" />
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="DirectML"
TextAlignment="Center"
VerticalAlignment="Center"
ToolTip.Tip="For any DirectX compatible GPU on Windows" />
</Template>
</Setter>
</Style>
<Style Selector="controls|Card[Tag=Mps]">
<Setter Property="Background" Value="White" />
<Setter Property="BorderBrush" Value="White" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="macOS"
TextAlignment="Center"
Foreground="Black"
VerticalAlignment="Center" />
</Template>
</Setter>
</Style>
<Style Selector="controls|Card[Tag=Cpu]">
<Setter Property="Background" Value="{DynamicResource ThemeBlueGreyColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBlueGreyColor}" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="CPU"
TextAlignment="Center"
VerticalAlignment="Center" />
</Template>
</Setter>
</Style>
</controls:Card.Styles>
</controls:Card>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
<controls:BetterAdvancedImage Grid.Row="4"
Margin="0, 12,0,0"
Source="{Binding SelectedPackage.PreviewImageUri}"
HorizontalAlignment="Center"
MaxHeight="300"
HorizontalAlignment="Left"
MaxHeight="250"
MaxWidth="600"
Stretch="UniformToFill" />
</Grid>
<Grid Grid.Column="0" Grid.Row="2"
Grid.ColumnSpan="2"
Margin="16,0,16,0"
Margin="16,16,16,0"
RowDefinitions="Auto, Auto, Auto, Auto"
HorizontalAlignment="Center"
ColumnDefinitions="Auto, Auto, Auto, Auto">
@ -61,7 +166,7 @@
<StackPanel Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4"
Orientation="Horizontal"
Margin="8"
Margin="8,8,8,0"
IsVisible="{Binding ShowDuplicateWarning}">
<ui:SymbolIcon
Foreground="{DynamicResource ThemeRedColor}"
@ -97,7 +202,7 @@
<StackPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4"
HorizontalAlignment="Center"
Orientation="Horizontal" Margin="0,8,0,8"
Orientation="Horizontal" Margin="0,0,0,8"
IsVisible="{Binding IsReleaseModeAvailable}">
<ToggleButton
Content="{x:Static lang:Resources.Label_Releases}"
@ -186,7 +291,7 @@
<!-- Install Button -->
<UniformGrid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2"
Columns="2"
Margin="0,32,0,0"
Margin="0,16,0,0"
HorizontalAlignment="Center">
<Button
Content="{x:Static lang:Resources.Action_Cancel}"
@ -206,14 +311,24 @@
Padding="16, 8, 16, 8" />
</UniformGrid>
<CheckBox Grid.Column="0"
Grid.Row="4"
VerticalAlignment="Bottom"
Margin="16,0,0,16"
Content="Show All Packages"
IsChecked="{Binding ShowIncompatiblePackages}"
ToolTip.Tip="Enabling &quot;Show All Packages&quot; will include packages that may not be compatible with your system and may run very slowly as a result."/>
<!-- Available Packages -->
<ScrollViewer Grid.Column="1" Grid.Row="0"
Margin="8, 16"
MaxHeight="400"
VerticalScrollBarVisibility="Visible">
VerticalScrollBarVisibility="Auto">
<ListBox
ItemsSource="{Binding AvailablePackages}"
SelectedItem="{Binding SelectedPackage}">
SelectedItem="{Binding SelectedPackage}"
CornerRadius="8">
<ListBox.Template>
<ControlTemplate>
<ItemsPresenter />

128
StabilityMatrix.Avalonia/Views/Dialogs/OneClickInstallDialog.axaml

@ -8,13 +8,14 @@
xmlns:packages="clr-namespace:StabilityMatrix.Core.Models.Packages;assembly=StabilityMatrix.Core"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core"
mc:Ignorable="d" d:DesignWidth="700" d:DesignHeight="700"
x:DataType="dialogs:OneClickInstallViewModel"
d:DataContext="{x:Static designData:DesignData.OneClickInstallViewModel}"
x:Class="StabilityMatrix.Avalonia.Views.Dialogs.OneClickInstallDialog">
<Grid MaxHeight="900"
RowDefinitions="Auto,Auto,Auto,Auto,Auto"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,*"
ColumnDefinitions="*,Auto">
<Grid.Transitions>
@ -71,16 +72,122 @@
Margin="16, 16, 0, 4"/>
<TextBlock Text="{Binding SelectedPackage.Blurb}"
TextWrapping="Wrap"
Margin="16, 0, 0, 8"/>
Margin="16, 0, 0, 4"/>
<TextBlock Text="{Binding SelectedPackage.Disclaimer}"
Padding="4"
Margin="16, 0, 0, 0"
TextWrapping="Wrap"
Foreground="OrangeRed"
IsVisible="{Binding SelectedPackage.Disclaimer, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
<ItemsRepeater Margin="16, 0, 0, 0"
ItemsSource="{Binding SelectedPackage.AvailableTorchVersions}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Horizontal" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="{x:Type models:TorchVersion}">
<controls:Card
Tag="{Binding }"
HorizontalAlignment="Left"
Padding="4"
Margin="0,8,8,0"
VerticalAlignment="Top">
<controls:Card.Styles>
<Style Selector="controls|Card[Tag=Cuda]">
<Setter Property="Background" Value="{DynamicResource ThemeGreenColorTransparent}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeGreenColorTransparent}" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="NVIDIA"
TextAlignment="Center"
VerticalAlignment="Center" />
</Template>
</Setter>
</Style>
<Style Selector="controls|Card[Tag=Rocm]">
<Setter Property="Background" Value="{DynamicResource ThemeDarkRedColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkRedColor}" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="AMD (Linux)"
TextAlignment="Center"
VerticalAlignment="Center"
ToolTip.Tip="For AMD GPUs that support ROCm on Linux" />
</Template>
</Setter>
</Style>
<Style Selector="controls|Card[Tag=DirectMl]">
<Setter Property="Background" Value="{DynamicResource ThemeDarkBlueColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeDarkBlueColor}" />
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="DirectML"
TextAlignment="Center"
VerticalAlignment="Center"
ToolTip.Tip="For any DirectX compatible GPU on Windows" />
</Template>
</Setter>
</Style>
<Style Selector="controls|Card[Tag=Mps]">
<Setter Property="Background" Value="White" />
<Setter Property="BorderBrush" Value="White" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="macOS"
TextAlignment="Center"
Foreground="Black"
VerticalAlignment="Center" />
</Template>
</Setter>
</Style>
<Style Selector="controls|Card[Tag=Cpu]">
<Setter Property="Background" Value="{DynamicResource ThemeBlueGreyColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBlueGreyColor}" />
<Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
</Style>
<Setter Property="Content">
<Template>
<TextBlock
FontWeight="Medium"
HorizontalAlignment="Center"
Text="CPU"
TextAlignment="Center"
VerticalAlignment="Center" />
</Template>
</Setter>
</Style>
</controls:Card.Styles>
</controls:Card>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
<controls:BetterAdvancedImage Source="{Binding SelectedPackage.PreviewImageUri}"
HorizontalAlignment="Left"
MaxHeight="300"
Height="300"
Width="400"
Stretch="UniformToFill"
Margin="16, 8"/>
</StackPanel>
@ -117,7 +224,7 @@
FontSize="32"
HorizontalAlignment="Center"
Classes="success"
Margin="16"
Margin="8"
Padding="16, 8, 16, 8" />
<Button Command="{Binding ToggleAdvancedModeCommand}"
Content="{x:Static lang:Resources.Label_SkipSetup}"
@ -126,11 +233,20 @@
Padding="16, 8, 16, 8"/>
</StackPanel>
<CheckBox Grid.Column="1"
Grid.Row="2"
VerticalAlignment="Bottom"
Content="Show All Packages"
IsVisible="{Binding ShowInstallButton}"
HorizontalAlignment="Center"
IsChecked="{Binding ShowIncompatiblePackages}"
ToolTip.Tip="Enabling &quot;Show All Packages&quot; will include packages that may not be compatible with your system and may run very slowly as a result."/>
<ScrollViewer Grid.Column="1" Grid.Row="1"
MaxHeight="400"
VerticalScrollBarVisibility="Visible"
Margin="0, 16">
Margin="0, 8">
<ListBox
Name="PackagesListBox"
Margin="8,0"

5
StabilityMatrix.Avalonia/Views/Dialogs/PackageModificationDialog.axaml

@ -12,7 +12,8 @@
x:Class="StabilityMatrix.Avalonia.Views.Dialogs.PackageModificationDialog">
<Grid Margin="8" RowDefinitions="Auto, Auto, Auto, *, Auto, Auto">
<TextBlock Grid.Row="0" Text="{Binding Text}"
FontSize="16"
FontSize="24"
FontWeight="Light"
Margin="4"
TextWrapping="WrapWithOverflow"
TextAlignment="Center"
@ -47,11 +48,13 @@
<CheckBox Grid.Row="4" IsChecked="{Binding CloseWhenFinished}"
HorizontalAlignment="Center"
IsVisible="{Binding !HideCloseButton}"
Margin="4" Content="{x:Static lang:Resources.Label_CloseDialogWhenFinished}"/>
<Button Grid.Row="5" Content="{x:Static lang:Resources.Action_Close}"
FontSize="20"
HorizontalAlignment="Center"
IsVisible="{Binding !HideCloseButton}"
Command="{Binding OnCloseButtonClick}"/>
</Grid>
</controls:UserControlBase>

1
StabilityMatrix.Core/Models/PackageModification/IPackageModificationRunner.cs

@ -12,6 +12,7 @@ public interface IPackageModificationRunner
List<string> ConsoleOutput { get; }
Guid Id { get; }
bool ShowDialogOnStart { get; init; }
bool HideCloseButton { get; init; }
string? ModificationCompleteMessage { get; init; }
bool Failed { get; set; }
}

2
StabilityMatrix.Core/Models/PackageModification/InstallPackageStep.cs

@ -36,5 +36,5 @@ public class InstallPackageStep : IPackageStep
.ConfigureAwait(false);
}
public string ProgressTitle => "Installing package...";
public string ProgressTitle => $"Installing {package.DisplayName}...";
}

1
StabilityMatrix.Core/Models/PackageModification/PackageModificationRunner.cs

@ -54,6 +54,7 @@ public class PackageModificationRunner : IPackageModificationRunner
IsRunning = false;
}
public bool HideCloseButton { get; init; }
public string? ModificationCompleteMessage { get; init; }
public bool ShowDialogOnStart { get; init; }

6
StabilityMatrix.Core/Models/Packages/A3WebUI.cs

@ -168,7 +168,7 @@ public class A3WebUI : BaseGitPackage
new[] { SharedFolderMethod.Symlink, SharedFolderMethod.None };
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl, TorchVersion.Rocm };
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.Rocm };
public override async Task<string> GetLatestVersion()
{
@ -203,10 +203,6 @@ public class A3WebUI : BaseGitPackage
case TorchVersion.Rocm:
await InstallRocmTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.DirectMl:
await InstallDirectMlTorch(venvRunner, progress, onConsoleOutput)
.ConfigureAwait(false);
break;
default:
throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null);
}

2
StabilityMatrix.Core/Models/Packages/BasePackage.cs

@ -102,6 +102,8 @@ public abstract class BasePackage
public abstract IEnumerable<TorchVersion> AvailableTorchVersions { get; }
public virtual bool IsCompatible => GetRecommendedTorchVersion() != TorchVersion.Cpu;
public virtual TorchVersion GetRecommendedTorchVersion()
{
// if there's only one AvailableTorchVersion, return that

28
StabilityMatrix.Core/Models/Packages/ComfyUI.cs

@ -146,7 +146,14 @@ public class ComfyUI : BaseGitPackage
public override Task<string> GetLatestVersion() => Task.FromResult("master");
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl, TorchVersion.Rocm };
new[]
{
TorchVersion.Cpu,
TorchVersion.Cuda,
TorchVersion.DirectMl,
TorchVersion.Rocm,
TorchVersion.Mps
};
public override async Task InstallPackage(
string installLocation,
@ -169,13 +176,24 @@ public class ComfyUI : BaseGitPackage
await InstallCpuTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.Cuda:
await InstallCudaTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
await venvRunner
.PipInstall(PyVenvRunner.TorchPipInstallArgsCuda121, onConsoleOutput)
.ConfigureAwait(false);
await venvRunner
.PipInstall("xformers==0.0.22.post4 --upgrade")
.ConfigureAwait(false);
break;
case TorchVersion.DirectMl:
await venvRunner
.PipInstall(PyVenvRunner.TorchPipInstallArgsDirectML, onConsoleOutput)
.ConfigureAwait(false);
break;
case TorchVersion.Rocm:
await InstallRocmTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.DirectMl:
await InstallDirectMlTorch(venvRunner, progress, onConsoleOutput)
case TorchVersion.Mps:
await venvRunner
.PipInstall(PyVenvRunner.TorchPipInstallArgsNightlyCpu, onConsoleOutput)
.ConfigureAwait(false);
break;
default:
@ -445,7 +463,7 @@ public class ComfyUI : BaseGitPackage
await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false);
await venvRunner
.PipInstall(PyVenvRunner.TorchPipInstallArgsRocm542, onConsoleOutput)
.PipInstall(PyVenvRunner.TorchPipInstallArgsRocm56, onConsoleOutput)
.ConfigureAwait(false);
}

60
StabilityMatrix.Core/Models/Packages/Fooocus.cs

@ -38,6 +38,12 @@ public class Fooocus : BaseGitPackage
public override List<LaunchOptionDefinition> LaunchOptions =>
new()
{
new LaunchOptionDefinition
{
Name = "Preset",
Type = LaunchOptionType.Bool,
Options = { "--preset anime", "--preset realistic" }
},
new LaunchOptionDefinition
{
Name = "Port",
@ -59,6 +65,49 @@ public class Fooocus : BaseGitPackage
Description = "Set the listen interface",
Options = { "--listen" }
},
new LaunchOptionDefinition
{
Name = "Output Directory",
Type = LaunchOptionType.String,
Description = "Override the output directory",
Options = { "--output-directory" }
},
new()
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper
.IterGpuInfo()
.Select(gpu => gpu.MemoryLevel)
.Max() switch
{
Level.Low => "--lowvram",
Level.Medium => "--normalvram",
_ => null
},
Options = { "--highvram", "--normalvram", "--lowvram", "--novram" }
},
new LaunchOptionDefinition
{
Name = "Use DirectML",
Type = LaunchOptionType.Bool,
Description = "Use pytorch with DirectML support",
InitialValue = HardwareHelper.PreferDirectML(),
Options = { "--directml" }
},
new LaunchOptionDefinition
{
Name = "Disable Xformers",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu(),
Options = { "--disable-xformers" }
},
new LaunchOptionDefinition
{
Name = "Auto-Launch",
Type = LaunchOptionType.Bool,
Options = { "--auto-launch" }
},
LaunchOptionDefinition.Extras
};
@ -87,7 +136,7 @@ public class Fooocus : BaseGitPackage
new() { [SharedOutputType.Text2Img] = new[] { "outputs" } };
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.Rocm };
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl, TorchVersion.Rocm };
public override Task<string> GetLatestVersion() => Task.FromResult("main");
@ -113,7 +162,7 @@ public class Fooocus : BaseGitPackage
switch (torchVersion)
{
case TorchVersion.Cuda:
torchVersionStr = "cu118";
torchVersionStr = "cu121";
break;
case TorchVersion.Rocm:
torchVersionStr = "rocm5.4.2";
@ -126,11 +175,16 @@ public class Fooocus : BaseGitPackage
await venvRunner
.PipInstall(
$"torch==2.0.1 torchvision==0.15.2 --extra-index-url https://download.pytorch.org/whl/{torchVersionStr}",
$"torch==2.1.0 torchvision==0.16.0 --extra-index-url https://download.pytorch.org/whl/{torchVersionStr}",
onConsoleOutput
)
.ConfigureAwait(false);
if (torchVersion == TorchVersion.Cuda)
{
await venvRunner.PipInstall("xformers==0.0.22.post4 --upgrade").ConfigureAwait(false);
}
var requirements = new FilePath(installLocation, "requirements_versions.txt");
await venvRunner
.PipInstallFromRequirements(requirements, onConsoleOutput, excludes: "torch")

247
StabilityMatrix.Core/Models/Packages/StableDiffusionDirectMl.cs

@ -0,0 +1,247 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using NLog;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Cache;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
public class StableDiffusionDirectMl : BaseGitPackage
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public override string Name => "stable-diffusion-webui-directml";
public override string DisplayName { get; set; } = "Stable Diffusion Web UI";
public override string Author => "lshqqytiger";
public override string LicenseType => "AGPL-3.0";
public override string LicenseUrl =>
"https://github.com/lshqqytiger/stable-diffusion-webui-directml/blob/master/LICENSE.txt";
public override string Blurb =>
"A fork of Automatic1111's Stable Diffusion WebUI with DirectML support";
public override string LaunchCommand => "launch.py";
public override Uri PreviewImageUri =>
new(
"https://github.com/lshqqytiger/stable-diffusion-webui-directml/raw/master/screenshot.png"
);
public override SharedFolderMethod RecommendedSharedFolderMethod => SharedFolderMethod.Symlink;
public StableDiffusionDirectMl(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders =>
new()
{
[SharedFolderType.StableDiffusion] = new[] { "models/Stable-diffusion" },
[SharedFolderType.ESRGAN] = new[] { "models/ESRGAN" },
[SharedFolderType.RealESRGAN] = new[] { "models/RealESRGAN" },
[SharedFolderType.SwinIR] = new[] { "models/SwinIR" },
[SharedFolderType.Lora] = new[] { "models/Lora" },
[SharedFolderType.LyCORIS] = new[] { "models/LyCORIS" },
[SharedFolderType.ApproxVAE] = new[] { "models/VAE-approx" },
[SharedFolderType.VAE] = new[] { "models/VAE" },
[SharedFolderType.DeepDanbooru] = new[] { "models/deepbooru" },
[SharedFolderType.Karlo] = new[] { "models/karlo" },
[SharedFolderType.TextualInversion] = new[] { "embeddings" },
[SharedFolderType.Hypernetwork] = new[] { "models/hypernetworks" },
[SharedFolderType.ControlNet] = new[] { "models/ControlNet" },
[SharedFolderType.Codeformer] = new[] { "models/Codeformer" },
[SharedFolderType.LDSR] = new[] { "models/LDSR" },
[SharedFolderType.AfterDetailer] = new[] { "models/adetailer" }
};
public override Dictionary<SharedOutputType, IReadOnlyList<string>>? SharedOutputFolders =>
new()
{
[SharedOutputType.Extras] = new[] { "outputs/extras-images" },
[SharedOutputType.Saved] = new[] { "log/images" },
[SharedOutputType.Img2Img] = new[] { "outputs/img2img-images" },
[SharedOutputType.Text2Img] = new[] { "outputs/txt2img-images" },
[SharedOutputType.Img2ImgGrids] = new[] { "outputs/img2img-grids" },
[SharedOutputType.Text2ImgGrids] = new[] { "outputs/txt2img-grids" }
};
[SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeNotEvident")]
public override List<LaunchOptionDefinition> LaunchOptions =>
new()
{
new()
{
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "localhost",
Options = new() { "--server-name" }
},
new()
{
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "7860",
Options = new() { "--port" }
},
new()
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper
.IterGpuInfo()
.Select(gpu => gpu.MemoryLevel)
.Max() switch
{
Level.Low => "--lowvram",
Level.Medium => "--medvram",
_ => null
},
Options = new() { "--lowvram", "--medvram", "--medvram-sdxl" }
},
new()
{
Name = "Xformers",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.HasNvidiaGpu(),
Options = new() { "--xformers" }
},
new()
{
Name = "API",
Type = LaunchOptionType.Bool,
InitialValue = true,
Options = new() { "--api" }
},
new()
{
Name = "Auto Launch Web UI",
Type = LaunchOptionType.Bool,
InitialValue = false,
Options = new() { "--autolaunch" }
},
new()
{
Name = "Skip Torch CUDA Check",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu(),
Options = new() { "--skip-torch-cuda-test" }
},
new()
{
Name = "Skip Python Version Check",
Type = LaunchOptionType.Bool,
InitialValue = true,
Options = new() { "--skip-python-version-check" }
},
new()
{
Name = "No Half",
Type = LaunchOptionType.Bool,
Description = "Do not switch the model to 16-bit floats",
InitialValue = HardwareHelper.HasAmdGpu(),
Options = new() { "--no-half" }
},
new()
{
Name = "Skip SD Model Download",
Type = LaunchOptionType.Bool,
InitialValue = false,
Options = new() { "--no-download-sd-model" }
},
new()
{
Name = "Skip Install",
Type = LaunchOptionType.Bool,
Options = new() { "--skip-install" }
},
LaunchOptionDefinition.Extras
};
public override IEnumerable<SharedFolderMethod> AvailableSharedFolderMethods =>
new[] { SharedFolderMethod.Symlink, SharedFolderMethod.None };
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
new[] { TorchVersion.Cpu, TorchVersion.DirectMl };
public override Task<string> GetLatestVersion() => Task.FromResult("master");
public override bool ShouldIgnoreReleases => true;
public override string OutputFolderName => "outputs";
public override async Task InstallPackage(
string installLocation,
TorchVersion torchVersion,
DownloadPackageVersionOptions versionOptions,
IProgress<ProgressReport>? progress = null,
Action<ProcessOutput>? onConsoleOutput = null
)
{
progress?.Report(new ProgressReport(-1f, "Setting up venv", isIndeterminate: true));
// Setup venv
await using var venvRunner = new PyVenvRunner(Path.Combine(installLocation, "venv"));
venvRunner.WorkingDirectory = installLocation;
await venvRunner.Setup(true, onConsoleOutput).ConfigureAwait(false);
switch (torchVersion)
{
case TorchVersion.DirectMl:
await InstallDirectMlTorch(venvRunner, progress, onConsoleOutput)
.ConfigureAwait(false);
break;
case TorchVersion.Cpu:
await InstallCpuTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
}
// Install requirements file
progress?.Report(
new ProgressReport(-1f, "Installing Package Requirements", isIndeterminate: true)
);
Logger.Info("Installing requirements_versions.txt");
var requirements = new FilePath(installLocation, "requirements_versions.txt");
await venvRunner
.PipInstallFromRequirements(requirements, onConsoleOutput, excludes: "torch")
.ConfigureAwait(false);
progress?.Report(new ProgressReport(1f, "Install complete", isIndeterminate: false));
}
public override async Task RunPackage(
string installedPackagePath,
string command,
string arguments,
Action<ProcessOutput>? onConsoleOutput
)
{
await SetupVenv(installedPackagePath).ConfigureAwait(false);
void HandleConsoleOutput(ProcessOutput s)
{
onConsoleOutput?.Invoke(s);
if (!s.Text.Contains("Running on", StringComparison.OrdinalIgnoreCase))
return;
var regex = new Regex(@"(https?:\/\/)([^:\s]+):(\d+)");
var match = regex.Match(s.Text);
if (!match.Success)
return;
WebUrl = match.Value;
OnStartupComplete(WebUrl);
}
var args = $"\"{Path.Combine(installedPackagePath, command)}\" {arguments}";
VenvRunner.RunDetached(args.TrimEnd(), HandleConsoleOutput, OnExit);
}
}

23
StabilityMatrix.Core/Models/Packages/StableDiffusionUx.cs

@ -169,7 +169,7 @@ public class StableDiffusionUx : BaseGitPackage
new[] { SharedFolderMethod.Symlink, SharedFolderMethod.None };
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl, TorchVersion.Rocm };
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.Rocm };
public override Task<string> GetLatestVersion() => Task.FromResult("master");
@ -202,12 +202,6 @@ public class StableDiffusionUx : BaseGitPackage
case TorchVersion.Rocm:
await InstallRocmTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.DirectMl:
await InstallDirectMlTorch(venvRunner, progress, onConsoleOutput)
.ConfigureAwait(false);
break;
default:
throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null);
}
// Install requirements file
@ -221,21 +215,6 @@ public class StableDiffusionUx : BaseGitPackage
.PipInstallFromRequirements(requirements, onConsoleOutput, excludes: "torch")
.ConfigureAwait(false);
progress?.Report(
new ProgressReport(1f, "Installing Package Requirements", isIndeterminate: false)
);
progress?.Report(new ProgressReport(-1f, "Updating configuration", isIndeterminate: true));
// // Create and add {"show_progress_type": "TAESD"} to config.json
// // Only add if the file doesn't exist
// var configPath = Path.Combine(installLocation, "config.json");
// if (!File.Exists(configPath))
// {
// var config = new JsonObject { { "show_progress_type", "TAESD" } };
// await File.WriteAllTextAsync(configPath, config.ToString()).ConfigureAwait(false);
// }
progress?.Report(new ProgressReport(1f, "Install complete", isIndeterminate: false));
}

2
StabilityMatrix.Core/Models/Packages/VladAutomatic.cs

@ -34,7 +34,7 @@ public class VladAutomatic : BaseGitPackage
public override SharedFolderMethod RecommendedSharedFolderMethod => SharedFolderMethod.Symlink;
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
new[] { TorchVersion.Cpu, TorchVersion.Rocm, TorchVersion.DirectMl, TorchVersion.Cuda };
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl, TorchVersion.Rocm };
public VladAutomatic(
IGithubApiCache githubApi,

3
StabilityMatrix.Core/Models/Packages/VoltaML.cs

@ -57,7 +57,8 @@ public class VoltaML : BaseGitPackage
public override SharedFolderMethod RecommendedSharedFolderMethod => SharedFolderMethod.Symlink;
public override IEnumerable<TorchVersion> AvailableTorchVersions => new[] { TorchVersion.None };
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl, TorchVersion.Mps };
public override IEnumerable<SharedFolderMethod> AvailableSharedFolderMethods =>
new[] { SharedFolderMethod.Symlink, SharedFolderMethod.None };

45
StabilityMatrix.Core/Python/PyVenvRunner.cs

@ -23,6 +23,8 @@ public class PyVenvRunner : IDisposable, IAsyncDisposable
public const string TorchPipInstallArgsCuda =
$"{TorchPipInstallArgs} --extra-index-url https://download.pytorch.org/whl/cu118";
public const string TorchPipInstallArgsCuda121 =
"torch torchvision --extra-index-url https://download.pytorch.org/whl/cu121";
public const string TorchPipInstallArgsCpu = TorchPipInstallArgs;
public const string TorchPipInstallArgsDirectML = "torch-directml";
@ -30,8 +32,11 @@ public class PyVenvRunner : IDisposable, IAsyncDisposable
$"{TorchPipInstallArgs} --extra-index-url https://download.pytorch.org/whl/rocm5.1.1";
public const string TorchPipInstallArgsRocm542 =
$"{TorchPipInstallArgs} --extra-index-url https://download.pytorch.org/whl/rocm5.4.2";
public const string TorchPipInstallArgsRocmNightly56 =
$"--pre {TorchPipInstallArgs} --index-url https://download.pytorch.org/whl/nightly/rocm5.6";
public const string TorchPipInstallArgsRocm56 =
$"{TorchPipInstallArgs} --index-url https://download.pytorch.org/whl/rocm5.6";
public const string TorchPipInstallArgsNightlyCpu =
"--pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cpu";
/// <summary>
/// Relative path to the site-packages folder from the venv root.
@ -243,6 +248,42 @@ public class PyVenvRunner : IDisposable, IAsyncDisposable
}
}
/// <summary>
/// Run a pip uninstall command. Waits for the process to exit.
/// workingDirectory defaults to RootPath.
/// </summary>
public async Task PipUninstall(string args, Action<ProcessOutput>? outputDataReceived = null)
{
if (!File.Exists(PipPath))
{
throw new FileNotFoundException("pip not found", PipPath);
}
// Record output for errors
var output = new StringBuilder();
var outputAction = new Action<ProcessOutput>(s =>
{
Logger.Debug($"Pip output: {s.Text}");
// Record to output
output.Append(s.Text);
// Forward to callback
outputDataReceived?.Invoke(s);
});
SetPyvenvCfg(PyRunner.PythonDir);
RunDetached($"-m pip uninstall -y {args}", outputAction);
await Process.WaitForExitAsync().ConfigureAwait(false);
// Check return code
if (Process.ExitCode != 0)
{
throw new ProcessException(
$"pip install failed with code {Process.ExitCode}: {output.ToString().ToRepr()}"
);
}
}
/// <summary>
/// Pip install from a requirements.txt file.
/// </summary>

Loading…
Cancel
Save