Browse Source

Rework and reduce calls to GitHub to help mitigate rate limiting. also made git pulls autostash & invokeAI should now install torch2.1/cuda12.1 for nvidia users

pull/263/head
JT 1 year ago
parent
commit
df40dc6a5b
  1. 9
      CHANGELOG.md
  2. 23
      StabilityMatrix.Avalonia/ViewModels/Dialogs/OneClickInstallViewModel.cs
  3. 10
      StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs
  4. 2
      StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs
  5. 149
      StabilityMatrix.Core/Helper/Cache/GithubApiCache.cs
  6. 11
      StabilityMatrix.Core/Helper/Cache/IGithubApiCache.cs
  7. 31
      StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs
  8. 2
      StabilityMatrix.Core/Models/Packages/BasePackage.cs
  9. 60
      StabilityMatrix.Core/Models/Packages/InvokeAI.cs
  10. 8
      StabilityMatrix.Core/Models/Packages/UnknownPackage.cs

9
CHANGELOG.md

@ -5,6 +5,15 @@ 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/), 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). and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
## v2.6.1
### Changed
- NVIDIA GPU users will be updated to use CUDA 12.1 for the InvokeAI package 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
- Reduced the amount of calls to GitHub to help prevent rate limiting
- Fixed rate limit crash on startup preventing app from starting
## v2.6.0 ## v2.6.0
### Added ### 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

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

@ -140,30 +140,17 @@ public partial class OneClickInstallViewModel : ContentDialogViewModelBase
SelectedPackage.Name SelectedPackage.Name
); );
var downloadVersion = new DownloadPackageVersionOptions var downloadVersion = await SelectedPackage.GetLatestVersion();
{
IsLatest = true,
IsPrerelease = false
};
var installedVersion = new InstalledPackageVersion { IsPrerelease = false }; var installedVersion = new InstalledPackageVersion { IsPrerelease = false };
var versionOptions = await SelectedPackage.GetAllVersionOptions(); if (SelectedPackage.ShouldIgnoreReleases)
if (versionOptions.AvailableVersions != null && versionOptions.AvailableVersions.Any())
{ {
downloadVersion.VersionTag = versionOptions.AvailableVersions.First().TagName; installedVersion.InstalledBranch = downloadVersion.BranchName;
installedVersion.InstalledReleaseVersion = downloadVersion.VersionTag; installedVersion.InstalledCommitSha = downloadVersion.CommitHash;
} }
else else
{ {
var latestVersion = await SelectedPackage.GetLatestVersion(); installedVersion.InstalledReleaseVersion = downloadVersion.VersionTag;
downloadVersion.BranchName = latestVersion.BranchName;
downloadVersion.CommitHash =
(await SelectedPackage.GetAllCommits(downloadVersion.BranchName))
?.FirstOrDefault()
?.Sha ?? string.Empty;
installedVersion.InstalledBranch = downloadVersion.BranchName;
installedVersion.InstalledCommitSha = downloadVersion.CommitHash;
} }
var torchVersion = SelectedPackage.GetRecommendedTorchVersion(); var torchVersion = SelectedPackage.GetRecommendedTorchVersion();

10
StabilityMatrix.Avalonia/ViewModels/MainWindowViewModel.cs

