Browse Source

Merge pull request #333 from LykosAI/main

Release v2.7.2
pull/336/head
JT 11 months ago committed by GitHub
parent
commit
f3766a6b08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      CHANGELOG.md
  2. 20
      StabilityMatrix.Avalonia/Helpers/UriHandler.cs
  3. 8
      StabilityMatrix.Avalonia/ViewModels/Inference/FreeUCardViewModel.cs
  4. 2
      StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs
  5. 84
      StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs
  6. 36
      StabilityMatrix.Core/Models/FileInterfaces/FileSystemPath.cs
  7. 92
      StabilityMatrix.Core/Models/Packages/A3WebUI.cs
  8. 236
      StabilityMatrix.Core/Models/Packages/ComfyUI.cs
  9. 56
      StabilityMatrix.Core/Models/Packages/Fooocus.cs
  10. 97
      StabilityMatrix.Core/Models/Packages/RuinedFooocus.cs
  11. 61
      StabilityMatrix.Core/Processes/ProcessArgsBuilder.cs
  12. 33
      StabilityMatrix.Core/Python/PipInstallArgs.cs
  13. 87
      StabilityMatrix.Core/Python/PipPackageSpecifier.cs

11
CHANGELOG.md

@ -5,6 +5,17 @@ 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.7.2
### Changed
- Changed Symlink shared folder link targets for Automatic1111 and ComfyUI. From `ControlNet -> models/controlnet` to `ControlNet -> models/controlnet/ControlNet` and `T2IAdapter -> models/controlnet/T2IAdapter`.
- Changed FreeU defaults to match recommended SD1.5 defaults
- Changed default denoise strength from 1.0 to 0.7
### Fixed
- Fixed ControlNet / T2IAdapter shared folder links for Automatic1111 conflicting with each other
- Fixed URIScheme registration errors on Linux
- Fixed RuinedFooocus missing output folder on startup
- Fixed incorrect Fooocus VRAM launch arguments
## v2.7.1
### Added
- Added Turkish UI language option, thanks to Progesor for the translation

20
StabilityMatrix.Avalonia/Helpers/UriHandler.cs

@ -56,8 +56,6 @@ public class UriHandler
Environment.Exit(0);
}
public void Callback() { }
public void RegisterUriScheme()
{
if (Compat.IsWindows)
@ -66,7 +64,17 @@ public class UriHandler
}
else
{
RegisterUriSchemeUnix();
// Try to register on unix but ignore errors
// Library does not support some distros
try
{
RegisterUriSchemeUnix();
}
catch (Exception e)
{
Debug.WriteLine(e);
Console.WriteLine(e);
}
}
}
@ -92,11 +100,7 @@ public class UriHandler
private void RegisterUriSchemeUnix()
{
var service = URISchemeServiceFactory.GetURISchemeSerivce(
Scheme,
Description,
Compat.AppCurrentPath.FullPath
);
var service = URISchemeServiceFactory.GetURISchemeSerivce(Scheme, Description, Compat.AppCurrentPath.FullPath);
service.Set();
}
}

8
StabilityMatrix.Avalonia/ViewModels/Inference/FreeUCardViewModel.cs

@ -14,21 +14,25 @@ public partial class FreeUCardViewModel : LoadableViewModelBase
public const string ModuleKey = "FreeU";
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[Range(0D, 10D)]
private double b1 = 1.1;
private double b1 = 1.5;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[Range(0D, 10D)]
private double b2 = 1.2;
private double b2 = 1.6;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[Range(0D, 10D)]
private double s1 = 0.9;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[Range(0D, 10D)]
private double s2 = 0.2;

2
StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs

@ -42,7 +42,7 @@ public partial class SamplerCardViewModel : LoadableViewModelBase, IParametersLo
private bool isDenoiseStrengthEnabled;
[ObservableProperty]
private double denoiseStrength = 1;
private double denoiseStrength = 0.7f;
[ObservableProperty]
[property: Category("Settings")]

84
StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs

