Browse Source

wip start of multi-package running stuff

pull/629/head
JT 9 months ago
parent
commit
49273b049a
  1. 135
      StabilityMatrix.Avalonia/Services/RunningPackageService.cs
  2. 54
      StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs
  3. 2
      StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs
  4. 13
      StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs
  5. 22
      StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml
  6. 13
      StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml.cs
  7. 1
      StabilityMatrix.Core/Helper/Factory/IPackageFactory.cs
  8. 33
      StabilityMatrix.Core/Helper/Factory/PackageFactory.cs

135
StabilityMatrix.Avalonia/Services/RunningPackageService.cs

@ -0,0 +1,135 @@
using System;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Notifications;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.Extensions.Logging;
using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Avalonia.ViewModels;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Factory;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.Services;
[Singleton]
public partial class RunningPackageService(
ILogger<RunningPackageService> logger,
IPackageFactory packageFactory,
INotificationService notificationService,
ISettingsManager settingsManager,
IPyRunner pyRunner
) : ObservableObject
{
[ObservableProperty]
private ObservableDictionary<Guid, RunningPackageViewModel> runningPackages = [];
public async Task<Guid?> StartPackage(InstalledPackage installedPackage, string? command = null)
{
var activeInstallName = installedPackage.PackageName;
var basePackage = string.IsNullOrWhiteSpace(activeInstallName)
? null
: packageFactory.GetNewBasePackage(installedPackage);
if (basePackage == null)
{
logger.LogWarning(
"During launch, package name '{PackageName}' did not match a definition",
activeInstallName
);
notificationService.Show(
new Notification(
"Package name invalid",
"Install package name did not match a definition. Please reinstall and let us know about this issue.",
NotificationType.Error
)
);
return null;
}
// If this is the first launch (LaunchArgs is null),
// load and save a launch options dialog vm
// so that dynamic initial values are saved.
if (installedPackage.LaunchArgs == null)
{
var definitions = basePackage.LaunchOptions;
// Create config cards and save them
var cards = LaunchOptionCard
.FromDefinitions(definitions, Array.Empty<LaunchOption>())
.ToImmutableArray();
var args = cards.SelectMany(c => c.Options).ToList();
logger.LogDebug(
"Setting initial launch args: {Args}",
string.Join(", ", args.Select(o => o.ToArgString()?.ToRepr()))
);
settingsManager.SaveLaunchArgs(installedPackage.Id, args);
}
if (basePackage is not StableSwarm)
{
await pyRunner.Initialize();
}
// Get path from package
var packagePath = new DirectoryPath(settingsManager.LibraryDir, installedPackage.LibraryPath!);
if (basePackage is not StableSwarm)
{
// Unpack sitecustomize.py to venv
await UnpackSiteCustomize(packagePath.JoinDir("venv"));
}
// Clear console and start update processing
var console = new ConsoleViewModel();
console.StartUpdates();
// Update shared folder links (in case library paths changed)
await basePackage.UpdateModelFolders(
packagePath,
installedPackage.PreferredSharedFolderMethod ?? basePackage.RecommendedSharedFolderMethod
);
// Load user launch args from settings and convert to string
var userArgs = installedPackage.LaunchArgs ?? [];
var userArgsString = string.Join(" ", userArgs.Select(opt => opt.ToArgString()));
// Join with extras, if any
userArgsString = string.Join(" ", userArgsString, basePackage.ExtraLaunchArguments);
// Use input command if provided, otherwise use package launch command
command ??= basePackage.LaunchCommand;
await basePackage.RunPackage(packagePath, command, userArgsString, o => console.Post(o));
var runningPackage = new PackagePair(installedPackage, basePackage);
EventManager.Instance.OnRunningPackageStatusChanged(runningPackage);
var viewModel = new RunningPackageViewModel(runningPackage, console);
RunningPackages.Add(runningPackage.InstalledPackage.Id, viewModel);
return runningPackage.InstalledPackage.Id;
}
public RunningPackageViewModel? GetRunningPackageViewModel(Guid id) =>
RunningPackages.TryGetValue(id, out var vm) ? vm : null;
private static async Task UnpackSiteCustomize(DirectoryPath venvPath)
{
var sitePackages = venvPath.JoinDir(PyVenvRunner.RelativeSitePackagesPath);
var file = sitePackages.JoinFile("sitecustomize.py");
file.Directory?.Create();
await Assets.PyScriptSiteCustomize.ExtractTo(file, true);
}
}

