Skip to content

Commit 24cb710

Browse files
authored
Merge pull request #296 from LykosAI/main
2 parents 42bb734 + 1d74fe2 commit 24cb710

File tree

9 files changed

+223
-115
lines changed

9 files changed

+223
-115
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to Stability Matrix will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
77

8+
## v2.6.5
9+
### Fixed
10+
- Fixed error when receiving unknown model format values from the Model Browser
11+
- Fixed process errors when installing or updating Pip packages using the Python packages dialog
12+
813
## v2.6.4
914
### Fixed
1015
- Fixed errors preventing Model Browser from finding results with certain search queries

StabilityMatrix.Avalonia/App.axaml.cs

+1
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ internal static IServiceCollection ConfigureServices()
424424
jsonSerializerOptions.Converters.Add(new ObjectToInferredTypesConverter());
425425
jsonSerializerOptions.Converters.Add(new DefaultUnknownEnumConverter<CivitFileType>());
426426
jsonSerializerOptions.Converters.Add(new DefaultUnknownEnumConverter<CivitModelType>());
427+
jsonSerializerOptions.Converters.Add(new DefaultUnknownEnumConverter<CivitModelFormat>());
427428
jsonSerializerOptions.Converters.Add(
428429
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
429430
);
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,110 @@
1-
using System.Text.Json;
1+
using System.Diagnostics;
2+
using System.Diagnostics.CodeAnalysis;
3+
using System.Reflection;
4+
using System.Runtime.Serialization;
5+
using System.Text.Json;
26
using System.Text.Json.Serialization;
3-
using StabilityMatrix.Core.Extensions;
47

58
namespace StabilityMatrix.Core.Converters.Json;
69

7-
public class DefaultUnknownEnumConverter<T> : JsonConverter<T>
10+
public class DefaultUnknownEnumConverter<
11+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T
12+
> : JsonConverter<T>
813
where T : Enum
914
{
15+
/// <summary>
16+
/// Lazy initialization for <see cref="EnumMemberValues"/>.
17+
/// </summary>
18+
private readonly Lazy<Dictionary<string, T>> _enumMemberValuesLazy =
19+
new(
20+
() =>
21+
typeof(T)
22+
.GetFields()
23+
.Where(field => field.IsStatic)
24+
.Select(
25+
field =>
26+
new
27+
{
28+
FieldName = field.Name,
29+
FieldValue = (T)field.GetValue(null)!,
30+
EnumMemberValue = field
31+
.GetCustomAttributes<EnumMemberAttribute>(false)
32+
.FirstOrDefault()
33+
?.Value?.ToString()
34+
}
35+
)
36+
.ToDictionary(x => x.EnumMemberValue ?? x.FieldName, x => x.FieldValue)
37+
);
38+
39+
/// <summary>
40+
/// Gets a dictionary of enum member values, keyed by the EnumMember attribute value, or the field name if no EnumMember attribute is present.
41+
/// </summary>
42+
private Dictionary<string, T> EnumMemberValues => _enumMemberValuesLazy.Value;
43+
44+
/// <summary>
45+
/// Lazy initialization for <see cref="EnumMemberNames"/>.
46+
/// </summary>
47+
private readonly Lazy<Dictionary<T, string>> _enumMemberNamesLazy;
48+
49+
/// <summary>
50+
/// Gets a dictionary of enum member names, keyed by the enum member value.
51+
/// </summary>
52+
private Dictionary<T, string> EnumMemberNames => _enumMemberNamesLazy.Value;
53+
54+
/// <summary>
55+
/// Gets the value of the "Unknown" enum member, or the 0 value if no "Unknown" member is present.
56+
/// </summary>
57+
private T UnknownValue =>
58+
EnumMemberValues.TryGetValue("Unknown", out var res) ? res : (T)Enum.ToObject(typeof(T), 0);
59+
60+
/// <inheritdoc />
61+
public override bool HandleNull => true;
62+
63+
public DefaultUnknownEnumConverter()
64+
{
65+
_enumMemberNamesLazy = new Lazy<Dictionary<T, string>>(
66+
() => EnumMemberValues.ToDictionary(x => x.Value, x => x.Key)
67+
);
68+
}
69+
70+
/// <inheritdoc />
1071
public override T Read(
1172
ref Utf8JsonReader reader,
1273
Type typeToConvert,
1374
JsonSerializerOptions options
1475
)
1576
{
16-
if (reader.TokenType != JsonTokenType.String)
77+
if (reader.TokenType is not (JsonTokenType.String or JsonTokenType.PropertyName))
1778
{
18-
throw new JsonException();
79+
throw new JsonException("Expected String or PropertyName token");
1980
}
2081

21-
var enumText = reader.GetString()?.Replace(" ", "_");
22-
if (Enum.TryParse(typeof(T), enumText, true, out var result))
82+
if (reader.GetString() is { } readerString)
2383
{
24-
return (T)result!;
25-
}
84+
// First try get exact match
85+
if (EnumMemberValues.TryGetValue(readerString, out var enumMemberValue))
86+
{
87+
return enumMemberValue;
88+
}
2689

27-
// Unknown value handling
28-
if (Enum.TryParse(typeof(T), "Unknown", true, out var unknownResult))
29-
{
30-
return (T)unknownResult!;
90+
// Otherwise try get case-insensitive match
91+
if (
92+
EnumMemberValues.Keys.FirstOrDefault(
93+
key => key.Equals(readerString, StringComparison.OrdinalIgnoreCase)
94+
) is
95+
{ } enumMemberName
96+
)
97+
{
98+
return EnumMemberValues[enumMemberName];
99+
}
100+
101+
Debug.WriteLine($"Unknown enum member value for {typeToConvert}: {readerString}");
31102
}
32103

33-
throw new JsonException($"Unable to parse '{enumText}' to enum '{typeof(T)}'.");
104+
return UnknownValue;
34105
}
35106

107+
/// <inheritdoc />
36108
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
37109
{
38110
if (value == null)
@@ -41,49 +113,20 @@ public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOption
41113
return;
42114
}
43115

44-
writer.WriteStringValue(value.GetStringValue().Replace("_", " "));
116+
writer.WriteStringValue(EnumMemberNames[value]);
45117
}
46118