@ -89,9 +89,7 @@ public partial class MainWindow : AppWindowBase
{
base.OnApplyTemplate(e);
navigationService.SetFrame(
FrameView ?? throw new NullReferenceException("Frame not found")
);
navigationService.SetFrame(FrameView ?? throw new NullReferenceException("Frame not found"));
}
protected override void OnOpened(EventArgs e)
@ -134,16 +132,15 @@ public partial class MainWindow : AppWindowBase
return;
// Navigate to first page
Dispatcher.UIThread.Post(
() =>
navigationService.NavigateTo(
vm.Pages[0],
new BetterSlideNavigationTransition
{
Effect = SlideNavigationTransitionEffect.FromBottom
}
)
);
Dispatcher
.UIThread
.Post(
() =>
navigationService.NavigateTo(
vm.Pages[0],
new BetterSlideNavigationTransition { Effect = SlideNavigationTransitionEffect.FromBottom }
)
);
// Check show update teaching tip
if (vm.UpdateViewModel.IsUpdateAvailable)
@ -167,27 +164,28 @@ public partial class MainWindow : AppWindowBase
{
var mainViewModel = (MainWindowViewModel)DataContext!;
mainViewModel.SelectedCategory = mainViewModel.Pages
mainViewModel.SelectedCategory = mainViewModel
.Pages
.Concat(mainViewModel.FooterPages)
.FirstOrDefault(x => x.GetType() == e.ViewModelType);
}
private void OnUpdateAvailable(object? sender, UpdateInfo? updateInfo)
{
Dispatcher.UIThread.Post(() =>
{
var vm = DataContext as MainWindowViewModel;
if (vm!.ShouldShowUpdateAvailableTeachingTip(updateInfo))
Dispatcher
.UIThread
.Post(() =>
{
var target = this.FindControl<NavigationViewItem>("FooterUpdateItem")!;
var tip = this.FindControl<TeachingTip>("UpdateAvailableTeachingTip")!;
if (DataContext is MainWindowViewModel vm && vm.ShouldShowUpdateAvailableTeachingTip(updateInfo))
{
var target = this.FindControl<NavigationViewItem>("FooterUpdateItem")!;
var tip = this.FindControl<TeachingTip>("UpdateAvailableTeachingTip")!;
tip.Target = target;
tip.Subtitle = $"{Compat.AppVersion.ToDisplayString()} -> {updateInfo.Version}";
tip.IsOpen = true;
}
});
tip.Target = target;
tip.Subtitle = $"{Compat.AppVersion.ToDisplayString()} -> {updateInfo.Version}";
tip.IsOpen = true;
}
});
}
public void SetDefaultFonts()
@ -284,16 +282,18 @@ public partial class MainWindow : AppWindowBase
private void OnImageLoadFailed(object? sender, ImageLoadFailedEventArgs e)
{
Dispatcher.UIThread.Post(() =>
{
var fileName = Path.GetFileName(e.Url);
var displayName = string.IsNullOrEmpty(fileName) ? e.Url : fileName;
notificationService.ShowPersistent(
"Failed to load image",
$"Could not load '{displayName}'\n({e.Exception.Message})",
NotificationType.Warning
);
});
Dispatcher
.UIThread
.Post(() =>
{
var fileName = Path.GetFileName(e.Url);
var displayName = string.IsNullOrEmpty(fileName) ? e.Url : fileName;
notificationService.ShowPersistent(
"Failed to load image",
$"Could not load '{displayName}'\n({e.Exception.Message})",
NotificationType.Warning
);
});
}
private void TryEnableMicaEffect()
@ -308,11 +308,7 @@ public partial class MainWindow : AppWindowBase
if (ActualThemeVariant == ThemeVariant.Dark)
{
var color = this.TryFindResource(
"SolidBackgroundFillColorBase",
ThemeVariant.Dark,
out var value
)
var color = this.TryFindResource("SolidBackgroundFillColorBase", ThemeVariant.Dark, out var value)
? (Color2)(Color)value!
: new Color2(30, 31, 34);
@ -323,11 +319,7 @@ public partial class MainWindow : AppWindowBase
else if (ActualThemeVariant == ThemeVariant.Light)
{
// Similar effect here
var color = this.TryFindResource(
"SolidBackgroundFillColorBase",
ThemeVariant.Light,
out var value
)
var color = this.TryFindResource("SolidBackgroundFillColorBase", ThemeVariant.Light, out var value)
? (Color2)(Color)value!
: new Color2(243, 243, 243);

36
StabilityMatrix.Core/Models/FileInterfaces/FileSystemPath.cs

@ -1,6 +1,6 @@
namespace StabilityMatrix.Core.Models.FileInterfaces;
public class FileSystemPath : IEquatable<FileSystemPath>, IEquatable<string>
public class FileSystemPath : IEquatable<FileSystemPath>, IEquatable<string>, IFormattable
{
public string FullPath { get; }
@ -8,27 +8,42 @@ public class FileSystemPath : IEquatable<FileSystemPath>, IEquatable<string>
{
FullPath = path;
}
protected FileSystemPath(FileSystemPath path) : this(path.FullPath)
protected FileSystemPath(FileSystemPath path)
: this(path.FullPath) { }
protected FileSystemPath(params string[] paths)
: this(Path.Combine(paths)) { }
public override string ToString()
{
return FullPath;
}
protected FileSystemPath(params string[] paths) : this(Path.Combine(paths))
/// <inheritdoc />
string IFormattable.ToString(string? format, IFormatProvider? formatProvider)
{
return ToString(format, formatProvider);
}
public override string ToString()
/// <summary>
/// Overridable IFormattable.ToString method.
/// By default, returns <see cref="FullPath"/>.
/// </summary>
protected virtual string ToString(string? format, IFormatProvider? formatProvider)
{
return FullPath;
}
public bool Equals(FileSystemPath? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return FullPath == other.FullPath;
}
public bool Equals(string? other)
{
return string.Equals(FullPath, other);
@ -48,8 +63,9 @@ public class FileSystemPath : IEquatable<FileSystemPath>, IEquatable<string>
{
return FullPath.GetHashCode();
}
// Implicit conversions to and from string
public static implicit operator string(FileSystemPath path) => path.FullPath;
public static implicit operator FileSystemPath(string path) => new(path);
}

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

@ -56,12 +56,14 @@ public class A3WebUI(
[SharedFolderType.Karlo] = new[] { "models/karlo" },
[SharedFolderType.TextualInversion] = new[] { "embeddings" },
[SharedFolderType.Hypernetwork] = new[] { "models/hypernetworks" },
[SharedFolderType.ControlNet] = new[] { "models/ControlNet" },
[SharedFolderType.ControlNet] = new[] { "models/controlnet/ControlNet" },
[SharedFolderType.Codeformer] = new[] { "models/Codeformer" },
[SharedFolderType.LDSR] = new[] { "models/LDSR" },
[SharedFolderType.AfterDetailer] = new[] { "models/adetailer" },
[SharedFolderType.T2IAdapter] = new[] { "models/controlnet" },
[SharedFolderType.IpAdapter] = new[] { "models/ipadapter" }
[SharedFolderType.T2IAdapter] = new[] { "models/controlnet/T2IAdapter" },
[SharedFolderType.IpAdapter] = new[] { "models/controlnet/IpAdapter" },
[SharedFolderType.InvokeIpAdapters15] = new[] { "models/controlnet/DiffusersIpAdapters" },
[SharedFolderType.InvokeIpAdaptersXl] = new[] { "models/controlnet/DiffusersIpAdaptersXL" }
};
public override Dictionary<SharedOutputType, IReadOnlyList<string>>? SharedOutputFolders =>
@ -183,39 +185,44 @@ public class A3WebUI(
)
{
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)
var venvRunner = await SetupVenv(installLocation, forceRecreate: true).ConfigureAwait(false);
await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing requirements...", isIndeterminate: true));
var requirements = new FilePath(installLocation, "requirements_versions.txt");
var pipArgs = new PipInstallArgs()
.WithTorch("==2.0.1")
.WithTorchVision("==0.15.2")
.WithTorchExtraIndex(
torchVersion switch
{
TorchVersion.Cpu => "cpu",
TorchVersion.Cuda => "cu118",
TorchVersion.Rocm => "rocm5.1.1",
_ => throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null)
}
)
.WithParsedFromRequirementsTxt(
await requirements.ReadAllTextAsync().ConfigureAwait(false),
excludePattern: "torch"
);
if (torchVersion == TorchVersion.Cuda)
{
case TorchVersion.Cpu:
await InstallCpuTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.Cuda:
await InstallCudaTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.Rocm:
await InstallRocmTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
default:
throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null);
pipArgs = pipArgs.WithXFormers("==0.0.20");
}
// v1.6.0 needs a httpx qualifier to fix a gradio issue
if (versionOptions.VersionTag?.Contains("1.6.0") ?? false)
{
await venvRunner.PipInstall("httpx==0.24.1", onConsoleOutput);
pipArgs = pipArgs.AddArg("httpx==0.24.1");
}
// 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);
await venvRunner.PipInstall(pipArgs, onConsoleOutput).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Updating configuration", isIndeterminate: true));
@ -261,21 +268,22 @@ public class A3WebUI(
VenvRunner.RunDetached(args.TrimEnd(), HandleConsoleOutput, OnExit);
}
private async Task InstallRocmTorch(
PyVenvRunner venvRunner,
IProgress<ProgressReport>? progress = null,
Action<ProcessOutput>? onConsoleOutput = null
)
/// <inheritdoc />
public override async Task SetupModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod)
{
progress?.Report(new ProgressReport(-1f, "Installing PyTorch for ROCm", isIndeterminate: true));
await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false);
// Migration for `controlnet` -> `controlnet/ControlNet` and `controlnet/T2IAdapter`
// If the original link exists, delete it first
if (installDirectory.JoinDir("models/controlnet") is { IsSymbolicLink: true } controlnetOldLink)
{
Logger.Info("Migration: Removing old controlnet link {Path}", controlnetOldLink);
await controlnetOldLink.DeleteAsync(false).ConfigureAwait(false);
}
await venvRunner
.PipInstall(
new PipInstallArgs().WithTorch("==2.0.1").WithTorchVision().WithTorchExtraIndex("rocm5.1.1"),
onConsoleOutput
)
.ConfigureAwait(false);
// Resume base setup
await base.SetupModelFolders(installDirectory, sharedFolderMethod).ConfigureAwait(false);
}
/// <inheritdoc />
public override Task UpdateModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod) =>
SetupModelFolders(installDirectory, sharedFolderMethod);
}

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

