From e8a336f0f3b303a013c16475d6c6463ac50fd59b Mon Sep 17 00:00:00 2001 From: Ionite Date: Thu, 5 Oct 2023 23:46:46 -0400 Subject: [PATCH 01/10] Add `--skip-install` default arg for A3WebUI --- StabilityMatrix.Core/Models/Packages/A3WebUI.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/StabilityMatrix.Core/Models/Packages/A3WebUI.cs b/StabilityMatrix.Core/Models/Packages/A3WebUI.cs index 5d0a275d..270d8cc8 100644 --- a/StabilityMatrix.Core/Models/Packages/A3WebUI.cs +++ b/StabilityMatrix.Core/Models/Packages/A3WebUI.cs @@ -144,6 +144,13 @@ public class A3WebUI : BaseGitPackage InitialValue = false, Options = new() { "--no-download-sd-model" } }, + new() + { + Name = "Skip Install", + Type = LaunchOptionType.Bool, + InitialValue = true, + Options = new() { "--skip-install" } + }, LaunchOptionDefinition.Extras }; From 65a993921d93d1fdac2c9f705b00fbc4c1b9bc19 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 6 Oct 2023 00:36:23 -0400 Subject: [PATCH 02/10] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c739ad8a..40866bc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ 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.5.1 +### Added +- `--skip-install` default launch argument for Automatic1111 Package + ## v2.5.0 ### Added - Added Inference, a built-in native Stable Diffusion interface, powered by ComfyUI From 31e2a470e2d464c8eb20ab6a78099b83849611b6 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 6 Oct 2023 14:45:49 -0400 Subject: [PATCH 03/10] Add missing changelog entry for 2.5.0 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40866bc0..c0389a75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 - Added the ability to Favorite models in the Model Browser - Added "Favorites" sort option to the Model Browser - Added notification flyout for new available updates. Dismiss to hide until the next update version. +- Added Italian UI language options, thanks to Marco Capelli for the translations ### Changed - Model Browser page size is now 20 instead of 14 - Update changelog now only shows the difference between the current version and the latest version From 4fe126b63ce93f940f162bf01cd218cc26b0bdcb Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 6 Oct 2023 14:57:21 -0400 Subject: [PATCH 04/10] Use fixed culture for double parsing --- StabilityMatrix.Avalonia/Models/Inference/Prompt.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/StabilityMatrix.Avalonia/Models/Inference/Prompt.cs b/StabilityMatrix.Avalonia/Models/Inference/Prompt.cs index a7a7ba73..73965033 100644 --- a/StabilityMatrix.Avalonia/Models/Inference/Prompt.cs +++ b/StabilityMatrix.Avalonia/Models/Inference/Prompt.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.IO; using System.Linq; using System.Text; @@ -19,6 +20,8 @@ namespace StabilityMatrix.Avalonia.Models.Inference; public record Prompt { + private static readonly CultureInfo NumericParsingCulture = CultureInfo.GetCultureInfo("en-US"); + public required string RawText { get; init; } public required ITokenizeLineResult TokenizeResult { get; init; } @@ -292,7 +295,7 @@ public record Prompt ]; // Convert to double - if (!double.TryParse(modelWeight, out var weightValue)) + if (!double.TryParse(modelWeight, NumericParsingCulture, out var weightValue)) { throw PromptValidationError.Network_InvalidWeight( currentToken.StartIndex, From caae3bab98900eae55704145a0a0b19b321dcb6e Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 6 Oct 2023 14:58:32 -0400 Subject: [PATCH 05/10] Update changelog for fix --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0389a75..2fc8c2ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2 ## v2.5.1 ### Added - `--skip-install` default launch argument for Automatic1111 Package +### Fixed +- Fixed Prompt weights showing syntax error in locales where decimal separator is not a period ## v2.5.0 ### Added From 4575eef5eb020160ec56714db153612c40acbe80 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 6 Oct 2023 15:47:19 -0400 Subject: [PATCH 06/10] Remove duplicate tmLanguage resource --- StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj index e07e8f30..4b71dec1 100644 --- a/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj +++ b/StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj @@ -72,7 +72,6 @@ - From e3928807a1512859ef0307351c368db713db3a9e Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 6 Oct 2023 15:55:14 -0400 Subject: [PATCH 07/10] Switch to NSubtitute for tests --- .../Avalonia/LoadableViewModelBaseTests.cs | 110 ++++++++---------- .../StabilityMatrix.Tests.csproj | 7 +- 2 files changed, 53 insertions(+), 64 deletions(-) diff --git a/StabilityMatrix.Tests/Avalonia/LoadableViewModelBaseTests.cs b/StabilityMatrix.Tests/Avalonia/LoadableViewModelBaseTests.cs index 7691bc87..c7c8ddac 100644 --- a/StabilityMatrix.Tests/Avalonia/LoadableViewModelBaseTests.cs +++ b/StabilityMatrix.Tests/Avalonia/LoadableViewModelBaseTests.cs @@ -2,9 +2,8 @@ using System.Text.Json.Serialization; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Moq; +using NSubstitute; using StabilityMatrix.Avalonia.Models; -using StabilityMatrix.Avalonia.ViewModels; using StabilityMatrix.Avalonia.ViewModels.Base; #pragma warning disable CS0657 // Not a valid attribute location for this declaration @@ -16,9 +15,9 @@ public class TestLoadableViewModel : LoadableViewModelBase { [JsonInclude] public string? Included { get; set; } - + public int Id { get; set; } - + [JsonIgnore] public int Ignored { get; set; } } @@ -43,10 +42,10 @@ public partial class TestLoadableViewModelObservable : LoadableViewModelBase [ObservableProperty] [property: JsonIgnore] private string? title; - + [ObservableProperty] private int id; - + [RelayCommand] private void TestCommand() { @@ -76,9 +75,9 @@ public class LoadableViewModelBaseTests Id = 123, Ignored = 456, }; - + var state = vm.SaveStateToJsonObject(); - + // [JsonInclude] and not marked property should be serialized. // Ignored property should be ignored. Assert.AreEqual(2, state.Count); @@ -90,17 +89,13 @@ public class LoadableViewModelBaseTests public void TestSaveStateToJsonObject_Observable() { // Mvvm ObservableProperty should be serialized. - var vm = new TestLoadableViewModelObservable - { - Title = "abc", - Id = 123, - }; + var vm = new TestLoadableViewModelObservable { Title = "abc", Id = 123, }; var state = vm.SaveStateToJsonObject(); - + // Title should be ignored since it has [JsonIgnore] // Command should be ignored from excluded type rules // Id should be serialized - + Assert.AreEqual(1, state.Count); Assert.AreEqual(123, state["Id"].Deserialize()); } @@ -110,23 +105,20 @@ public class LoadableViewModelBaseTests { // Properties of type IJsonLoadableState should be serialized by calling their // SaveStateToJsonObject method. - + // Make a mock IJsonLoadableState - var mockState = new Mock(); + var mockState = Substitute.For(); + + var vm = new TestLoadableViewModelNestedInterface { NestedState = mockState }; - var vm = new TestLoadableViewModelNestedInterface - { - NestedState = mockState.Object - }; - // Serialize var state = vm.SaveStateToJsonObject(); - + // Check results Assert.AreEqual(1, state.Count); - + // Check that SaveStateToJsonObject was called - mockState.Verify(x => x.SaveStateToJsonObject(), Times.Once); + mockState.Received().SaveStateToJsonObject(); } [TestMethod] @@ -139,20 +131,20 @@ public class LoadableViewModelBaseTests Id = 123, Ignored = 456, }; - + var state = vm.SaveStateToJsonObject(); - + // Create a new instance and load the state var vm2 = new TestLoadableViewModel(); vm2.LoadStateFromJsonObject(state); - + // Check [JsonInclude] and not marked property was loaded Assert.AreEqual("abc", vm2.Included); Assert.AreEqual(123, vm2.Id); // Check ignored property was not loaded Assert.AreEqual(0, vm2.Ignored); } - + [TestMethod] public void TestLoadStateFromJsonObject_Nested_DefaultCtor() { @@ -163,27 +155,24 @@ public class LoadableViewModelBaseTests Id = 123, Ignored = 456, }; - - var vm = new TestLoadableViewModelNested - { - NestedState = nested - }; - + + var vm = new TestLoadableViewModelNested { NestedState = nested }; + var state = vm.SaveStateToJsonObject(); - + // Create a new instance with null NestedState, rely on default ctor var vm2 = new TestLoadableViewModelNested(); vm2.LoadStateFromJsonObject(state); - + // Check nested state was loaded Assert.IsNotNull(vm2.NestedState); - - var loadedNested = (TestLoadableViewModel) vm2.NestedState; + + var loadedNested = (TestLoadableViewModel)vm2.NestedState; Assert.AreEqual("abc", loadedNested.Included); Assert.AreEqual(123, loadedNested.Id); Assert.AreEqual(0, loadedNested.Ignored); } - + [TestMethod] public void TestLoadStateFromJsonObject_Nested_Existing() { @@ -194,63 +183,60 @@ public class LoadableViewModelBaseTests Id = 123, Ignored = 456, }; - - var vm = new TestLoadableViewModelNestedInterface - { - NestedState = nested - }; - + + var vm = new TestLoadableViewModelNestedInterface { NestedState = nested }; + var state = vm.SaveStateToJsonObject(); - + // Create a new instance with existing NestedState var vm2 = new TestLoadableViewModelNestedInterface { NestedState = new TestLoadableViewModel() }; vm2.LoadStateFromJsonObject(state); - + // Check nested state was loaded Assert.IsNotNull(vm2.NestedState); - - var loadedNested = (TestLoadableViewModel) vm2.NestedState; + + var loadedNested = (TestLoadableViewModel)vm2.NestedState; Assert.AreEqual("abc", loadedNested.Included); Assert.AreEqual(123, loadedNested.Id); Assert.AreEqual(0, loadedNested.Ignored); } - + [TestMethod] public void TestLoadStateFromJsonObject_ReadOnly() { var vm = new TestLoadableViewModelReadOnly(456); - + var state = vm.SaveStateToJsonObject(); - + // Check no properties were serialized Assert.AreEqual(0, state.Count); - + // Create a new instance and load the state var vm2 = new TestLoadableViewModelReadOnly(123); vm2.LoadStateFromJsonObject(state); - + // Read only property should have been ignored Assert.AreEqual(123, vm2.ReadOnly); } - + [TestMethod] public void TestLoadStateFromJsonObject_ReadOnlyLoadable() { var vm = new TestLoadableViewModelReadOnlyLoadable { - ReadOnlyLoadable = - { - Included = "abc-123" - } + ReadOnlyLoadable = { Included = "abc-123" } }; var state = vm.SaveStateToJsonObject(); - + // Check readonly loadable property was serialized Assert.AreEqual(1, state.Count); - Assert.AreEqual("abc-123", state["ReadOnlyLoadable"].Deserialize()!.Included); + Assert.AreEqual( + "abc-123", + state["ReadOnlyLoadable"].Deserialize()!.Included + ); } } diff --git a/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj b/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj index 7bd9f4d0..e82a92a8 100644 --- a/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj +++ b/StabilityMatrix.Tests/StabilityMatrix.Tests.csproj @@ -15,13 +15,13 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -32,7 +32,10 @@ - + + + + From 3664b1d3ad57cf4d167464c1a8199f6e220618ee Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 6 Oct 2023 16:07:04 -0400 Subject: [PATCH 08/10] Add prompt tests --- .../Models/TagCompletion/TokenizerProvider.cs | 15 ++- StabilityMatrix.Tests/Avalonia/PromptTests.cs | 114 ++++++++++++++++++ 2 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 StabilityMatrix.Tests/Avalonia/PromptTests.cs diff --git a/StabilityMatrix.Avalonia/Models/TagCompletion/TokenizerProvider.cs b/StabilityMatrix.Avalonia/Models/TagCompletion/TokenizerProvider.cs index 3a958878..74b22d44 100644 --- a/StabilityMatrix.Avalonia/Models/TagCompletion/TokenizerProvider.cs +++ b/StabilityMatrix.Avalonia/Models/TagCompletion/TokenizerProvider.cs @@ -8,16 +8,15 @@ namespace StabilityMatrix.Avalonia.Models.TagCompletion; public class TokenizerProvider : ITokenizerProvider { private readonly Registry registry = new(new RegistryOptions(ThemeName.DarkPlus)); - private IGrammar grammar; - - public TokenizerProvider() - { - SetPromptGrammar(); - } - + private IGrammar? grammar; + /// public ITokenizeLineResult TokenizeLine(string lineText) { + if (grammar is null) + { + SetPromptGrammar(); + } return grammar.TokenizeLine(lineText); } @@ -27,7 +26,7 @@ public class TokenizerProvider : ITokenizerProvider using var stream = Assets.ImagePromptLanguageJson.Open(); grammar = registry.LoadGrammarFromStream(stream); } - + public void SetGrammar(string scopeName) { grammar = registry.LoadGrammar(scopeName); diff --git a/StabilityMatrix.Tests/Avalonia/PromptTests.cs b/StabilityMatrix.Tests/Avalonia/PromptTests.cs new file mode 100644 index 00000000..60b556a0 --- /dev/null +++ b/StabilityMatrix.Tests/Avalonia/PromptTests.cs @@ -0,0 +1,114 @@ +using System.Globalization; +using System.Reflection; +using NSubstitute; +using StabilityMatrix.Avalonia.Extensions; +using StabilityMatrix.Avalonia.Models.Inference; +using StabilityMatrix.Avalonia.Models.TagCompletion; +using StabilityMatrix.Core.Models.Tokens; +using TextMateSharp.Grammars; +using TextMateSharp.Registry; + +namespace StabilityMatrix.Tests.Avalonia; + +[TestClass] +public class PromptTests +{ + private ITokenizerProvider tokenizerProvider = null!; + + [TestInitialize] + public void TestInitialize() + { + tokenizerProvider = Substitute.For(); + + var promptSyntaxFile = Assembly + .GetExecutingAssembly() + .GetManifestResourceStream("StabilityMatrix.Tests.ImagePrompt.tmLanguage.json")!; + + var registry = new Registry(new RegistryOptions(ThemeName.DarkPlus)); + var grammar = registry.LoadGrammarFromStream(promptSyntaxFile); + + tokenizerProvider + .TokenizeLine(Arg.Any()) + .Returns(x => grammar.TokenizeLine(x.Arg())); + } + + [TestMethod] + public void TestPromptProcessedText() + { + var prompt = Prompt.FromRawText("test", tokenizerProvider); + + prompt.Process(); + + Assert.AreEqual("test", prompt.ProcessedText); + } + + [TestMethod] + public void TestPromptWeightParsing() + { + var prompt = Prompt.FromRawText("", tokenizerProvider); + + prompt.Process(); + + // Output should have no loras + Assert.AreEqual("", prompt.ProcessedText); + + var network = prompt.ExtraNetworks[0]; + + Assert.AreEqual(PromptExtraNetworkType.Lora, network.Type); + Assert.AreEqual("my_model", network.Name); + Assert.AreEqual(1.5f, network.ModelWeight); + } + + /// + /// Tests that we can parse decimal numbers with different cultures + /// + [TestMethod] + public void TestPromptWeightParsing_DecimalSeparatorCultures_ShouldParse() + { + var prompt = Prompt.FromRawText("", tokenizerProvider); + + // Cultures like de-DE use commas as decimal separators, check that we can parse those too + ExecuteWithCulture(prompt.Process, CultureInfo.GetCultureInfo("de-DE")); + + // Output should have no loras + Assert.AreEqual("", prompt.ProcessedText); + + var network = prompt.ExtraNetworks![0]; + + Assert.AreEqual(PromptExtraNetworkType.Lora, network.Type); + Assert.AreEqual("my_model", network.Name); + Assert.AreEqual(1.5f, network.ModelWeight); + } + + private static T? ExecuteWithCulture(Func func, CultureInfo culture) + { + var result = default(T); + + var thread = new Thread(() => + { + result = func(); + }) + { + CurrentCulture = culture + }; + + thread.Start(); + thread.Join(); + + return result; + } + + private static void ExecuteWithCulture(Action func, CultureInfo culture) + { + var thread = new Thread(() => + { + func(); + }) + { + CurrentCulture = culture + }; + + thread.Start(); + thread.Join(); + } +} From 1e537d2b2d82aeb286bfe3cf21a7e8bdd6ed3929 Mon Sep 17 00:00:00 2001 From: Ionite Date: Fri, 6 Oct 2023 16:07:12 -0400 Subject: [PATCH 09/10] Fix prompt culture parsing --- StabilityMatrix.Avalonia/Models/Inference/Prompt.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/StabilityMatrix.Avalonia/Models/Inference/Prompt.cs b/StabilityMatrix.Avalonia/Models/Inference/Prompt.cs index 73965033..c4c76fa4 100644 --- a/StabilityMatrix.Avalonia/Models/Inference/Prompt.cs +++ b/StabilityMatrix.Avalonia/Models/Inference/Prompt.cs @@ -20,8 +20,6 @@ namespace StabilityMatrix.Avalonia.Models.Inference; public record Prompt { - private static readonly CultureInfo NumericParsingCulture = CultureInfo.GetCultureInfo("en-US"); - public required string RawText { get; init; } public required ITokenizeLineResult TokenizeResult { get; init; } @@ -295,7 +293,9 @@ public record Prompt ]; // Convert to double - if (!double.TryParse(modelWeight, NumericParsingCulture, out var weightValue)) + if ( + !double.TryParse(modelWeight, CultureInfo.InvariantCulture, out var weightValue) + ) { throw PromptValidationError.Network_InvalidWeight( currentToken.StartIndex, From 908a683531ac6761907b15637e58d53d7049dff0 Mon Sep 17 00:00:00 2001 From: JT Date: Fri, 6 Oct 2023 19:14:54 -0700 Subject: [PATCH 10/10] Disable packages combobox when package is running --- StabilityMatrix.Avalonia/Views/LaunchPageView.axaml | 1 + 1 file changed, 1 insertion(+) diff --git a/StabilityMatrix.Avalonia/Views/LaunchPageView.axaml b/StabilityMatrix.Avalonia/Views/LaunchPageView.axaml index 250f5a30..e77a3cfa 100644 --- a/StabilityMatrix.Avalonia/Views/LaunchPageView.axaml +++ b/StabilityMatrix.Avalonia/Views/LaunchPageView.axaml @@ -121,6 +121,7 @@ Margin="8,8,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Top" + IsEnabled="{Binding RunningPackage, Converter={x:Static ObjectConverters.IsNull}}" ItemsSource="{Binding InstalledPackages}" SelectedItem="{Binding SelectedPackage}">