47119
/// <inheritdoc />
48120
public override T ReadAsPropertyName(
49121
ref Utf8JsonReader reader,
50122
Type typeToConvert,
51123
JsonSerializerOptions options
52-
)
53-
{
54-
if (reader.TokenType != JsonTokenType.PropertyName)
55-
{
56-
throw new JsonException();
57-
}
58-
59-
var enumText = reader.GetString()?.Replace(" ", "_");
60-
if (Enum.TryParse(typeof(T), enumText, true, out var result))
61-
{
62-
return (T)result!;
63-
}
64-
65-
// Unknown value handling
66-
if (Enum.TryParse(typeof(T), "Unknown", true, out var unknownResult))
67-
{
68-
return (T)unknownResult!;
69-
}
70-
71-
throw new JsonException($"Unable to parse '{enumText}' to enum '{typeof(T)}'.");
72-
}
124+
) => Read(ref reader, typeToConvert, options);
73125

74126
/// <inheritdoc />
75127
public override void WriteAsPropertyName(
76128
Utf8JsonWriter writer,
77129
T? value,
78130
JsonSerializerOptions options
79-
)
80-
{
81-
if (value == null)
82-
{
83-
writer.WriteNullValue();
84-
return;
85-
}
86-
87-
writer.WritePropertyName(value.GetStringValue().Replace("_", " "));
88-
}
131+
) => Write(writer, value, options);
89132
}

StabilityMatrix.Core/Helper/ArchiveHelper.cs