@ -10,6 +10,7 @@ using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
using YamlDotNet.Core;
using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
@ -53,12 +54,12 @@ public class ComfyUI(
[SharedFolderType.TextualInversion] = new[] { "models/embeddings" },
[SharedFolderType.VAE] = new[] { "models/vae" },
[SharedFolderType.ApproxVAE] = new[] { "models/vae_approx" },
[SharedFolderType.ControlNet] = new[] { "models/controlnet" },
[SharedFolderType.ControlNet] = new[] { "models/controlnet/ControlNet" },
[SharedFolderType.GLIGEN] = new[] { "models/gligen" },
[SharedFolderType.ESRGAN] = new[] { "models/upscale_models" },
[SharedFolderType.Hypernetwork] = new[] { "models/hypernetworks" },
[SharedFolderType.IpAdapter] = new[] { "models/ipadapter" },
[SharedFolderType.T2IAdapter] = new[] { "models/controlnet" },
[SharedFolderType.T2IAdapter] = new[] { "models/controlnet/T2IAdapter" },
};
public override Dictionary<SharedOutputType, IReadOnlyList<string>>? SharedOutputFolders =>
@ -155,80 +156,48 @@ public class ComfyUI(
venvRunner.WorkingDirectory = installLocation;
await venvRunner.Setup(true, onConsoleOutput).ConfigureAwait(false);
// Install torch / xformers based on gpu info
switch (torchVersion)
await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing Package Requirements...", isIndeterminate: true));
var pipArgs = new PipInstallArgs();
pipArgs = torchVersion switch
{
case TorchVersion.Cpu:
await InstallCpuTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.Cuda:
await venvRunner
.PipInstall(
new PipInstallArgs()
.WithTorch("~=2.1.0")
.WithTorchVision()
.WithXFormers("==0.0.22.post4")
.AddArg("--upgrade")
.WithTorchExtraIndex("cu121"),
onConsoleOutput
)
.ConfigureAwait(false);
break;
case TorchVersion.DirectMl:
await venvRunner
.PipInstall(new PipInstallArgs().WithTorchDirectML(), onConsoleOutput)
.ConfigureAwait(false);
break;
case TorchVersion.Rocm:
await InstallRocmTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.Mps:
await venvRunner
.PipInstall(
new PipInstallArgs()
.AddArg("--pre")
.WithTorch()
.WithTorchVision()
.WithTorchExtraIndex("nightly/cpu"),
onConsoleOutput
TorchVersion.DirectMl => pipArgs.WithTorchDirectML(),
TorchVersion.Mps
=> pipArgs.AddArg("--pre").WithTorch().WithTorchVision().WithTorchExtraIndex("nightly/cpu"),
_
=> pipArgs
.AddArg("--upgrade")
.WithTorch("~=2.1.0")
.WithTorchVision()
.WithTorchExtraIndex(
torchVersion switch
{
TorchVersion.Cpu => "cpu",
TorchVersion.Cuda => "cu121",
TorchVersion.Rocm => "rocm5.6",
_ => throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null)
}
)
.ConfigureAwait(false);
break;
default:
throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null);
}
};
// Install requirements file (skip torch)
progress?.Report(new ProgressReport(-1, "Installing Package Requirements", isIndeterminate: true));
if (torchVersion == TorchVersion.Cuda)
{
pipArgs = pipArgs.WithXFormers("==0.0.22.post4");
}
var requirementsFile = new FilePath(installLocation, "requirements.txt");
var requirements = new FilePath(installLocation, "requirements.txt");
await venvRunner
.PipInstallFromRequirements(requirementsFile, onConsoleOutput, excludes: "torch")
.ConfigureAwait(false);
pipArgs = pipArgs.WithParsedFromRequirementsTxt(
await requirements.ReadAllTextAsync().ConfigureAwait(false),
excludePattern: "torch"
);
progress?.Report(new ProgressReport(1, "Installing Package Requirements", isIndeterminate: false));
}
await venvRunner.PipInstall(pipArgs, onConsoleOutput).ConfigureAwait(false);
private async Task AutoDetectAndInstallTorch(PyVenvRunner venvRunner, IProgress<ProgressReport>? progress = null)
{
var gpus = HardwareHelper.IterGpuInfo().ToList();
if (gpus.Any(g => g.IsNvidia))
{
await InstallCudaTorch(venvRunner, progress).ConfigureAwait(false);
}
else if (HardwareHelper.PreferRocm())
{
await InstallRocmTorch(venvRunner, progress).ConfigureAwait(false);
}
else if (HardwareHelper.PreferDirectML())
{
await InstallDirectMlTorch(venvRunner, progress).ConfigureAwait(false);
}
else
{
await InstallCpuTorch(venvRunner, progress).ConfigureAwait(false);
}
progress?.Report(new ProgressReport(1, "Installed Package Requirements", isIndeterminate: false));
}
public override async Task RunPackage(
@ -267,26 +236,55 @@ public class ComfyUI(
}
}
public override Task SetupModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod)
public override Task SetupModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod) =>
sharedFolderMethod switch
{
SharedFolderMethod.Symlink => SetupModelFoldersSymlink(installDirectory),
SharedFolderMethod.Configuration => SetupModelFoldersConfig(installDirectory),
SharedFolderMethod.None => Task.CompletedTask,
_ => throw new ArgumentOutOfRangeException(nameof(sharedFolderMethod), sharedFolderMethod, null)
};
public override Task UpdateModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod) =>
SetupModelFolders(installDirectory, sharedFolderMethod);
public override Task RemoveModelFolderLinks(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod)
{
return sharedFolderMethod switch
{
SharedFolderMethod.Symlink => base.RemoveModelFolderLinks(installDirectory, sharedFolderMethod),
SharedFolderMethod.Configuration => RemoveConfigSection(installDirectory),
SharedFolderMethod.None => Task.CompletedTask,
_ => throw new ArgumentOutOfRangeException(nameof(sharedFolderMethod), sharedFolderMethod, null)
};
}
private async Task SetupModelFoldersSymlink(DirectoryPath installDirectory)
{
switch (sharedFolderMethod)
// Migration for `controlnet` -> `controlnet/ControlNet` and `controlnet/T2IAdapter`
// If the original link exists, delete it first
if (installDirectory.JoinDir("models/controlnet") is { IsSymbolicLink: true } controlnetOldLink)
{
case SharedFolderMethod.None:
return Task.CompletedTask;
case SharedFolderMethod.Symlink:
return base.SetupModelFolders(installDirectory, sharedFolderMethod);
Logger.Info("Migration: Removing old controlnet link {Path}", controlnetOldLink);
await controlnetOldLink.DeleteAsync(false).ConfigureAwait(false);
}
var extraPathsYamlPath = installDirectory + "extra_model_paths.yaml";
// Resume base setup
await base.SetupModelFolders(installDirectory, SharedFolderMethod.Symlink).ConfigureAwait(false);
}
private async Task SetupModelFoldersConfig(DirectoryPath installDirectory)
{
var extraPathsYamlPath = installDirectory.JoinFile("extra_model_paths.yaml");
var modelsDir = SettingsManager.ModelsDirectory;
var exists = File.Exists(extraPathsYamlPath);
if (!exists)
if (!extraPathsYamlPath.Exists)
{
Logger.Info("Creating extra_model_paths.yaml");
File.WriteAllText(extraPathsYamlPath, string.Empty);
extraPathsYamlPath.Create();
}
var yaml = File.ReadAllText(extraPathsYamlPath);
var yaml = await extraPathsYamlPath.ReadAllTextAsync().ConfigureAwait(false);
using var sr = new StringReader(yaml);
var yamlStream = new YamlStream();
yamlStream.Load(sr);
@ -307,7 +305,7 @@ public class ComfyUI(
if (stabilityMatrixNode.Key != null)
{
if (stabilityMatrixNode.Value is not YamlMappingNode nodeValue)
return Task.CompletedTask;
return;
nodeValue.Children["checkpoints"] = Path.Combine(modelsDir, "StableDiffusion");
nodeValue.Children["vae"] = Path.Combine(modelsDir, "VAE");
@ -319,7 +317,11 @@ public class ComfyUI(
+ $"{Path.Combine(modelsDir, "SwinIR")}";
nodeValue.Children["embeddings"] = Path.Combine(modelsDir, "TextualInversion");
nodeValue.Children["hypernetworks"] = Path.Combine(modelsDir, "Hypernetwork");
nodeValue.Children["controlnet"] = Path.Combine(modelsDir, "ControlNet");
nodeValue.Children["controlnet"] = string.Join(
'\n',
Path.Combine(modelsDir, "ControlNet"),
Path.Combine(modelsDir, "T2IAdapter")
);
nodeValue.Children["clip"] = Path.Combine(modelsDir, "CLIP");
nodeValue.Children["diffusers"] = Path.Combine(modelsDir, "Diffusers");
nodeValue.Children["gligen"] = Path.Combine(modelsDir, "GLIGEN");
@ -340,7 +342,10 @@ public class ComfyUI(
},
{ "embeddings", Path.Combine(modelsDir, "TextualInversion") },
{ "hypernetworks", Path.Combine(modelsDir, "Hypernetwork") },
{ "controlnet", Path.Combine(modelsDir, "ControlNet") },
{
"controlnet",
string.Join('\n', Path.Combine(modelsDir, "ControlNet"), Path.Combine(modelsDir, "T2IAdapter"))
},
{ "clip", Path.Combine(modelsDir, "CLIP") },
{ "diffusers", Path.Combine(modelsDir, "Diffusers") },
{ "gligen", Path.Combine(modelsDir, "GLIGEN") },
@ -357,82 +362,45 @@ public class ComfyUI(
newRootNode.Children.Add(stabilityMatrixNode);
var serializer = new SerializerBuilder().WithNamingConvention(UnderscoredNamingConvention.Instance).Build();
var yamlData = serializer.Serialize(newRootNode);
File.WriteAllText(extraPathsYamlPath, yamlData);
var serializer = new SerializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.WithDefaultScalarStyle(ScalarStyle.Literal)
.Build();
return Task.CompletedTask;
var yamlData = serializer.Serialize(newRootNode);
await extraPathsYamlPath.WriteAllTextAsync(yamlData).ConfigureAwait(false);
}
public override Task UpdateModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod) =>
sharedFolderMethod switch
{
SharedFolderMethod.Symlink => base.UpdateModelFolders(installDirectory, sharedFolderMethod),
SharedFolderMethod.Configuration => SetupModelFolders(installDirectory, sharedFolderMethod),
SharedFolderMethod.None => Task.CompletedTask,
_ => Task.CompletedTask
};
public override Task RemoveModelFolderLinks(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod)
private static async Task RemoveConfigSection(DirectoryPath installDirectory)
{
return sharedFolderMethod switch
{
SharedFolderMethod.Configuration => RemoveConfigSection(installDirectory),
SharedFolderMethod.None => Task.CompletedTask,
SharedFolderMethod.Symlink => base.RemoveModelFolderLinks(installDirectory, sharedFolderMethod),
_ => Task.CompletedTask
};
}
var extraPathsYamlPath = installDirectory.JoinFile("extra_model_paths.yaml");
private Task RemoveConfigSection(string installDirectory)
{
var extraPathsYamlPath = Path.Combine(installDirectory, "extra_model_paths.yaml");
var exists = File.Exists(extraPathsYamlPath);
if (!exists)
if (!extraPathsYamlPath.Exists)
{
return Task.CompletedTask;
return;
}
var yaml = File.ReadAllText(extraPathsYamlPath);
var yaml = await extraPathsYamlPath.ReadAllTextAsync().ConfigureAwait(false);
using var sr = new StringReader(yaml);
var yamlStream = new YamlStream();
yamlStream.Load(sr);
if (!yamlStream.Documents.Any())
{
return Task.CompletedTask;
return;
}
var root = yamlStream.Documents[0].RootNode;
if (root is not YamlMappingNode mappingNode)
{
return Task.CompletedTask;
return;
}
mappingNode.Children.Remove("stability_matrix");
var serializer = new SerializerBuilder().WithNamingConvention(UnderscoredNamingConvention.Instance).Build();
var yamlData = serializer.Serialize(mappingNode);
File.WriteAllText(extraPathsYamlPath, yamlData);
return Task.CompletedTask;
}
private async Task InstallRocmTorch(
PyVenvRunner venvRunner,
IProgress<ProgressReport>? progress = null,
Action<ProcessOutput>? onConsoleOutput = null
)
{
progress?.Report(new ProgressReport(-1f, "Installing PyTorch for ROCm", isIndeterminate: true));
await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false);
await venvRunner
.PipInstall(
new PipInstallArgs().WithTorch("==2.0.1").WithTorchVision().WithTorchExtraIndex("rocm5.6"),
onConsoleOutput
)
.ConfigureAwait(false);
await extraPathsYamlPath.WriteAllTextAsync(yamlData).ConfigureAwait(false);
}
}

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

@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text.RegularExpressions;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
@ -76,11 +77,11 @@ public class Fooocus(
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.IterGpuInfo().Select(gpu => gpu.MemoryLevel).Max() switch
{
MemoryLevel.Low => "--lowvram",
MemoryLevel.Medium => "--normalvram",
MemoryLevel.Low => "--always-low-vram",
MemoryLevel.Medium => "--always-normal-vram",
_ => null
},
Options = { "--highvram", "--normalvram", "--lowvram", "--novram" }
Options = { "--always-high-vram", "--always-normal-vram", "--always-low-vram", "--always-no-vram" }
},
new LaunchOptionDefinition
{
@ -152,39 +153,38 @@ public class Fooocus(
{
var venvRunner = await SetupVenv(installLocation, forceRecreate: true).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing torch...", isIndeterminate: true));
progress?.Report(new ProgressReport(-1f, "Installing requirements...", isIndeterminate: true));
var pipArgs = new PipInstallArgs();
if (torchVersion == TorchVersion.DirectMl)
{
await venvRunner
.PipInstall(new PipInstallArgs().WithTorchDirectML(), onConsoleOutput)
.ConfigureAwait(false);
pipArgs = pipArgs.WithTorchDirectML();
}
else
{
var extraIndex = torchVersion switch
{
TorchVersion.Cpu => "cpu",
TorchVersion.Cuda => "cu121",
TorchVersion.Rocm => "rocm5.6",
_ => throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null)
};
await venvRunner
.PipInstall(
new PipInstallArgs()
.WithTorch("==2.1.0")
.WithTorchVision("==0.16.0")
.WithTorchExtraIndex(extraIndex),
onConsoleOutput
)
.ConfigureAwait(false);
pipArgs = pipArgs
.WithTorch("==2.1.0")
.WithTorchVision("==0.16.0")
.WithTorchExtraIndex(
torchVersion switch
{
TorchVersion.Cpu => "cpu",
TorchVersion.Cuda => "cu121",
TorchVersion.Rocm => "rocm5.6",
_ => throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null)
}
);
}
var requirements = new FilePath(installLocation, "requirements_versions.txt");
await venvRunner
.PipInstallFromRequirements(requirements, onConsoleOutput, excludes: "torch")
.ConfigureAwait(false);
pipArgs = pipArgs.WithParsedFromRequirementsTxt(
await requirements.ReadAllTextAsync().ConfigureAwait(false),
excludePattern: "torch"
);
await venvRunner.PipInstall(pipArgs, onConsoleOutput).ConfigureAwait(false);
}
public override async Task RunPackage(

97
StabilityMatrix.Core/Models/Packages/RuinedFooocus.cs

@ -1,9 +1,11 @@
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Cache;
using StabilityMatrix.Core.Helper.HardwareInfo;
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;
@ -26,6 +28,79 @@ public class RuinedFooocus(
new("https://raw.githubusercontent.com/runew0lf/pmmconfigs/main/RuinedFooocus_ss.png");
public override PackageDifficulty InstallerSortOrder => PackageDifficulty.Expert;
public override List<LaunchOptionDefinition> LaunchOptions =>
new()
{
new LaunchOptionDefinition
{
Name = "Preset",
Type = LaunchOptionType.Bool,
Options = { "--preset anime", "--preset realistic" }
},
new LaunchOptionDefinition
{
Name = "Port",
Type = LaunchOptionType.String,
Description = "Sets the listen port",
Options = { "--port" }
},
new LaunchOptionDefinition
{
Name = "Share",
Type = LaunchOptionType.Bool,
Description = "Set whether to share on Gradio",
Options = { "--share" }
},
new LaunchOptionDefinition
{
Name = "Listen",
Type = LaunchOptionType.String,
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
{
MemoryLevel.Low => "--lowvram",
MemoryLevel.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
};
public override async Task InstallPackage(
string installLocation,
TorchVersion torchVersion,
@ -39,13 +114,23 @@ public class RuinedFooocus(
{
var venvRunner = await SetupVenv(installLocation, forceRecreate: true).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing torch...", isIndeterminate: true));
await InstallCudaTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing requirements...", isIndeterminate: true));
var requirements = new FilePath(installLocation, "requirements_versions.txt");
await venvRunner
.PipInstallFromRequirements(requirements, onConsoleOutput, excludes: "torch")
.PipInstall(
new PipInstallArgs()
.WithTorch("==2.0.1")
.WithTorchVision("==0.15.2")
.WithXFormers("==0.0.20")
.WithTorchExtraIndex("cu118")
.WithParsedFromRequirementsTxt(
await requirements.ReadAllTextAsync().ConfigureAwait(false),
excludePattern: "torch"
),
onConsoleOutput
)
.ConfigureAwait(false);
}
else
@ -60,5 +145,9 @@ public class RuinedFooocus(
)
.ConfigureAwait(false);
}
// Create output folder since it's not created by default
var outputFolder = new DirectoryPath(installLocation, OutputFolderName);
outputFolder.Create();
}
}

61
StabilityMatrix.Core/Processes/ProcessArgsBuilder.cs

@ -1,6 +1,5 @@
using System.Diagnostics;
using System.Collections.Immutable;
using System.Diagnostics.Contracts;
using OneOf;
namespace StabilityMatrix.Core.Processes;
@ -9,14 +8,7 @@ namespace StabilityMatrix.Core.Processes;
/// </summary>
public record ProcessArgsBuilder
{
protected ProcessArgsBuilder() { }
public ProcessArgsBuilder(params Argument[] arguments)
{
Arguments = arguments.ToList();
}
public List<Argument> Arguments { get; init; } = new();
public IImmutableList<Argument> Arguments { get; init; } = ImmutableArray<Argument>.Empty;
private IEnumerable<string> ToStringArgs()
{
@ -34,6 +26,11 @@ public record ProcessArgsBuilder
}
}
public ProcessArgsBuilder(params Argument[] arguments)
{
Arguments = arguments.ToImmutableArray();
}
/// <inheritdoc />
public override string ToString()
{
@ -45,8 +42,7 @@ public record ProcessArgsBuilder
return ToStringArgs().ToArray();
}
public static implicit operator ProcessArgs(ProcessArgsBuilder builder) =>
builder.ToProcessArgs();
public static implicit operator ProcessArgs(ProcessArgsBuilder builder) => builder.ToProcessArgs();
}
public static class ProcessArgBuilderExtensions
@ -55,7 +51,33 @@ public static class ProcessArgBuilderExtensions
public static T AddArg<T>(this T builder, Argument argument)
where T : ProcessArgsBuilder
{
return builder with { Arguments = builder.Arguments.Append(argument).ToList() };
return builder with { Arguments = builder.Arguments.Add(argument) };
}
[Pure]
public static T AddArgs<T>(this T builder, params Argument[] argument)
where T : ProcessArgsBuilder
{
return builder with { Arguments = builder.Arguments.AddRange(argument) };
}
[Pure]
public static T UpdateArg<T>(this T builder, string key, Argument argument)
where T : ProcessArgsBuilder
{
var oldArg = builder
.Arguments
.FirstOrDefault(x => x.Match(stringArg => stringArg == key, tupleArg => tupleArg.Item1 == key));
if (oldArg is null)
{
return builder.AddArg(argument);
}
return builder with
{
Arguments = builder.Arguments.Replace(oldArg, argument)
};
}
[Pure]
@ -64,15 +86,10 @@ public static class ProcessArgBuilderExtensions
{
return builder with
{
Arguments = builder.Arguments
.Where(
x =>
x.Match(
stringArg => stringArg != argumentKey,
tupleArg => tupleArg.Item1 != argumentKey
)
)
.ToList()
Arguments = builder
.Arguments
.Where(x => x.Match(stringArg => stringArg != argumentKey, tupleArg => tupleArg.Item1 != argumentKey))
.ToImmutableArray()
};
}
}

33
StabilityMatrix.Core/Python/PipInstallArgs.cs

@ -1,4 +1,7 @@
using StabilityMatrix.Core.Processes;
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Processes;
namespace StabilityMatrix.Core.Python;
@ -9,20 +12,36 @@ public record PipInstallArgs : ProcessArgsBuilder
public PipInstallArgs WithTorch(string version = "") => this.AddArg($"torch{version}");
public PipInstallArgs WithTorchDirectML(string version = "") =>
this.AddArg($"torch-directml{version}");
public PipInstallArgs WithTorchDirectML(string version = "") => this.AddArg($"torch-directml{version}");
public PipInstallArgs WithTorchVision(string version = "") =>
this.AddArg($"torchvision{version}");
public PipInstallArgs WithTorchVision(string version = "") => this.AddArg($"torchvision{version}");
public PipInstallArgs WithXFormers(string version = "") => this.AddArg($"xformers{version}");
public PipInstallArgs WithExtraIndex(string indexUrl) =>
this.AddArg(("--extra-index-url", indexUrl));
public PipInstallArgs WithExtraIndex(string indexUrl) => this.AddArg(("--extra-index-url", indexUrl));
public PipInstallArgs WithTorchExtraIndex(string index) =>
this.AddArg(("--extra-index-url", $"https://download.pytorch.org/whl/{index}"));
public PipInstallArgs WithParsedFromRequirementsTxt(
string requirements,
[StringSyntax(StringSyntaxAttribute.Regex)] string? excludePattern = null
)
{
var requirementsEntries = requirements
.SplitLines(StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
.AsEnumerable();
if (excludePattern is not null)
{
var excludeRegex = new Regex($"^{excludePattern}$");
requirementsEntries = requirementsEntries.Where(s => !excludeRegex.IsMatch(s));
}
return this.AddArgs(requirementsEntries.Select(s => (Argument)s).ToArray());
}
/// <inheritdoc />
public override string ToString()
{

87
StabilityMatrix.Core/Python/PipPackageSpecifier.cs

@ -0,0 +1,87 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using StabilityMatrix.Core.Processes;
namespace StabilityMatrix.Core.Python;
public partial record PipPackageSpecifier
{
public required string Name { get; init; }
public string? Constraint { get; init; }
public string? Version { get; init; }
public string? VersionConstraint => Constraint is null || Version is null ? null : Constraint + Name;
public static PipPackageSpecifier Parse(string value)
{
var result = TryParse(value, true, out var packageSpecifier);
Debug.Assert(result);
return packageSpecifier!;
}
public static bool TryParse(string value, [NotNullWhen(true)] out PipPackageSpecifier? packageSpecifier)
{
return TryParse(value, false, out packageSpecifier);
}
private static bool TryParse(
string value,
bool throwOnFailure,
[NotNullWhen(true)] out PipPackageSpecifier? packageSpecifier
)
{
var match = PackageSpecifierRegex().Match(value);
if (!match.Success)
{
if (throwOnFailure)
{
throw new ArgumentException($"Invalid package specifier: {value}");
}
packageSpecifier = null;
return false;
}
packageSpecifier = new PipPackageSpecifier
{
Name = match.Groups["package_name"].Value,
Constraint = match.Groups["version_constraint"].Value,
Version = match.Groups["version"].Value
};
return true;
}
/// <inheritdoc />
public override string ToString()
{
return Name + VersionConstraint;
}
public static implicit operator Argument(PipPackageSpecifier specifier)
{
return specifier.VersionConstraint is null
? new Argument(specifier.Name)
: new Argument((specifier.Name, specifier.VersionConstraint));
}
public static implicit operator PipPackageSpecifier(string specifier)
{
return Parse(specifier);
}
/// <summary>
/// Regex to match a pip package specifier.
/// </summary>
[GeneratedRegex(
"(?<package_name>[a-zA-Z0-9_]+)(?<version_specifier>(?<version_constraint>==|>=|<=|>|<|~=|!=)(<version>[a-zA-Z0-9_.]+))?",
RegexOptions.CultureInvariant,
1000
)]
private static partial Regex PackageSpecifierRegex();
}
Loading…
Cancel
Save