@ -111,7 +111,7 @@ public partial class MainWindowViewModel : ViewModelBase
var startupTime = CodeTimer.FormatTime(Program.StartupTimer.Elapsed); var startupTime = CodeTimer.FormatTime(Program.StartupTimer.Elapsed);
Logger.Info($"App started ({startupTime})"); Logger.Info($"App started ({startupTime})");
if (Program.Args.DebugOneClickInstall || !settingsManager.Settings.InstalledPackages.Any()) if (Program.Args.DebugOneClickInstall || settingsManager.Settings.InstalledPackages.Any())
{ {
var viewModel = dialogFactory.Get<OneClickInstallViewModel>(); var viewModel = dialogFactory.Get<OneClickInstallViewModel>();
var dialog = new BetterContentDialog var dialog = new BetterContentDialog
@ -180,7 +180,8 @@ public partial class MainWindowViewModel : ViewModelBase
private async Task<bool> EnsureDataDirectory() private async Task<bool> EnsureDataDirectory()
{ {
// If we can't find library, show selection dialog // If we can't find library, show selection dialog
if (!settingsManager.TryFindLibrary()) var foundInitially = settingsManager.TryFindLibrary();
if (!foundInitially)
{ {
var result = await ShowSelectDataDirectoryDialog(); var result = await ShowSelectDataDirectoryDialog();
if (!result) if (!result)
@ -194,7 +195,10 @@ public partial class MainWindowViewModel : ViewModelBase
} }
// Tell LaunchPage to load any packages if they selected an existing directory // Tell LaunchPage to load any packages if they selected an existing directory
EventManager.Instance.OnInstalledPackagesChanged(); if (!foundInitially)
{
EventManager.Instance.OnInstalledPackagesChanged();
}
// Check if there are old packages, if so show migration dialog // Check if there are old packages, if so show migration dialog
// TODO: Migration dialog // TODO: Migration dialog

2
StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs

@ -99,7 +99,7 @@ public partial class PackageManagerViewModel : PageViewModelBase
.Bind(PackageCards) .Bind(PackageCards)
.Subscribe(); .Subscribe();
timer = new DispatcherTimer { Interval = TimeSpan.FromMinutes(15), IsEnabled = true }; timer = new DispatcherTimer { Interval = TimeSpan.FromMinutes(60), IsEnabled = true };
timer.Tick += async (_, _) => await CheckPackagesForUpdates(); timer.Tick += async (_, _) => await CheckPackagesForUpdates();
} }

149
StabilityMatrix.Core/Helper/Cache/GithubApiCache.cs

@ -1,4 +1,5 @@
using Octokit; using Microsoft.Extensions.Logging;
using Octokit;
using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Database; using StabilityMatrix.Core.Database;
using StabilityMatrix.Core.Models.Database; using StabilityMatrix.Core.Models.Database;
@ -10,83 +11,85 @@ public class GithubApiCache : IGithubApiCache
{ {
private readonly ILiteDbContext dbContext; private readonly ILiteDbContext dbContext;
private readonly IGitHubClient githubApi; private readonly IGitHubClient githubApi;
private readonly ILogger<IGithubApiCache> logger;
private readonly TimeSpan cacheDuration = TimeSpan.FromMinutes(15); private readonly TimeSpan cacheDuration = TimeSpan.FromMinutes(15);
public GithubApiCache(ILiteDbContext dbContext, IGitHubClient githubApi) public GithubApiCache(
ILiteDbContext dbContext,
IGitHubClient githubApi,
ILogger<IGithubApiCache> logger
)
{ {
this.dbContext = dbContext; this.dbContext = dbContext;
this.githubApi = githubApi; this.githubApi = githubApi;
} this.logger = logger;
public async Task<Release?> GetLatestRelease(string username, string repository)
{
var cacheKey = $"Releases-{username}-{repository}";
var latestRelease = await dbContext.GetGithubCacheEntry(cacheKey);
if (latestRelease != null && !IsCacheExpired(latestRelease.LastUpdated))
{
return latestRelease.AllReleases.First();
}
var allReleases = await githubApi.Repository.Release.GetAll(username, repository);
if (allReleases == null)
{
return null;
}
var cacheEntry = new GithubCacheEntry
{
CacheKey = cacheKey,
AllReleases = allReleases.OrderByDescending(x => x.CreatedAt)
};
await dbContext.UpsertGithubCacheEntry(cacheEntry);
return cacheEntry.AllReleases.First();
} }
public async Task<IEnumerable<Release>> GetAllReleases(string username, string repository) public async Task<IEnumerable<Release>> GetAllReleases(string username, string repository)
{ {
var cacheKey = $"Releases-{username}-{repository}"; var cacheKey = $"Releases-{username}-{repository}";
var cacheEntry = await dbContext.GetGithubCacheEntry(cacheKey); var cacheEntry = await dbContext.GetGithubCacheEntry(cacheKey).ConfigureAwait(false);
if (cacheEntry != null && !IsCacheExpired(cacheEntry.LastUpdated)) if (cacheEntry != null && !IsCacheExpired(cacheEntry.LastUpdated))
{ {
return cacheEntry.AllReleases.OrderByDescending(x => x.CreatedAt); return cacheEntry.AllReleases.OrderByDescending(x => x.CreatedAt);
} }
var allReleases = await githubApi.Repository.Release.GetAll(username, repository); try
if (allReleases == null)
{ {
return new List<Release>().OrderByDescending(x => x.CreatedAt); var allReleases = await githubApi.Repository.Release
} .GetAll(username, repository)
.ConfigureAwait(false);
if (allReleases == null)
{
return new List<Release>();
}
var newCacheEntry = new GithubCacheEntry var newCacheEntry = new GithubCacheEntry
{ {
CacheKey = cacheKey, CacheKey = cacheKey,
AllReleases = allReleases.OrderByDescending(x => x.CreatedAt) AllReleases = allReleases.OrderByDescending(x => x.CreatedAt)
}; };
await dbContext.UpsertGithubCacheEntry(newCacheEntry); await dbContext.UpsertGithubCacheEntry(newCacheEntry).ConfigureAwait(false);
return newCacheEntry.AllReleases; return newCacheEntry.AllReleases;
}
catch (ApiException ex)
{
logger.LogWarning(ex, "Failed to get releases from Github API.");
return cacheEntry?.AllReleases.OrderByDescending(x => x.CreatedAt)
?? Enumerable.Empty<Release>();
}
} }
public async Task<IEnumerable<Branch>> GetAllBranches(string username, string repository) public async Task<IEnumerable<Branch>> GetAllBranches(string username, string repository)
{ {
var cacheKey = $"Branches-{username}-{repository}"; var cacheKey = $"Branches-{username}-{repository}";
var cacheEntry = await dbContext.GetGithubCacheEntry(cacheKey); var cacheEntry = await dbContext.GetGithubCacheEntry(cacheKey).ConfigureAwait(false);
if (cacheEntry != null && !IsCacheExpired(cacheEntry.LastUpdated)) if (cacheEntry != null && !IsCacheExpired(cacheEntry.LastUpdated))
{ {
return cacheEntry.Branches; return cacheEntry.Branches;
} }
var branches = await githubApi.Repository.Branch.GetAll(username, repository); try
if (branches == null)
{ {
return new List<Branch>(); var branches = await githubApi.Repository.Branch
} .GetAll(username, repository)
.ConfigureAwait(false);
if (branches == null)
{
return new List<Branch>();
}
var newCacheEntry = new GithubCacheEntry { CacheKey = cacheKey, Branches = branches }; var newCacheEntry = new GithubCacheEntry { CacheKey = cacheKey, Branches = branches };
await dbContext.UpsertGithubCacheEntry(newCacheEntry); await dbContext.UpsertGithubCacheEntry(newCacheEntry).ConfigureAwait(false);
return newCacheEntry.Branches; return newCacheEntry.Branches;
}
catch (ApiException ex)
{
logger.LogWarning(ex, "Failed to get branches from Github API.");
return cacheEntry?.Branches ?? Enumerable.Empty<Branch>();
}
} }
public async Task<IEnumerable<GitCommit>?> GetAllCommits( public async Task<IEnumerable<GitCommit>?> GetAllCommits(
@ -98,37 +101,47 @@ public class GithubApiCache : IGithubApiCache
) )
{ {
var cacheKey = $"Commits-{username}-{repository}-{branch}-{page}-{perPage}"; var cacheKey = $"Commits-{username}-{repository}-{branch}-{page}-{perPage}";
var cacheEntry = await dbContext.GetGithubCacheEntry(cacheKey); var cacheEntry = await dbContext.GetGithubCacheEntry(cacheKey).ConfigureAwait(false);
if (cacheEntry != null && !IsCacheExpired(cacheEntry.LastUpdated)) if (cacheEntry != null && !IsCacheExpired(cacheEntry.LastUpdated))
{ {
return cacheEntry.Commits; return cacheEntry.Commits;
} }
var commits = await githubApi.Repository.Commit.GetAll( try
username, {
repository, var commits = await githubApi.Repository.Commit
new CommitRequest { Sha = branch }, .GetAll(
new ApiOptions username,
repository,
new CommitRequest { Sha = branch },
new ApiOptions
{
PageCount = page,
PageSize = perPage,
StartPage = page
}
)
.ConfigureAwait(false);
if (commits == null)
{ {
PageCount = page, return new List<GitCommit>();
PageSize = perPage,
StartPage = page
} }
);
if (commits == null) var newCacheEntry = new GithubCacheEntry
{ {
return new List<GitCommit>(); CacheKey = cacheKey,
} Commits = commits.Select(x => new GitCommit { Sha = x.Sha })
};
await dbContext.UpsertGithubCacheEntry(newCacheEntry).ConfigureAwait(false);
var newCacheEntry = new GithubCacheEntry return newCacheEntry.Commits;
}
catch (ApiException ex)
{ {
CacheKey = cacheKey, logger.LogWarning(ex, "Failed to get commits from Github API.");
Commits = commits.Select(x => new GitCommit { Sha = x.Sha }) return cacheEntry?.Commits ?? Enumerable.Empty<GitCommit>();
}; }
await dbContext.UpsertGithubCacheEntry(newCacheEntry);
return newCacheEntry.Commits;
} }
private bool IsCacheExpired(DateTimeOffset expiration) => private bool IsCacheExpired(DateTimeOffset expiration) =>

11
StabilityMatrix.Core/Helper/Cache/IGithubApiCache.cs

@ -6,12 +6,15 @@ namespace StabilityMatrix.Core.Helper.Cache;
public interface IGithubApiCache public interface IGithubApiCache
{ {
Task<Release?> GetLatestRelease(string username, string repository);
Task<IEnumerable<Release>> GetAllReleases(string username, string repository); Task<IEnumerable<Release>> GetAllReleases(string username, string repository);
Task<IEnumerable<Branch>> GetAllBranches(string username, string repository); Task<IEnumerable<Branch>> GetAllBranches(string username, string repository);
Task<IEnumerable<GitCommit>?> GetAllCommits(string username, string repository, string branch, int page = 1, Task<IEnumerable<GitCommit>?> GetAllCommits(
[AliasAs("per_page")] int perPage = 10); string username,
string repository,
string branch,
int page = 1,
[AliasAs("per_page")] int perPage = 10
);
} }

31
StabilityMatrix.Core/Models/Packages/BaseGitPackage.cs

@ -86,31 +86,19 @@ public abstract class BaseGitPackage : BasePackage
}; };
} }
var release = await GetLatestRelease(includePrerelease).ConfigureAwait(false); var releases = await GithubApi.GetAllReleases(Author, Name).ConfigureAwait(false);
var latestRelease = includePrerelease
? releases.First()
: releases.First(x => !x.Prerelease);
return new DownloadPackageVersionOptions return new DownloadPackageVersionOptions
{ {
IsLatest = true, IsLatest = true,
IsPrerelease = release.Prerelease, IsPrerelease = latestRelease.Prerelease,
VersionTag = release.TagName! VersionTag = latestRelease.TagName!
}; };
} }
protected async Task<Release> GetLatestRelease(bool includePrerelease = false)
{
var releases = await GithubApi.GetAllReleases(Author, Name).ConfigureAwait(false);
return includePrerelease ? releases.First() : releases.First(x => !x.Prerelease);
}
public override Task<IEnumerable<Branch>> GetAllBranches()
{
return GithubApi.GetAllBranches(Author, Name);
}
public override Task<IEnumerable<Release>> GetAllReleases()
{
return GithubApi.GetAllReleases(Author, Name);
}
public override Task<IEnumerable<GitCommit>?> GetAllCommits( public override Task<IEnumerable<GitCommit>?> GetAllCommits(
string branch, string branch,
int page = 1, int page = 1,
@ -126,7 +114,7 @@ public abstract class BaseGitPackage : BasePackage
if (!ShouldIgnoreReleases) if (!ShouldIgnoreReleases)
{ {
var allReleases = await GetAllReleases().ConfigureAwait(false); var allReleases = await GithubApi.GetAllReleases(Author, Name).ConfigureAwait(false);
var releasesList = allReleases.ToList(); var releasesList = allReleases.ToList();
if (releasesList.Any()) if (releasesList.Any())
{ {
@ -143,7 +131,7 @@ public abstract class BaseGitPackage : BasePackage
} }
// Branch mode // Branch mode
var allBranches = await GetAllBranches().ConfigureAwait(false); var allBranches = await GithubApi.GetAllBranches(Author, Name).ConfigureAwait(false);
packageVersionOptions.AvailableBranches = allBranches.Select( packageVersionOptions.AvailableBranches = allBranches.Select(
b => new PackageVersion { TagName = $"{b.Name}", ReleaseNotesMarkdown = string.Empty } b => new PackageVersion { TagName = $"{b.Name}", ReleaseNotesMarkdown = string.Empty }
); );
@ -423,6 +411,7 @@ public abstract class BaseGitPackage : BasePackage
installedPackage.FullPath!, installedPackage.FullPath!,
onConsoleOutput, onConsoleOutput,
"pull", "pull",
"--autostash",
"origin", "origin",
installedPackage.Version.InstalledBranch installedPackage.Version.InstalledBranch
) )

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

@ -166,8 +166,6 @@ public abstract class BasePackage
int page = 1, int page = 1,
int perPage = 10 int perPage = 10
); );
public abstract Task<IEnumerable<Branch>> GetAllBranches();
public abstract Task<IEnumerable<Release>> GetAllReleases();
public abstract Task<DownloadPackageVersionOptions> GetLatestVersion( public abstract Task<DownloadPackageVersionOptions> GetLatestVersion(
bool includePrerelease = false bool includePrerelease = false
); );

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