54
StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageCardViewModel.cs

@ -26,7 +26,6 @@ using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.FileInterfaces; using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.PackageModification; using StabilityMatrix.Core.Models.PackageModification;
using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Models.Settings;
using StabilityMatrix.Core.Processes; using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Services; using StabilityMatrix.Core.Services;
@ -34,15 +33,16 @@ namespace StabilityMatrix.Avalonia.ViewModels.PackageManager;
[ManagedService] [ManagedService]
[Transient] [Transient]
public partial class PackageCardViewModel : ProgressViewModel public partial class PackageCardViewModel(
ILogger<PackageCardViewModel> logger,
IPackageFactory packageFactory,
INotificationService notificationService,
ISettingsManager settingsManager,
INavigationService<MainWindowViewModel> navigationService,
ServiceManager<ViewModelBase> vmFactory,
RunningPackageService runningPackageService
) : ProgressViewModel
{ {
private readonly ILogger<PackageCardViewModel> logger;
private readonly IPackageFactory packageFactory;
private readonly INotificationService notificationService;
private readonly ISettingsManager settingsManager;
private readonly INavigationService<MainWindowViewModel> navigationService;
private readonly ServiceManager<ViewModelBase> vmFactory;
[ObservableProperty] [ObservableProperty]
private InstalledPackage? package; private InstalledPackage? package;
@ -82,23 +82,6 @@ public partial class PackageCardViewModel : ProgressViewModel
[ObservableProperty] [ObservableProperty]
private bool canUseExtensions; private bool canUseExtensions;
public PackageCardViewModel(
ILogger<PackageCardViewModel> logger,
IPackageFactory packageFactory,
INotificationService notificationService,
ISettingsManager settingsManager,
INavigationService<MainWindowViewModel> navigationService,
ServiceManager<ViewModelBase> vmFactory
)
{
this.logger = logger;
this.packageFactory = packageFactory;
this.notificationService = notificationService;
this.settingsManager = settingsManager;
this.navigationService = navigationService;
this.vmFactory = vmFactory;
}
partial void OnPackageChanged(InstalledPackage? value) partial void OnPackageChanged(InstalledPackage? value)
{ {
if (string.IsNullOrWhiteSpace(value?.PackageName)) if (string.IsNullOrWhiteSpace(value?.PackageName))
@ -163,15 +146,26 @@ public partial class PackageCardViewModel : ProgressViewModel
} }
} }
public void Launch() public async Task Launch()
{ {
if (Package == null) if (Package == null)
return; return;
settingsManager.Transaction(s => s.ActiveInstalledPackageId = Package.Id); var packageId = await runningPackageService.StartPackage(Package);
if (packageId != null)
{
var vm = runningPackageService.GetRunningPackageViewModel(packageId.Value);
if (vm != null)
{
navigationService.NavigateTo(vm, new BetterDrillInNavigationTransition());
}
}
navigationService.NavigateTo<LaunchPageViewModel>(new BetterDrillInNavigationTransition()); // settingsManager.Transaction(s => s.ActiveInstalledPackageId = Package.Id);
EventManager.Instance.OnPackageLaunchRequested(Package.Id); //
// navigationService.NavigateTo<LaunchPageViewModel>(new BetterDrillInNavigationTransition());
// EventManager.Instance.OnPackageLaunchRequested(Package.Id);
} }
public async Task Uninstall() public async Task Uninstall()

2
StabilityMatrix.Avalonia/ViewModels/PackageManager/PackageInstallDetailViewModel.cs

@ -281,7 +281,7 @@ public partial class PackageInstallDetailViewModel(
SelectedVersion = !IsReleaseMode SelectedVersion = !IsReleaseMode
? AvailableVersions?.FirstOrDefault(x => x.TagName == SelectedPackage.MainBranch) ? AvailableVersions?.FirstOrDefault(x => x.TagName == SelectedPackage.MainBranch)
?? AvailableVersions?.FirstOrDefault() ?? AvailableVersions?.FirstOrDefault()
: AvailableVersions?.FirstOrDefault(); : AvailableVersions?.FirstOrDefault(v => !v.IsPrerelease);
CanInstall = !ShowDuplicateWarning; CanInstall = !ShowDuplicateWarning;
} }

13
StabilityMatrix.Avalonia/ViewModels/RunningPackageViewModel.cs

