Skip to content
Merged

v2.15.3 #1432

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ All notable changes to Stability Matrix will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).

## v2.15.3
### Changed
- Updated fallback rocm index for InvokeAI to rocm6.3
- Updated SwarmUI to launch via the launch script for better compatibility
### Fixed
- Fixed cuDNN frontend error on ComfyUI-Zluda startup (thanks @neural_fault!)
- Maybe finally actually fixed threading issue with the Python Packages dialog search box for real this time? (may fix [#1392](https://github.com/LykosAI/StabilityMatrix/issues/1392))
- Fixed potential install failures when moving duplicate files into shared model folders (may fix [#1393](https://github.com/LykosAI/StabilityMatrix/issues/1393))
- Fixed potential threading issues with the Inference image gallery (may fix [#1408](https://github.com/LykosAI/StabilityMatrix/issues/1408))
- Fixed [#1424](https://github.com/LykosAI/StabilityMatrix/issues/1424) - Civitai account 401 error when connecting accounts, updated for new API changes
### Supporters
#### 🌟 Visionaries
Our deepest gratitude to our Visionaries for their foundational support: **Waterclouds**, **JungleDragon**, **bluepopsicle**, **Bob S**, and **whudunit**! Your commitment allows us to focus on the essential work of squashing bugs and improving stability, ensuring a rock-solid experience for everyone.
#### 🚀 Pioneers
A huge thank you to our incredible Pioneers for keeping the project on track! Your support is vital for these important refinement updates. Thank you to **Szir777**, **Noah M**, **USATechDude**, **Thom**, **SeraphOfSalem**, **Desert Viber**, **Tundra Everquill**, **Adam**, **Droolguy**, **Philip R.**, **ACTUALLY_the_Real_Willem_Dafoe**, **takyamtom**, and a warm welcome to our newest Pioneer, **robek**!


## v2.15.2
### Changed
- Updated Avalonia to 11.3.7
Expand Down
64 changes: 64 additions & 0 deletions StabilityMatrix.Avalonia/Helpers/UnixPrerequisiteHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using StabilityMatrix.Avalonia.Languages;
using StabilityMatrix.Core.Exceptions;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.HardwareInfo;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.Packages;
Expand Down Expand Up @@ -696,6 +697,69 @@ public async Task InstallUvIfNecessary(IProgress<ProgressReport>? progress = nul
File.Delete(UvDownloadPath);
}

public string? GetGfxArchFromAmdGpuName()
{
var gpu =
settingsManager.Settings.PreferredGpu
?? HardwareHelper.IterGpuInfo().FirstOrDefault(x => x is { Name: not null, IsAmd: true });

if (gpu?.Name is null || !gpu.IsAmd)
return null;

// Normalize for safer substring checks (handles RX7800 vs RX 7800, etc.)
var name = gpu.Name;
var nameNoSpaces = name.Replace(" ", "", StringComparison.Ordinal);

return name switch
{
// RDNA4
_ when Has("9060") || Has("9070") => "gfx1201",

// RDNA3.5 APUs
_ when Has("860M") => "gfx1152",
_ when Has("890M") => "gfx1150",
_ when Has("8040S") || Has("8050S") || Has("8060S") || Has("880M") || Has("Z2 Extreme") =>
"gfx1151",

// RDNA3 APUs (Phoenix)
_ when Has("740M") || Has("760M") || Has("780M") || Has("Z1") || Has("Z2") => "gfx1103",

// RDNA3 dGPU Navi33
_ when Has("7400") || Has("7500") || Has("7600") || Has("7650") || Has("7700S") => "gfx1102",

// RDNA3 dGPU Navi32
_ when Has("7700") || Has("RX 7800") || HasNoSpace("RX7800") => "gfx1101",

// RDNA3 dGPU Navi31 (incl. Pro)
_ when Has("W7800") || Has("7900") || Has("7950") || Has("7990") => "gfx1100",

// RDNA2 APUs (Rembrandt)
_ when Has("660M") || Has("680M") => "gfx1035",

// RDNA2 Navi24 low-end (incl. some mobiles)
_ when Has("6300") || Has("6400") || Has("6450") || Has("6500") || Has("6550") || Has("6500M") =>
"gfx1034",

// RDNA2 Navi23
_ when Has("6600") || Has("6650") || Has("6700S") || Has("6800S") || Has("6600M") => "gfx1032",

// RDNA2 Navi22 (note: desktop 6800 is NOT here; that’s Navi21/gfx1030)
_ when Has("6700") || Has("6750") || Has("6800M") || Has("6850M") => "gfx1031",

// RDNA2 Navi21 (big die)
_ when Has("6800") || Has("6900") || Has("6950") => "gfx1030",

_ => null,
};

bool HasNoSpace(string s) =>
nameNoSpaces.Contains(
s.Replace(" ", "", StringComparison.Ordinal),
StringComparison.OrdinalIgnoreCase
);
bool Has(string s) => name.Contains(s, StringComparison.OrdinalIgnoreCase);
}

private async Task DownloadAndExtractPrerequisite(
IProgress<ProgressReport>? progress,
string downloadUrl,
Expand Down
63 changes: 63 additions & 0 deletions StabilityMatrix.Avalonia/Helpers/WindowsPrerequisiteHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,69 @@ public async Task AddMissingLibsToVenv(
// await downloadPath.DeleteAsync();
}

public string? GetGfxArchFromAmdGpuName()
{
var gpu =
settingsManager.Settings.PreferredGpu
?? HardwareHelper.IterGpuInfo().FirstOrDefault(x => x is { Name: not null, IsAmd: true });

if (gpu?.Name is null || !gpu.IsAmd)
return null;

// Normalize for safer substring checks (handles RX7800 vs RX 7800, etc.)
var name = gpu.Name;
var nameNoSpaces = name.Replace(" ", "", StringComparison.Ordinal);

return name switch
{
// RDNA4
_ when Has("9060") || Has("9070") => "gfx1201",

// RDNA3.5 APUs
_ when Has("860M") => "gfx1152",
_ when Has("890M") => "gfx1150",
_ when Has("8040S") || Has("8050S") || Has("8060S") || Has("880M") || Has("Z2 Extreme") =>
"gfx1151",

// RDNA3 APUs (Phoenix)
_ when Has("740M") || Has("760M") || Has("780M") || Has("Z1") || Has("Z2") => "gfx1103",

// RDNA3 dGPU Navi33
_ when Has("7400") || Has("7500") || Has("7600") || Has("7650") || Has("7700S") => "gfx1102",

// RDNA3 dGPU Navi32
_ when Has("7700") || Has("RX 7800") || HasNoSpace("RX7800") => "gfx1101",

// RDNA3 dGPU Navi31 (incl. Pro)
_ when Has("W7800") || Has("7900") || Has("7950") || Has("7990") => "gfx1100",

// RDNA2 APUs (Rembrandt)
_ when Has("660M") || Has("680M") => "gfx1035",

// RDNA2 Navi24 low-end (incl. some mobiles)
_ when Has("6300") || Has("6400") || Has("6450") || Has("6500") || Has("6550") || Has("6500M") =>
"gfx1034",

// RDNA2 Navi23
_ when Has("6600") || Has("6650") || Has("6700S") || Has("6800S") || Has("6600M") => "gfx1032",

// RDNA2 Navi22 (note: desktop 6800 is NOT here; that’s Navi21/gfx1030)
_ when Has("6700") || Has("6750") || Has("6800M") || Has("6850M") => "gfx1031",

// RDNA2 Navi21 (big die)
_ when Has("6800") || Has("6900") || Has("6950") => "gfx1030",

_ => null,
};

bool HasNoSpace(string s) =>
nameNoSpaces.Contains(
s.Replace(" ", "", StringComparison.Ordinal),
StringComparison.OrdinalIgnoreCase
);
bool Has(string s) => name.Contains(s, StringComparison.OrdinalIgnoreCase);
}

private async Task DownloadAndExtractPrerequisite(
IProgress<ProgressReport>? progress,
string downloadUrl,
Expand Down
55 changes: 44 additions & 11 deletions StabilityMatrix.Avalonia/Services/AccountsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,10 @@ public async Task CivitLoginAsync(string apiToken)
var secrets = await secretsManager.SafeLoadAsync();

// Get id first using the api token
var userAccount = await civitTRPCApi.GetUserAccountDefault(apiToken);
var id = userAccount.Result.Data.Json.Id;
var userAccount = await civitTRPCApi.GetUserAccount(bearerToken: apiToken);
var id =
userAccount.InnerJson?.Id
?? throw new InvalidOperationException("GetUserAccount did not contain an id");

// Then get the username using the id
var account = await civitTRPCApi.GetUserById(new CivitGetUserByIdRequest { Id = id }, apiToken);
Expand Down Expand Up @@ -241,7 +243,7 @@ secrets.LykosAccountV2 is not null
{
IsConnected = true,
Principal = principal,
User = user
User = user,
}
);

Expand Down Expand Up @@ -282,27 +284,55 @@ private async Task RefreshHuggingFaceAsync(Secrets secrets)
if (response.IsSuccessStatusCode && response.Content != null)
{
// Token is valid, user info fetched
logger.LogInformation("Hugging Face token is valid. User: {Username}", response.Content.Name);
OnHuggingFaceAccountStatusUpdate(new HuggingFaceAccountStatusUpdateEventArgs(true, response.Content.Name));
logger.LogInformation(
"Hugging Face token is valid. User: {Username}",
response.Content.Name
);
OnHuggingFaceAccountStatusUpdate(
new HuggingFaceAccountStatusUpdateEventArgs(true, response.Content.Name)
);
}
else
{
// Token is likely invalid or other API error
logger.LogWarning("Hugging Face token validation failed. Status: {StatusCode}, Error: {Error}, Content: {Content}", response.StatusCode, response.Error?.ToString(), await response.Error?.GetContentAsAsync<string>() ?? "N/A");
OnHuggingFaceAccountStatusUpdate(new HuggingFaceAccountStatusUpdateEventArgs(false, null, $"Token validation failed: {response.StatusCode}"));
logger.LogWarning(
"Hugging Face token validation failed. Status: {StatusCode}, Error: {Error}, Content: {Content}",
response.StatusCode,
response.Error?.ToString(),
await response.Error?.GetContentAsAsync<string>() ?? "N/A"
);
OnHuggingFaceAccountStatusUpdate(
new HuggingFaceAccountStatusUpdateEventArgs(
false,
null,
$"Token validation failed: {response.StatusCode}"
)
);
}
}
catch (ApiException apiEx)
{
// Handle Refit's ApiException (network issues, non-success status codes not caught by IsSuccessStatusCode if IApiResponse isn't used directly)
logger.LogError(apiEx, "Hugging Face API request failed during token validation. Content: {Content}", await apiEx.GetContentAsAsync<string>() ?? "N/A");
OnHuggingFaceAccountStatusUpdate(new HuggingFaceAccountStatusUpdateEventArgs(false, null, "API request failed during token validation."));
logger.LogError(
apiEx,
"Hugging Face API request failed during token validation. Content: {Content}",
await apiEx.GetContentAsAsync<string>() ?? "N/A"
);
OnHuggingFaceAccountStatusUpdate(
new HuggingFaceAccountStatusUpdateEventArgs(
false,
null,
"API request failed during token validation."
)
);
}
catch (Exception ex)
{
// Handle other unexpected errors
logger.LogError(ex, "An unexpected error occurred during Hugging Face token validation.");
OnHuggingFaceAccountStatusUpdate(new HuggingFaceAccountStatusUpdateEventArgs(false, null, "An unexpected error occurred."));
OnHuggingFaceAccountStatusUpdate(
new HuggingFaceAccountStatusUpdateEventArgs(false, null, "An unexpected error occurred.")
);
}
}
else
Expand Down Expand Up @@ -390,7 +420,10 @@ private void OnHuggingFaceAccountStatusUpdate(HuggingFaceAccountStatusUpdateEven
else if (e.IsConnected && HuggingFaceStatus?.IsConnected == false)
{
// Assuming Username might be null for now as we are not fetching it.
logger.LogInformation("Hugging Face account connected" + (string.IsNullOrWhiteSpace(e.Username) ? "" : $" (User: {e.Username})"));
logger.LogInformation(
"Hugging Face account connected"
+ (string.IsNullOrWhiteSpace(e.Username) ? "" : $" (User: {e.Username})")
);
}
else if (!e.IsConnected && !string.IsNullOrWhiteSpace(e.ErrorMessage))
{
Expand Down
2 changes: 1 addition & 1 deletion StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<ApplicationIcon>./Assets/Icon.ico</ApplicationIcon>
<Version>2.15.0-dev.999</Version>
<Version>2.16.0-dev.999</Version>
<InformationalVersion>$(Version)</InformationalVersion>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ private void PostConstruct()
var searchPredicate = this.WhenPropertyChanged(vm => vm.SearchQuery)
.Throttle(TimeSpan.FromMilliseconds(100))
.DistinctUntilChanged()
.ObserveOn(SynchronizationContext.Current!)
.Select(value =>
{
if (string.IsNullOrWhiteSpace(value.Value))
Expand All @@ -86,8 +87,8 @@ private void PostConstruct()
.Filter(searchPredicate)
.Transform(p => new PythonPackagesItemViewModel(settingsManager) { Package = p })
.SortBy(vm => vm.Package.Name)
.Bind(Packages)
.ObserveOn(SynchronizationContext.Current!)
.Bind(Packages)
.Subscribe();
}

Expand Down Expand Up @@ -120,10 +121,14 @@ await pyInstallationManager.GetInstallationAsync(

var packages = await venvRunner.PipList();

packageSource.EditDiff(packages);
Dispatcher.UIThread.Post(() =>
{
var currentName = SelectedPackage?.Package.Name;
SelectedPackage = null;

// Delay a bit to prevent thread issues with UI list
await Task.Delay(100);
packageSource.EditDiff(packages);
SelectedPackage = Packages.FirstOrDefault(p => p.Package.Name == currentName);
});
}
}
finally
Expand Down Expand Up @@ -155,6 +160,7 @@ await pyInstallationManager.GetInstallationAsync(
{
// Backup selected package
var currentPackageName = SelectedPackage?.Package.Name;
SelectedPackage = null;

packageSource.EditDiff(packages);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Linq;
using Avalonia.Threading;
using StabilityMatrix.Avalonia.Models;

namespace StabilityMatrix.Avalonia.ViewModels.Inference;
Expand All @@ -12,13 +13,20 @@ public interface IImageGalleryComponent
/// </summary>
public void LoadImagesToGallery(params ImageSource[] imageSources)
{
ImageGalleryCardViewModel.ImageSources.Clear();

foreach (var imageSource in imageSources)
Dispatcher.UIThread.Post(() =>
{
ImageGalleryCardViewModel.ImageSources.Add(imageSource);
}
ImageGalleryCardViewModel.SelectedImage = null;
ImageGalleryCardViewModel.SelectedImageIndex = -1;

ImageGalleryCardViewModel.ImageSources.Clear();

foreach (var imageSource in imageSources)
{
ImageGalleryCardViewModel.ImageSources.Add(imageSource);
}

ImageGalleryCardViewModel.SelectedImage = imageSources.FirstOrDefault();
ImageGalleryCardViewModel.SelectedImageIndex = imageSources.Length > 0 ? 0 : -1;
ImageGalleryCardViewModel.SelectedImage = imageSources.FirstOrDefault();
});
}
}
16 changes: 14 additions & 2 deletions StabilityMatrix.Core/Api/ICivitTRPCApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,25 @@ Task<CivitUserProfileResponse> GetUserProfile(

[QueryUriFormat(UriFormat.UriEscaped)]
[Get("/api/trpc/buzz.getUserAccount")]
Task<CivitTrpcResponse<CivitUserAccountResponse>> GetUserAccount(
Task<CivitTrpcArrayResponse<CivitUserAccountResponse>> GetUserAccount(
[Query] string input,
[Authorize] string bearerToken,
CancellationToken cancellationToken = default
);

Task<CivitTrpcResponse<CivitUserAccountResponse>> GetUserAccountDefault(
[QueryUriFormat(UriFormat.UriEscaped)]
[Get("/api/trpc/buzz.getUserAccount")]
Task<CivitTrpcArrayResponse<CivitUserAccountResponse>> GetUserAccount(
[Authorize] string bearerToken,
CancellationToken cancellationToken = default
);

/// <summary>
/// Calls <see cref="GetUserAccount(string, string, CancellationToken)"/> with default JSON input.
/// Not required and returns 401 since Oct 2025 since civit changes.
/// Mainly just use <see cref="GetUserAccount(string, CancellationToken)"/> instead.
/// </summary>
Task<CivitTrpcArrayResponse<CivitUserAccountResponse>> GetUserAccountDefault(
string bearerToken,
CancellationToken cancellationToken = default
)
Expand Down
Loading
Loading