+14-16
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,16 @@ public static async Task AddToArchive7Z(string archivePath, string sourceDirecto
8686

8787
public static async Task<ArchiveInfo> Extract7Z(string archivePath, string extractDirectory)
8888
{
89-
var args =
90-
$"x {ProcessRunner.Quote(archivePath)} -o{ProcessRunner.Quote(extractDirectory)} -y";
89+
var result = await ProcessRunner
90+
.GetProcessResultAsync(
91+
SevenZipPath,
92+
new[] { "x", archivePath, "-o" + ProcessRunner.Quote(extractDirectory), "-y" }
93+
)
94+
.ConfigureAwait(false);
9195

92-
Logger.Debug($"Starting process '{SevenZipPath}' with arguments '{args}'");
96+
result.EnsureSuccessExitCode();
9397

94-
using var process = new Process();
95-
process.StartInfo = new ProcessStartInfo(SevenZipPath, args)
96-
{
97-
RedirectStandardOutput = true,
98-
RedirectStandardError = true,
99-
UseShellExecute = false,
100-
CreateNoWindow = true
101-
};
102-
process.Start();
103-
await ProcessRunner.WaitForExitConditionAsync(process);
104-
var output = await process.StandardOutput.ReadToEndAsync();
98+
var output = result.StandardOutput ?? "";
10599

106100
try
107101
{
@@ -153,8 +147,12 @@ IProgress<ProgressReport> progress
153147
$"x {ProcessRunner.Quote(archivePath)} -o{ProcessRunner.Quote(extractDirectory)} -y -bsp1";
154148
Logger.Debug($"Starting process '{SevenZipPath}' with arguments '{args}'");
155149

156-
var process = ProcessRunner.StartProcess(SevenZipPath, args, outputDataReceived: onOutput);
157-
await ProcessRunner.WaitForExitConditionAsync(process);
150+
using var process = ProcessRunner.StartProcess(
151+
SevenZipPath,
152+
args,
153+
outputDataReceived: onOutput
154+
);
155+
await ProcessRunner.WaitForExitConditionAsync(process).ConfigureAwait(false);
158156

159157
progress.Report(new ProgressReport(1f, "Finished extracting", type: ProgressType.Extract));
160158

Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
using System.Text.Json.Serialization;
1+
using System.Runtime.Serialization;
2+
using System.Text.Json.Serialization;
23
using StabilityMatrix.Core.Converters.Json;
34

45
namespace StabilityMatrix.Core.Models.Api;
56

67
[JsonConverter(typeof(DefaultUnknownEnumConverter<CivitFileType>))]
78
public enum CivitFileType
89
{
10+
Unknown,
911
Model,
1012
VAE,
11-
Training_Data,
12-
Unknown,
13+
14+
[EnumMember(Value = "Training Data")]
15+
TrainingData
1316
}
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
using System.Text.Json.Serialization;
2+
using StabilityMatrix.Core.Converters.Json;
23

34
namespace StabilityMatrix.Core.Models.Api;
45

5-
6-
[JsonConverter(typeof(JsonStringEnumConverter))]
6+
[JsonConverter(typeof(DefaultUnknownEnumConverter<CivitModelFormat>))]
77
public enum CivitModelFormat
88
{
9+
Unknown,
910
SafeTensor,
1011
PickleTensor,
12+
Diffusers,
1113
Other
1214
}

StabilityMatrix.Core/Models/Api/CivitModelType.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace StabilityMatrix.Core.Models.Api;
99
[SuppressMessage("ReSharper", "InconsistentNaming")]
1010
public enum CivitModelType
1111
{
12+
Unknown,
13+
1214
[ConvertTo<SharedFolderType>(SharedFolderType.StableDiffusion)]
1315
Checkpoint,
1416

@@ -39,6 +41,5 @@ public enum CivitModelType
3941
Wildcards,
4042
Workflows,
4143
Other,
42-
All,
43-
Unknown
44+
All
4445
}

StabilityMatrix.Core/Processes/ProcessRunner.cs

+20-46
Original file line numberDiff line numberDiff line change
@@ -401,35 +401,6 @@ public static string Quote(string argument)
401401
return inner.Contains(' ') ? $"\"{inner}\"" : argument;
402402
}
403403

404-
/// <summary>
405-
/// Check if the process exited with the expected exit code.
406-
/// </summary>
407-
/// <param name="process">Process to check.</param>
408-
/// <param name="expectedExitCode">Expected exit code.</param>
409-
/// <param name="stdout">Process stdout.</param>
410-
/// <param name="stderr">Process stderr.</param>
411-
/// <exception cref="ProcessException">Thrown if exit code does not match expected value.</exception>
412-
// ReSharper disable once MemberCanBePrivate.Global
413-
public static Task ValidateExitConditionAsync(
414-
Process process,
415-
int expectedExitCode = 0,
416-
string? stdout = null,
417-
string? stderr = null
418-
)
419-
{
420-
var exitCode = process.ExitCode;
421-
if (exitCode == expectedExitCode)
422-
{
423-
return Task.CompletedTask;
424-
}
425-
426-
var pName = process.StartInfo.FileName;
427-
var msg =
428-
$"Process {pName} failed with exit-code {exitCode}. stdout: '{stdout}', stderr: '{stderr}'";
429-
Logger.Error(msg);
430-
throw new ProcessException(msg);
431-
}
432-
433404
/// <summary>
434405
/// Waits for process to exit, then validates exit code.
435406
/// </summary>
@@ -443,25 +414,28 @@ public static async Task WaitForExitConditionAsync(
443414
CancellationToken cancelToken = default
444415
)
445416
{
446-
if (process is AnsiProcess)
417+
if (!process.HasExited)
447418
{
448-
throw new ArgumentException(
449-
$"{nameof(WaitForExitConditionAsync)} does not support AnsiProcess, which uses custom async data reading",
450-
nameof(process)
451-
);
419+
await process.WaitForExitAsync(cancelToken).ConfigureAwait(false);
452420
}
453421

454-
var stdout = new StringBuilder();
455-
var stderr = new StringBuilder();
456-
process.OutputDataReceived += (_, args) => stdout.Append(args.Data);
457-
process.ErrorDataReceived += (_, args) => stderr.Append(args.Data);
458-
await process.WaitForExitAsync(cancelToken).ConfigureAwait(false);
459-
await ValidateExitConditionAsync(
460-
process,
461-
expectedExitCode,
462-
stdout.ToString(),
463-
stderr.ToString()
464-
)
465-
.ConfigureAwait(false);
422+
if (process.ExitCode == expectedExitCode)
423+
{
424+
return;
425+
}
426+
427+
// Accessing ProcessName may error on some platforms
428+
string? processName = null;
429+
try
430+
{
431+
processName = process.ProcessName;
432+
}
433+
catch (SystemException) { }
434+
435+
throw new ProcessException(
436+
"Process "
437+
+ (processName == null ? "" : processName + " ")
438+
+ $"failed with exit-code {process.ExitCode}."
439+
);
466440
}
467441
}

0 commit comments

Comments
 (0)