@ -0,0 +1,13 @@
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Models;
namespace StabilityMatrix.Avalonia.ViewModels;
[View(typeof(ConsoleOutputPage))]
public class RunningPackageViewModel(PackagePair runningPackage, ConsoleViewModel console) : ViewModelBase
{
public PackagePair RunningPackage { get; } = runningPackage;
public ConsoleViewModel Console { get; } = console;
}

22
StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml

@ -0,0 +1,22 @@
<controls:UserControlBase xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:avaloniaEdit="https://github.com/avaloniaui/avaloniaedit"
xmlns:viewModels="clr-namespace:StabilityMatrix.Avalonia.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="viewModels:RunningPackageViewModel"
x:Class="StabilityMatrix.Avalonia.Views.ConsoleOutputPage">
<avaloniaEdit:TextEditor
x:Name="Console"
Margin="8,8,16,10"
DataContext="{Binding Console}"
Document="{Binding Document}"
FontFamily="Cascadia Code,Consolas,Menlo,Monospace"
IsReadOnly="True"
LineNumbersForeground="DarkSlateGray"
ShowLineNumbers="True"
VerticalScrollBarVisibility="Auto"
WordWrap="True" />
</controls:UserControlBase>

13
StabilityMatrix.Avalonia/Views/ConsoleOutputPage.axaml.cs

@ -0,0 +1,13 @@
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Core.Attributes;
namespace StabilityMatrix.Avalonia.Views;
[Transient]
public partial class ConsoleOutputPage : UserControlBase
{
public ConsoleOutputPage()
{
InitializeComponent();
}
}

1
StabilityMatrix.Core/Helper/Factory/IPackageFactory.cs

@ -10,4 +10,5 @@ public interface IPackageFactory
BasePackage? this[string packageName] { get; } BasePackage? this[string packageName] { get; }
PackagePair? GetPackagePair(InstalledPackage? installedPackage); PackagePair? GetPackagePair(InstalledPackage? installedPackage);
IEnumerable<BasePackage> GetPackagesByType(PackageType packageType); IEnumerable<BasePackage> GetPackagesByType(PackageType packageType);
BasePackage GetNewBasePackage(InstalledPackage installedPackage);
} }

33
StabilityMatrix.Core/Helper/Factory/PackageFactory.cs

@ -1,22 +1,53 @@
using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper.Cache;
using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Helper.Factory; namespace StabilityMatrix.Core.Helper.Factory;
[Singleton(typeof(IPackageFactory))] [Singleton(typeof(IPackageFactory))]
public class PackageFactory : IPackageFactory public class PackageFactory : IPackageFactory
{ {
private readonly IGithubApiCache githubApiCache;
private readonly ISettingsManager settingsManager;
private readonly IDownloadService downloadService;
private readonly IPrerequisiteHelper prerequisiteHelper;
/// <summary> /// <summary>
/// Mapping of package.Name to package /// Mapping of package.Name to package
/// </summary> /// </summary>
private readonly Dictionary<string, BasePackage> basePackages; private readonly Dictionary<string, BasePackage> basePackages;
public PackageFactory(IEnumerable<BasePackage> basePackages) public PackageFactory(
IEnumerable<BasePackage> basePackages,
IGithubApiCache githubApiCache,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
{ {
this.githubApiCache = githubApiCache;
this.settingsManager = settingsManager;
this.downloadService = downloadService;
this.prerequisiteHelper = prerequisiteHelper;
this.basePackages = basePackages.ToDictionary(x => x.Name); this.basePackages = basePackages.ToDictionary(x => x.Name);
} }
public BasePackage GetNewBasePackage(InstalledPackage installedPackage)
{
return installedPackage.PackageName switch
{
"ComfyUI" => new ComfyUI(githubApiCache, settingsManager, downloadService, prerequisiteHelper),
"Fooocus" => new Fooocus(githubApiCache, settingsManager, downloadService, prerequisiteHelper),
"stable-diffusion-webui"
=> new A3WebUI(githubApiCache, settingsManager, downloadService, prerequisiteHelper),
"Fooocus-ControlNet-SDXL"
=> new FocusControlNet(githubApiCache, settingsManager, downloadService, prerequisiteHelper),
_ => throw new ArgumentOutOfRangeException()
};
}
public IEnumerable<BasePackage> GetAllAvailablePackages() public IEnumerable<BasePackage> GetAllAvailablePackages()
{ {
return basePackages.Values.OrderBy(p => p.InstallerSortOrder).ThenBy(p => p.DisplayName); return basePackages.Values.OrderBy(p => p.InstallerSortOrder).ThenBy(p => p.DisplayName);

Loading…
Cancel
Save