@ -188,14 +188,66 @@ public class InvokeAI : BaseGitPackage
var pipCommandArgs = var pipCommandArgs =
"-e . --use-pep517 --extra-index-url https://download.pytorch.org/whl/cpu"; "-e . --use-pep517 --extra-index-url https://download.pytorch.org/whl/cpu";
var installTorch21 = versionOptions.IsLatest;
if (!string.IsNullOrWhiteSpace(versionOptions.VersionTag) && !versionOptions.IsLatest)
{
if (
Version.TryParse(versionOptions.VersionTag, out var version)
&& version >= new Version(3, 4)
)
{
installTorch21 = true;
}
}
switch (torchVersion) switch (torchVersion)
{ {
// If has Nvidia Gpu, install CUDA version // If has Nvidia Gpu, install CUDA version
case TorchVersion.Cuda: case TorchVersion.Cuda:
await InstallCudaTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false); if (installTorch21)
Logger.Info("Starting InvokeAI install (CUDA)..."); {
pipCommandArgs = progress?.Report(
"-e .[xformers] --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu118"; new ProgressReport(
-1f,
"Installing PyTorch for CUDA",
isIndeterminate: true
)
);
var args = new List<Argument>();
if (exists)
{
args.Add("--upgrade");
args.Add("--force-reinstall");
}
await venvRunner
.PipInstall(
new PipInstallArgs(
args.Any() ? args.ToArray() : Array.Empty<Argument>()
)
.WithTorch("==2.1.0")
.WithTorchVision("==0.16.0")
.WithXFormers("==0.0.22post7")
.WithTorchExtraIndex("cu121"),
onConsoleOutput
)
.ConfigureAwait(false);
Logger.Info("Starting InvokeAI install (CUDA)...");
pipCommandArgs =
"-e .[xformers] --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu121";
}
else
{
await InstallCudaTorch(venvRunner, progress, onConsoleOutput)
.ConfigureAwait(false);
Logger.Info("Starting InvokeAI install (CUDA)...");
pipCommandArgs =
"-e .[xformers] --use-pep517 --extra-index-url https://download.pytorch.org/whl/cu118";
}
break; break;
// For AMD, Install ROCm version // For AMD, Install ROCm version
case TorchVersion.Rocm: case TorchVersion.Rocm:

8
StabilityMatrix.Core/Models/Packages/UnknownPackage.cs

@ -163,12 +163,4 @@ public class UnknownPackage : BasePackage
int page = 1, int page = 1,
int perPage = 10 int perPage = 10
) => Task.FromResult<IEnumerable<GitCommit>?>(null); ) => Task.FromResult<IEnumerable<GitCommit>?>(null);
/// <inheritdoc />
public override Task<IEnumerable<Branch>> GetAllBranches() =>
Task.FromResult(Enumerable.Empty<Branch>());
/// <inheritdoc />
public override Task<IEnumerable<Release>> GetAllReleases() =>
Task.FromResult(Enumerable.Empty<Release>());
} }

Loading…
Cancel
Save