Browse Source

Merge pull request #412 from ionite34/downmerge

Downmerge
pull/438/head
JT 11 months ago committed by GitHub
parent
commit
5267d8bcef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 17
      CHANGELOG.md
  2. 2
      README.md
  3. 20
      StabilityMatrix.Avalonia/Helpers/UriHandler.cs
  4. 21
      StabilityMatrix.Avalonia/Languages/Cultures.cs
  5. 783
      StabilityMatrix.Avalonia/Languages/Resources.tr-TR.resx
  6. 8
      StabilityMatrix.Avalonia/ViewModels/Inference/FreeUCardViewModel.cs
  7. 2
      StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToImageViewModel.cs
  8. 2
      StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs
  9. 84
      StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs
  10. 36
      StabilityMatrix.Core/Models/FileInterfaces/FileSystemPath.cs
  11. 92
      StabilityMatrix.Core/Models/Packages/A3WebUI.cs
  12. 236
      StabilityMatrix.Core/Models/Packages/ComfyUI.cs
  13. 56
      StabilityMatrix.Core/Models/Packages/Fooocus.cs
  14. 97
      StabilityMatrix.Core/Models/Packages/RuinedFooocus.cs
  15. 61
      StabilityMatrix.Core/Processes/ProcessArgsBuilder.cs
  16. 33
      StabilityMatrix.Core/Python/PipInstallArgs.cs
  17. 87
      StabilityMatrix.Core/Python/PipPackageSpecifier.cs

17
CHANGELOG.md

@ -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.7.2
### Changed
- Changed Symlink shared folder link targets for Automatic1111 and ComfyUI. From `ControlNet -> models/controlnet` to `ControlNet -> models/controlnet/ControlNet` and `T2IAdapter -> models/controlnet/T2IAdapter`.
- Changed FreeU defaults to match recommended SD1.5 defaults
- Changed default denoise strength from 1.0 to 0.7
### Fixed
- Fixed ControlNet / T2IAdapter shared folder links for Automatic1111 conflicting with each other
- Fixed URIScheme registration errors on Linux
- Fixed RuinedFooocus missing output folder on startup
- Fixed incorrect Fooocus VRAM launch arguments
## v2.7.1
### Added
- Added Turkish UI language option, thanks to Progresor for the translation
### Fixed
- Fixed Inference Image to Image projects missing denoise strength setting
## v2.7.0
### Added
#### General

2
README.md

@ -104,6 +104,8 @@ Stability Matrix is now available in the following languages, thanks to our comm
- Lautaroturina
- 🇷🇺 Русский
- aolko
- 🇹🇷 Türkçe
- Progresor
If you would like to contribute a translation, please create an issue or contact us on Discord. Include an email where we'll send an invite to our [POEditor](https://poeditor.com/) project.

20
StabilityMatrix.Avalonia/Helpers/UriHandler.cs

@ -56,8 +56,6 @@ public class UriHandler
Environment.Exit(0);
}
public void Callback() { }
public void RegisterUriScheme()
{
if (Compat.IsWindows)
@ -66,7 +64,17 @@ public class UriHandler
}
else if (Compat.IsLinux)
{
RegisterUriSchemeUnix();
// Try to register on unix but ignore errors
// Library does not support some distros
try
{
RegisterUriSchemeUnix();
}
catch (Exception e)
{
Debug.WriteLine(e);
Console.WriteLine(e);
}
}
}
@ -92,11 +100,7 @@ public class UriHandler
private void RegisterUriSchemeUnix()
{
var service = URISchemeServiceFactory.GetURISchemeSerivce(
Scheme,
Description,
Compat.AppCurrentPath.FullPath
);
var service = URISchemeServiceFactory.GetURISchemeSerivce(Scheme, Description, Compat.AppCurrentPath.FullPath);
service.Set();
}
}

21
StabilityMatrix.Avalonia/Languages/Cultures.cs

@ -13,10 +13,7 @@ public static class Cultures
public static CultureInfo? Current => Resources.Culture;
public static readonly Dictionary<string, CultureInfo> SupportedCulturesByCode = new Dictionary<
string,
CultureInfo
>
public static readonly Dictionary<string, CultureInfo> SupportedCulturesByCode = new Dictionary<string, CultureInfo>
{
["en-US"] = Default,
["ja-JP"] = new("ja-JP"),
@ -25,18 +22,15 @@ public static class Cultures
["it-IT"] = new("it-IT"),
["fr-FR"] = new("fr-FR"),
["es"] = new("es"),
["ru-RU"] = new("ru-RU")
["ru-RU"] = new("ru-RU"),
["tr-TR"] = new("tr-TR")
};
public static IReadOnlyList<CultureInfo> SupportedCultures =>
SupportedCulturesByCode.Values.ToImmutableList();
public static IReadOnlyList<CultureInfo> SupportedCultures => SupportedCulturesByCode.Values.ToImmutableList();
public static CultureInfo GetSupportedCultureOrDefault(string? cultureCode)
{
if (
cultureCode is null
|| !SupportedCulturesByCode.TryGetValue(cultureCode, out var culture)
)
if (cultureCode is null || !SupportedCulturesByCode.TryGetValue(cultureCode, out var culture))
{
return Default;
}
@ -54,10 +48,7 @@ public static class Cultures
public static bool TrySetSupportedCulture(string? cultureCode)
{
if (
cultureCode is null
|| !SupportedCulturesByCode.TryGetValue(cultureCode, out var culture)
)
if (cultureCode is null || !SupportedCulturesByCode.TryGetValue(cultureCode, out var culture))
{
return false;
}

783
StabilityMatrix.Avalonia/Languages/Resources.tr-TR.resx

@ -0,0 +1,783 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Action_Launch" xml:space="preserve">
<value>Başlat</value>
</data>
<data name="Action_Quit" xml:space="preserve">
<value>Çıkış</value>
</data>
<data name="Action_Save" xml:space="preserve">
<value>Kaydet</value>
</data>
<data name="Action_Cancel" xml:space="preserve">
<value>İptal</value>
</data>
<data name="Label_Language" xml:space="preserve">
<value>Dil</value>
</data>
<data name="Text_RelaunchRequiredToApplyLanguage" xml:space="preserve">
<value>Yeni dil seçeneğinin etkili olması için yeniden başlatma gerekiyor</value>
</data>
<data name="Action_Relaunch" xml:space="preserve">
<value>Yeniden başlat</value>
</data>
<data name="Action_RelaunchLater" xml:space="preserve">
<value>Daha Sonra Yeniden Başlat</value>
</data>
<data name="Label_RelaunchRequired" xml:space="preserve">
<value>Yeniden Başlatma Gerekli</value>
</data>
<data name="Label_UnknownPackage" xml:space="preserve">
<value>Bilinmeyen Paket</value>
</data>
<data name="Action_Import" xml:space="preserve">
<value>İçe Aktar</value>
</data>
<data name="Label_PackageType" xml:space="preserve">
<value>Paket Türü</value>
</data>
<data name="Label_Version" xml:space="preserve">
<value>Sürüm</value>
</data>
<data name="Label_VersionType" xml:space="preserve">
<value>Sürüm Türü</value>
</data>
<data name="Label_Releases" xml:space="preserve">
<value>Sürümler</value>
</data>
<data name="Label_Branches" xml:space="preserve">
<value>Dallar</value>
</data>
<data name="Label_DragAndDropCheckpointsHereToImport" xml:space="preserve">
<value>İçe aktarmak için chekpoints&apos;leri buraya sürükleyip bırakın</value>
</data>
<data name="Label_Emphasis" xml:space="preserve">
<value>Vurgu</value>
</data>
<data name="Label_Deemphasis" xml:space="preserve">
<value>Vurguyu Kaldırma</value>
</data>
<data name="Label_EmbeddingsOrTextualInversion" xml:space="preserve">
<value>Emebeddings / Textual Inversion</value>
</data>
<data name="Label_NetworksLoraOrLycoris" xml:space="preserve">
<value>Networks (Lora / LyCORIS)</value>
</data>
<data name="Label_Comments" xml:space="preserve">
<value>Yorumlar</value>
</data>
<data name="Label_ShowPixelGridAtHighZoomLevels" xml:space="preserve">
<value>Yüksek yakınlaştırma seviyesinde piksel ızgarasını göster</value>
</data>
<data name="Label_Steps" xml:space="preserve">
<value>Adımlar</value>
</data>
<data name="Label_StepsBase" xml:space="preserve">
<value>Adımlar - Temel</value>
</data>
<data name="Label_StepsRefiner" xml:space="preserve">
<value>Adımlar - İyileştirici</value>
</data>
<data name="Label_CFGScale" xml:space="preserve">
<value>CFG Ölçeği</value>
</data>
<data name="Label_DenoisingStrength" xml:space="preserve">
<value>Gürültü Azaltma Gücü</value>
</data>
<data name="Label_Width" xml:space="preserve">
<value>Genişlik</value>
</data>
<data name="Label_Height" xml:space="preserve">
<value>Yükseklik</value>
</data>
<data name="Label_Refiner" xml:space="preserve">
<value>İyileştirici</value>
</data>
<data name="Label_VAE" xml:space="preserve">
<value>VAE</value>
</data>
<data name="Label_Model" xml:space="preserve">
<value>Model</value>
</data>
<data name="Action_Connect" xml:space="preserve">
<value>Bağlan</value>
</data>
<data name="Label_ConnectingEllipsis" xml:space="preserve">
<value>Bağlanıyor...</value>
</data>
<data name="Action_Close" xml:space="preserve">
<value>Kapat</value>
</data>
<data name="Label_WaitingToConnectEllipsis" xml:space="preserve">
<value>Bağlanmayı bekliyor...</value>
</data>
<data name="Label_UpdateAvailable" xml:space="preserve">
<value>Güncelleme Mevcut</value>
</data>
<data name="Label_BecomeAPatron" xml:space="preserve">
<value>Sponsor Ol</value>
</data>
<data name="Label_JoinDiscord" xml:space="preserve">
<value>Discord Sunucusuna Katıl</value>
</data>
<data name="Label_Downloads" xml:space="preserve">
<value>İndirmeler</value>
</data>
<data name="Action_Install" xml:space="preserve">
<value>Yükle</value>
</data>
<data name="Label_SkipSetup" xml:space="preserve">
<value>İlk kurulum işlemini atla</value>
</data>
<data name="Label_UnexpectedErrorOccurred" xml:space="preserve">
<value>Beklenmeyen bir hata oluştu</value>
</data>
<data name="Action_ExitApplication" xml:space="preserve">
<value>Uygulamadan Çık</value>
</data>
<data name="Label_DisplayName" xml:space="preserve">
<value>Görünen Ad</value>
</data>
<data name="Label_InstallationWithThisNameExists" xml:space="preserve">
<value>Bu ada sahip bir yükleme zaten mevcut.</value>
</data>
<data name="Label_PleaseChooseDifferentName" xml:space="preserve">
<value>Lütfen farklı bir ad seçin veya başka bir yükleme konumu seçin.</value>
</data>
<data name="Label_AdvancedOptions" xml:space="preserve">
<value>Gelişmiş Seçenekler</value>
</data>
<data name="Label_Commit" xml:space="preserve">
<value>İşlemek</value>
</data>
<data name="Label_SharedModelFolderStrategy" xml:space="preserve">
<value>Paylaşılan Model Klasör Stratejisi</value>
</data>
<data name="Label_PyTorchVersion" xml:space="preserve">
<value>PyTorch Sürümü</value>
</data>
<data name="Label_CloseDialogWhenFinished" xml:space="preserve">
<value>Bittiğinde iletişim kutusunu kapat</value>
</data>
<data name="Label_DataDirectory" xml:space="preserve">
<value>Veri Klasörü</value>
</data>
<data name="Label_DataDirectoryExplanation" xml:space="preserve">
<value>Model chekpoints, Lora&apos;ların, web UI&apos;lerin, ayarların vb. kurulacağı yer burasıdır.</value>
</data>
<data name="Label_FatWarning" xml:space="preserve">
<value>FAT32 veya exFAT sürücü kullanırken hatalarla karşılaşabilirsiniz. Daha sorunsuz bir deneyim için farklı bir sürücü seçin.</value>
</data>
<data name="Label_PortableMode" xml:space="preserve">
<value>Taşınabilir Mod</value>
</data>
<data name="Label_PortableModeExplanation" xml:space="preserve">
<value>Taşınabilir Modda tüm veriler ve ayarlar uygulamayla aynı dizinde saklanacaktır. Uygulamayı &apos;Veri&apos; klasörüyle birlikte farklı bir konuma veya bilgisayara taşıyabileceksiniz.</value>
</data>
<data name="Action_Continue" xml:space="preserve">
<value>Devam</value>
</data>
<data name="Label_PreviousImage" xml:space="preserve">
<value>Önceki Resim</value>
</data>
<data name="Label_NextImage" xml:space="preserve">
<value>Sonraki Resim</value>
</data>
<data name="Label_ModelDescription" xml:space="preserve">
<value>Model Açıklaması</value>
</data>
<data name="Label_NewVersionAvailable" xml:space="preserve">
<value>Stabilite Matrisi için yeni bir sürüm mevcut!</value>
</data>
<data name="Label_ImportLatest" xml:space="preserve">
<value>En Yeniyi İçe Aktar -</value>
</data>
<data name="Label_AllVersions" xml:space="preserve">
<value>Tüm Sürümler</value>
</data>
<data name="Label_ModelSearchWatermark" xml:space="preserve">
<value>Modelleri, #etiketleri veya @kullanıcıları ara</value>
</data>
<data name="Action_Search" xml:space="preserve">
<value>Ara</value>
</data>
<data name="Label_Sort" xml:space="preserve">
<value>Sırala</value>
</data>
<data name="Label_TimePeriod" xml:space="preserve">
<value>Süre</value>
</data>
<data name="Label_ModelType" xml:space="preserve">
<value>Model Türü</value>
</data>
<data name="Label_BaseModel" xml:space="preserve">
<value>Temel Model</value>
</data>
<data name="Label_ShowNsfwContent" xml:space="preserve">
<value>NSFW İçerik Göster</value>
</data>
<data name="Label_DataProvidedByCivitAi" xml:space="preserve">
<value>CivitAI tarafından sağlanan veriler</value>
</data>
<data name="Label_Page" xml:space="preserve">
<value>Sayfa</value>
</data>
<data name="Label_FirstPage" xml:space="preserve">
<value>İlk Sayfa</value>
</data>
<data name="Label_PreviousPage" xml:space="preserve">
<value>Önceki Sayfa</value>
</data>
<data name="Label_NextPage" xml:space="preserve">
<value>Sonraki Sayfa</value>
</data>
<data name="Label_LastPage" xml:space="preserve">
<value>Son Sayfa</value>
</data>
<data name="Action_Rename" xml:space="preserve">
<value>Yeniden Adlandır</value>
</data>
<data name="Action_Delete" xml:space="preserve">
<value>Sil</value>
</data>
<data name="Action_OpenOnCivitAi" xml:space="preserve">
<value>CivitAI&apos;de Aç</value>
</data>
<data name="Label_ConnectedModel" xml:space="preserve">
<value>Bağlı Model</value>
</data>
<data name="Label_LocalModel" xml:space="preserve">
<value>Yerel Model</value>
</data>
<data name="Action_ShowInExplorer" xml:space="preserve">
<value>Explorer&apos;da göster</value>
</data>
<data name="Action_New" xml:space="preserve">
<value>Yeni</value>
</data>
<data name="Label_Folder" xml:space="preserve">
<value>Klasör</value>
</data>
<data name="Label_DropFileToImport" xml:space="preserve">
<value>İçe aktarma için dosyayı buraya bırakın</value>
</data>
<data name="Label_ImportAsConnected" xml:space="preserve">
<value>Metadata ile içeri aktar</value>
</data>
<data name="Label_ImportAsConnectedExplanation" xml:space="preserve">
<value>Yeni yerel içe aktarmalar için bağlı meta veri arayın</value>
</data>
<data name="Label_Indexing" xml:space="preserve">
<value>İndeksleniyor...</value>
</data>
<data name="Label_ModelsFolder" xml:space="preserve">
<value>Model Klasörü</value>
</data>
<data name="Label_Categories" xml:space="preserve">
<value>Kategoriler</value>
</data>
<data name="Label_LetsGetStarted" xml:space="preserve">
<value>Başlayalım</value>
</data>
<data name="Label_ReadAndAgree" xml:space="preserve">
<value>Lisans Sözleşmesini Okudum ve Kabul Ediyorum.</value>
</data>
<data name="Label_LicenseAgreement" xml:space="preserve">
<value>Lisans Sözleşmesi.</value>
</data>
<data name="Label_FindConnectedMetadata" xml:space="preserve">
<value>Metadata&apos;larını bul</value>
</data>
<data name="Label_ShowModelImages" xml:space="preserve">
<value>Model Resimlerini Göster</value>
</data>
<data name="Label_Appearance" xml:space="preserve">
<value>Görünüm</value>
</data>
<data name="Label_Theme" xml:space="preserve">
<value>Tema</value>
</data>
<data name="Label_CheckpointManager" xml:space="preserve">
<value>Checkpoint Yöneticisi</value>
</data>
<data name="Label_RemoveSymlinksOnShutdown" xml:space="preserve">
<value>Kapatma sırasında paylaşılan Checkpoint dizini sembolik bağlantılarını kaldırın</value>
</data>
<data name="Label_RemoveSymlinksOnShutdown_Details" xml:space="preserve">
<value>Stabilite Matrisini başka bir sürücüye taşımada sorun yaşıyorsanız bu seçeneği seçin</value>
</data>
<data name="Label_ResetCheckpointsCache" xml:space="preserve">
<value>Checkpoints Önbelleğini Sıfırla</value>
</data>
<data name="Label_ResetCheckpointsCache_Details" xml:space="preserve">
<value>Kurulu checkpoints önbelleğini yeniden oluştur. Model tarayıcısında checkpointler yanlış etiketlenmişse kullanın</value>
</data>
<data name="Label_PackageEnvironment" xml:space="preserve">
<value>Paket Ortamı</value>
</data>
<data name="Action_Edit" xml:space="preserve">
<value>Düzenle</value>
</data>
<data name="Label_EnvironmentVariables" xml:space="preserve">
<value>Ortam Değişkenleri</value>
</data>
<data name="Label_EmbeddedPython" xml:space="preserve">
<value>Gömülü Python</value>
</data>
<data name="Action_CheckVersion" xml:space="preserve">
<value>Sürümü Kontrol Et</value>
</data>
<data name="Label_Integrations" xml:space="preserve">
<value>Entegrasyonlar</value>
</data>
<data name="Label_DiscordRichPresence" xml:space="preserve">
<value>Discord Zengin Varlık</value>
</data>
<data name="Label_System" xml:space="preserve">
<value>Sistem</value>
</data>
<data name="Label_AddToStartMenu" xml:space="preserve">
<value>Stability Matrix&apos;i başlat menüsüne ekle</value>
</data>
<data name="Label_AddToStartMenu_Details" xml:space="preserve">
<value>Mevcut uygulama konumu kullanılır, uygulamayı taşırsanız tekrar çalıştırabilirsiniz</value>
</data>
<data name="Label_OnlyAvailableOnWindows" xml:space="preserve">
<value>Yalnızca Windows&apos;ta kullanılabilir</value>
</data>
<data name="Action_AddForCurrentUser" xml:space="preserve">
<value>Geçerli Kullanıcı için Ekle</value>
</data>
<data name="Action_AddForAllUsers" xml:space="preserve">
<value>Tüm Kullanıcılar İçin Ekle</value>
</data>
<data name="Label_SelectNewDataDirectory" xml:space="preserve">
<value>Yeni Veri Dizini Seç</value>
</data>
<data name="Label_SelectNewDataDirectory_Details" xml:space="preserve">
<value>Mevcut veriyi taşımaz</value>
</data>
<data name="Action_SelectDirectory" xml:space="preserve">
<value>Dizin Seçin</value>
</data>
<data name="Label_About" xml:space="preserve">
<value>Hakkında</value>
</data>
<data name="Label_StabilityMatrix" xml:space="preserve">
<value>Stability Matrix</value>
</data>
<data name="Label_LicenseAndOpenSourceNotices" xml:space="preserve">
<value>Lisans ve Açık Kaynak Bildirimleri</value>
</data>
<data name="TeachingTip_ClickLaunchToGetStarted" xml:space="preserve">
<value>Başlamak için Başlat&apos;a tıklayın!</value>
</data>
<data name="Action_Stop" xml:space="preserve">
<value>Dur</value>
</data>
<data name="Action_SendInput" xml:space="preserve">
<value>Giriş Gönder</value>
</data>
<data name="Label_Input" xml:space="preserve">
<value>Giriş</value>
</data>
<data name="Action_Send" xml:space="preserve">
<value>Gönder</value>
</data>
<data name="Label_InputRequired" xml:space="preserve">
<value>Giriş gerekiyor</value>
</data>
<data name="Label_ConfirmQuestion" xml:space="preserve">
<value>Onaylamak?</value>
</data>
<data name="Action_Yes" xml:space="preserve">
<value>Evet</value>
</data>
<data name="Label_No" xml:space="preserve">
<value>Hayır</value>
</data>
<data name="Action_OpenWebUI" xml:space="preserve">
<value>Web Arayüzünü Aç</value>
</data>
<data name="Text_WelcomeToStabilityMatrix" xml:space="preserve">
<value>Stability Matrix&apos;e Hoş Geldiniz!</value>
</data>
<data name="Text_OneClickInstaller_SubHeader" xml:space="preserve">
<value>Tercih ettiğiniz arayüzü seçin ve başlamak için Yükle&apos;ye tıklayın</value>
</data>
<data name="Label_Installing" xml:space="preserve">
<value>Yükleniyor</value>
</data>
<data name="Text_ProceedingToLaunchPage" xml:space="preserve">
<value>Başlatma sayfasına geçiliyor</value>
</data>
<data name="Progress_DownloadingPackage" xml:space="preserve">
<value>Paket indiriliyor...</value>
</data>
<data name="Progress_DownloadComplete" xml:space="preserve">
<value>İndirme tamamlandı</value>
</data>
<data name="Progress_InstallationComplete" xml:space="preserve">
<value>Kurulum tamamlandı</value>
</data>
<data name="Progress_InstallingPrerequisites" xml:space="preserve">
<value>Önkoşullar kuruluyor...</value>
</data>
<data name="Progress_InstallingPackageRequirements" xml:space="preserve">
<value>Paket gereksinimleri kuruluyor...</value>
</data>
<data name="Action_OpenInExplorer" xml:space="preserve">
<value>Explorer&apos;da Aç</value>
</data>
<data name="Action_OpenInFinder" xml:space="preserve">
<value>Finder&apos;da Aç</value>
</data>
<data name="Action_Uninstall" xml:space="preserve">
<value>Kaldır</value>
</data>
<data name="Action_CheckForUpdates" xml:space="preserve">
<value>Güncellemeleri Kontrol Et</value>
</data>
<data name="Action_Update" xml:space="preserve">
<value>Güncelle</value>
</data>
<data name="Action_AddPackage" xml:space="preserve">
<value>Paket Ekle</value>
</data>
<data name="TeachingTip_AddPackageToGetStarted" xml:space="preserve">
<value>Başlamak için bir paket ekle!</value>
</data>
<data name="Label_EnvVarsTable_Name" xml:space="preserve">
<value>Ad</value>
</data>
<data name="Label_EnvVarsTable_Value" xml:space="preserve">
<value>Değer</value>
</data>
<data name="Action_Remove" xml:space="preserve">
<value>Kaldır</value>
</data>
<data name="Label_Details" xml:space="preserve">
<value>Ayrıntılar</value>
</data>
<data name="Label_Callstack" xml:space="preserve">
<value>Çağrı Yığını</value>
</data>
<data name="Label_InnerException" xml:space="preserve">
<value>İç istisna</value>
</data>
<data name="Label_SearchEllipsis" xml:space="preserve">
<value>Ara...</value>
</data>
<data name="Action_OK" xml:space="preserve">
<value>Tamam</value>
</data>
<data name="Action_Retry" xml:space="preserve">
<value>Tekrar Dene</value>
</data>
<data name="Label_PythonVersionInfo" xml:space="preserve">
<value>Python Sürüm Bilgisi</value>
</data>
<data name="Action_Restart" xml:space="preserve">
<value>Yeniden Başlat</value>
</data>
<data name="Label_ConfirmDelete" xml:space="preserve">
<value>Silmeyi Onayla</value>
</data>
<data name="Text_PackageUninstall_Details" xml:space="preserve">
<value>Bu, paket klasörünü ve içindeki tüm içeriği, eklediğiniz tüm oluşturulmuş resimleri ve dosyaları silecek.</value>
</data>
<data name="Progress_UninstallingPackage" xml:space="preserve">
<value>Paket Kaldırılıyor...</value>
</data>
<data name="Label_PackageUninstalled" xml:space="preserve">
<value>Paket Kaldırıldı</value>
</data>
<data name="Text_SomeFilesCouldNotBeDeleted" xml:space="preserve">
<value>Bazı dosyalar silinemedi. Lütfen paket klasöründeki açık dosyaları kapatın ve tekrar deneyin.</value>
</data>
<data name="Label_InvalidPackageType" xml:space="preserve">
<value>Geçersiz Paket Türü</value>
</data>
<data name="TextTemplate_UpdatingPackage" xml:space="preserve">
<value>{0} Güncelleniyor</value>
</data>
<data name="Progress_UpdateComplete" xml:space="preserve">
<value>Güncelleme tamamlandı</value>
</data>
<data name="TextTemplate_PackageUpdatedToLatest" xml:space="preserve">
<value>{0}, en son sürüme güncellendi</value>
</data>
<data name="TextTemplate_ErrorUpdatingPackage" xml:space="preserve">
<value>{0} güncelleme hatası</value>
</data>
<data name="Progress_UpdateFailed" xml:space="preserve">
<value>Güncelleme başarısız oldu</value>
</data>
<data name="Action_OpenInBrowser" xml:space="preserve">
<value>Tarayıcıda Aç</value>
</data>
<data name="Label_ErrorInstallingPackage" xml:space="preserve">
<value>Paket kurulum hatası</value>
</data>
<data name="Label_Branch" xml:space="preserve">
<value>Dal</value>
</data>
<data name="Label_AutoScrollToEnd" xml:space="preserve">
<value>Konsol çıktısının sonuna otomatik olarak kaydır</value>
</data>
<data name="Label_License" xml:space="preserve">
<value>Lisans</value>
</data>
<data name="Label_SharedModelStrategyShort" xml:space="preserve">
<value>Model Paylaşımı</value>
</data>
<data name="Label_PleaseSelectDataDirectory" xml:space="preserve">
<value>Lütfen Bir Veri Klasörü Seçin</value>
</data>
<data name="Label_DataFolderName" xml:space="preserve">
<value>Veri Klasörü Adı</value>
</data>
<data name="Label_CurrentDirectory" xml:space="preserve">
<value>Geçerli klasör:</value>
</data>
<data name="Text_AppWillRelaunchAfterUpdate" xml:space="preserve">
<value>Uygulama güncelleme sonrası yeniden başlatılacaktır</value>
</data>
<data name="Action_RemindMeLater" xml:space="preserve">
<value>Daha Sonra Hatırlat</value>
</data>
<data name="Action_InstallNow" xml:space="preserve">
<value>Şimdi Yükle</value>
</data>
<data name="Label_ReleaseNotes" xml:space="preserve">
<value>Sürüm Notları</value>
</data>
<data name="Action_OpenProjectEllipsis" xml:space="preserve">
<value>Projeyi Aç...</value>
</data>
<data name="Action_SaveAsEllipsis" xml:space="preserve">
<value>Farklı Kaydet...</value>
</data>
<data name="Action_RestoreDefaultLayout" xml:space="preserve">
<value>Varsayılan Düzeni Geri Yükle</value>
</data>
<data name="Label_UseSharedOutputFolder" xml:space="preserve">
<value>Çıktı Paylaşımı</value>
</data>
<data name="Label_BatchIndex" xml:space="preserve">
<value>Toplu İndeks</value>
</data>
<data name="Action_Copy" xml:space="preserve">
<value>Kopyala</value>
</data>
<data name="Action_OpenInViewer" xml:space="preserve">
<value>Resim Görüntüleyici&apos;de aç</value>
</data>
<data name="Label_NumImagesSelected" xml:space="preserve">
<value>{0} resim seçildi</value>
</data>
<data name="Label_OutputFolder" xml:space="preserve">
<value>Çıkış Klasörü</value>
</data>
<data name="Label_OutputType" xml:space="preserve">
<value>Çıkış Türü</value>
</data>
<data name="Action_ClearSelection" xml:space="preserve">
<value>Seçimi Temizle</value>
</data>
<data name="Action_SelectAll" xml:space="preserve">
<value>Tümünü Seç</value>
</data>
<data name="Action_SendToInference" xml:space="preserve">
<value>Çıkarıma Gönder</value>
</data>
<data name="Label_TextToImage" xml:space="preserve">
<value>Metinden Resime</value>
</data>
<data name="Label_ImageToImage" xml:space="preserve">
<value>Resimden Resime</value>
</data>
<data name="Label_Inpainting" xml:space="preserve">
<value>Inpainting</value>
</data>
<data name="Label_Upscale" xml:space="preserve">
<value>Upscale</value>
</data>
<data name="Label_OutputsPageTitle" xml:space="preserve">
<value>Çıkış Tarayıcısı</value>
</data>
<data name="Label_OneImageSelected" xml:space="preserve">
<value>1 resim seçildi</value>
</data>
<data name="Label_PythonPackages" xml:space="preserve">
<value>Python Paketleri</value>
</data>
<data name="Action_Consolidate" xml:space="preserve">
<value>Birleştir</value>
</data>
<data name="Label_AreYouSure" xml:space="preserve">
<value>Emin misiniz?</value>
</data>
<data name="Label_ConsolidateExplanation" xml:space="preserve">
<value>Bu, seçilen paketlerden tüm oluşturulmuş resimleri, paylaşılan çıktılar klasörünün Konsolide dizinine taşıyacak. Bu işlem geri alınamaz.</value>
</data>
<data name="Action_Refresh" xml:space="preserve">
<value>Yenile</value>
</data>
<data name="Action_Upgrade" xml:space="preserve">
<value>Yükselt</value>
</data>
<data name="Action_Downgrade" xml:space="preserve">
<value>Düşür</value>
</data>
<data name="Action_OpenGithub" xml:space="preserve">
<value>GitHub&apos;da Aç</value>
</data>
<data name="Label_Connected" xml:space="preserve">
<value>Bağlandı</value>
</data>
<data name="Action_Disconnect" xml:space="preserve">
<value>Bağlantıyı Kes</value>
</data>
<data name="Label_Email" xml:space="preserve">
<value>E-posta</value>
</data>
<data name="Label_Username" xml:space="preserve">
<value>Kullanıcı Adı</value>
</data>
<data name="Label_Password" xml:space="preserve">
<value>Parola</value>
</data>
<data name="Action_Login" xml:space="preserve">
<value>Giriş</value>
</data>
<data name="Action_Signup" xml:space="preserve">
<value>Kaydol</value>
</data>
<data name="Label_ConfirmPassword" xml:space="preserve">
<value>Parolayı Onayla</value>
</data>
<data name="Label_ApiKey" xml:space="preserve">
<value>API Key</value>
</data>
<data name="Label_Accounts" xml:space="preserve">
<value>Hesaplar</value>
</data>
</root>

8
StabilityMatrix.Avalonia/ViewModels/Inference/FreeUCardViewModel.cs

@ -14,21 +14,25 @@ public partial class FreeUCardViewModel : LoadableViewModelBase
public const string ModuleKey = "FreeU";
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[Range(0D, 10D)]
private double b1 = 1.1;
private double b1 = 1.5;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[Range(0D, 10D)]
private double b2 = 1.2;
private double b2 = 1.6;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[Range(0D, 10D)]
private double s1 = 0.9;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required]
[Range(0D, 10D)]
private double s2 = 0.2;

2
StabilityMatrix.Avalonia/ViewModels/Inference/InferenceImageToImageViewModel.cs

@ -32,6 +32,8 @@ public class InferenceImageToImageViewModel : InferenceTextToImageViewModel
: base(notificationService, inferenceClientManager, settingsManager, vmFactory, modelIndexService)
{
SelectImageCardViewModel = vmFactory.Get<SelectImageCardViewModel>();
SamplerCardViewModel.IsDenoiseStrengthEnabled = true;
}
/// <inheritdoc />

2
StabilityMatrix.Avalonia/ViewModels/Inference/SamplerCardViewModel.cs

@ -42,7 +42,7 @@ public partial class SamplerCardViewModel : LoadableViewModelBase, IParametersLo
private bool isDenoiseStrengthEnabled;
[ObservableProperty]
private double denoiseStrength = 1;
private double denoiseStrength = 0.7f;
[ObservableProperty]
[property: Category("Settings")]

84
StabilityMatrix.Avalonia/Views/MainWindow.axaml.cs

@ -89,9 +89,7 @@ public partial class MainWindow : AppWindowBase
{
base.OnApplyTemplate(e);
navigationService.SetFrame(
FrameView ?? throw new NullReferenceException("Frame not found")
);
navigationService.SetFrame(FrameView ?? throw new NullReferenceException("Frame not found"));
}
protected override void OnOpened(EventArgs e)
@ -134,16 +132,15 @@ public partial class MainWindow : AppWindowBase
return;
// Navigate to first page
Dispatcher.UIThread.Post(
() =>
navigationService.NavigateTo(
vm.Pages[0],
new BetterSlideNavigationTransition
{
Effect = SlideNavigationTransitionEffect.FromBottom
}
)
);
Dispatcher
.UIThread
.Post(
() =>
navigationService.NavigateTo(
vm.Pages[0],
new BetterSlideNavigationTransition { Effect = SlideNavigationTransitionEffect.FromBottom }
)
);
// Check show update teaching tip
if (vm.UpdateViewModel.IsUpdateAvailable)
@ -167,27 +164,28 @@ public partial class MainWindow : AppWindowBase
{
var mainViewModel = (MainWindowViewModel)DataContext!;
mainViewModel.SelectedCategory = mainViewModel.Pages
mainViewModel.SelectedCategory = mainViewModel
.Pages
.Concat(mainViewModel.FooterPages)
.FirstOrDefault(x => x.GetType() == e.ViewModelType);
}
private void OnUpdateAvailable(object? sender, UpdateInfo? updateInfo)
{
Dispatcher.UIThread.Post(() =>
{
var vm = DataContext as MainWindowViewModel;
if (vm!.ShouldShowUpdateAvailableTeachingTip(updateInfo))
Dispatcher
.UIThread
.Post(() =>
{
var target = this.FindControl<NavigationViewItem>("FooterUpdateItem")!;
var tip = this.FindControl<TeachingTip>("UpdateAvailableTeachingTip")!;
if (DataContext is MainWindowViewModel vm && vm.ShouldShowUpdateAvailableTeachingTip(updateInfo))
{
var target = this.FindControl<NavigationViewItem>("FooterUpdateItem")!;
var tip = this.FindControl<TeachingTip>("UpdateAvailableTeachingTip")!;
tip.Target = target;
tip.Subtitle = $"{Compat.AppVersion.ToDisplayString()} -> {updateInfo.Version}";
tip.IsOpen = true;
}
});
tip.Target = target;
tip.Subtitle = $"{Compat.AppVersion.ToDisplayString()} -> {updateInfo.Version}";
tip.IsOpen = true;
}
});
}
public void SetDefaultFonts()
@ -284,16 +282,18 @@ public partial class MainWindow : AppWindowBase
private void OnImageLoadFailed(object? sender, ImageLoadFailedEventArgs e)
{
Dispatcher.UIThread.Post(() =>
{
var fileName = Path.GetFileName(e.Url);
var displayName = string.IsNullOrEmpty(fileName) ? e.Url : fileName;
notificationService.ShowPersistent(
"Failed to load image",
$"Could not load '{displayName}'\n({e.Exception.Message})",
NotificationType.Warning
);
});
Dispatcher
.UIThread
.Post(() =>
{
var fileName = Path.GetFileName(e.Url);
var displayName = string.IsNullOrEmpty(fileName) ? e.Url : fileName;
notificationService.ShowPersistent(
"Failed to load image",
$"Could not load '{displayName}'\n({e.Exception.Message})",
NotificationType.Warning
);
});
}
private void TryEnableMicaEffect()
@ -308,11 +308,7 @@ public partial class MainWindow : AppWindowBase
if (ActualThemeVariant == ThemeVariant.Dark)
{
var color = this.TryFindResource(
"SolidBackgroundFillColorBase",
ThemeVariant.Dark,
out var value
)
var color = this.TryFindResource("SolidBackgroundFillColorBase", ThemeVariant.Dark, out var value)
? (Color2)(Color)value!
: new Color2(30, 31, 34);
@ -323,11 +319,7 @@ public partial class MainWindow : AppWindowBase
else if (ActualThemeVariant == ThemeVariant.Light)
{
// Similar effect here
var color = this.TryFindResource(
"SolidBackgroundFillColorBase",
ThemeVariant.Light,
out var value
)
var color = this.TryFindResource("SolidBackgroundFillColorBase", ThemeVariant.Light, out var value)
? (Color2)(Color)value!
: new Color2(243, 243, 243);

36
StabilityMatrix.Core/Models/FileInterfaces/FileSystemPath.cs

@ -1,6 +1,6 @@
namespace StabilityMatrix.Core.Models.FileInterfaces;
public class FileSystemPath : IEquatable<FileSystemPath>, IEquatable<string>
public class FileSystemPath : IEquatable<FileSystemPath>, IEquatable<string>, IFormattable
{
public string FullPath { get; }
@ -8,27 +8,42 @@ public class FileSystemPath : IEquatable<FileSystemPath>, IEquatable<string>
{
FullPath = path;
}
protected FileSystemPath(FileSystemPath path) : this(path.FullPath)
protected FileSystemPath(FileSystemPath path)
: this(path.FullPath) { }
protected FileSystemPath(params string[] paths)
: this(Path.Combine(paths)) { }
public override string ToString()
{
return FullPath;
}
protected FileSystemPath(params string[] paths) : this(Path.Combine(paths))
/// <inheritdoc />
string IFormattable.ToString(string? format, IFormatProvider? formatProvider)
{
return ToString(format, formatProvider);
}
public override string ToString()
/// <summary>
/// Overridable IFormattable.ToString method.
/// By default, returns <see cref="FullPath"/>.
/// </summary>
protected virtual string ToString(string? format, IFormatProvider? formatProvider)
{
return FullPath;
}
public bool Equals(FileSystemPath? other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return FullPath == other.FullPath;
}
public bool Equals(string? other)
{
return string.Equals(FullPath, other);
@ -48,8 +63,9 @@ public class FileSystemPath : IEquatable<FileSystemPath>, IEquatable<string>
{
return FullPath.GetHashCode();
}
// Implicit conversions to and from string
public static implicit operator string(FileSystemPath path) => path.FullPath;
public static implicit operator FileSystemPath(string path) => new(path);
}

92
StabilityMatrix.Core/Models/Packages/A3WebUI.cs

@ -56,12 +56,14 @@ public class A3WebUI(
[SharedFolderType.Karlo] = new[] { "models/karlo" },
[SharedFolderType.TextualInversion] = new[] { "embeddings" },
[SharedFolderType.Hypernetwork] = new[] { "models/hypernetworks" },
[SharedFolderType.ControlNet] = new[] { "models/ControlNet" },
[SharedFolderType.ControlNet] = new[] { "models/controlnet/ControlNet" },
[SharedFolderType.Codeformer] = new[] { "models/Codeformer" },
[SharedFolderType.LDSR] = new[] { "models/LDSR" },
[SharedFolderType.AfterDetailer] = new[] { "models/adetailer" },
[SharedFolderType.T2IAdapter] = new[] { "models/controlnet" },
[SharedFolderType.IpAdapter] = new[] { "models/ipadapter" }
[SharedFolderType.T2IAdapter] = new[] { "models/controlnet/T2IAdapter" },
[SharedFolderType.IpAdapter] = new[] { "models/controlnet/IpAdapter" },
[SharedFolderType.InvokeIpAdapters15] = new[] { "models/controlnet/DiffusersIpAdapters" },
[SharedFolderType.InvokeIpAdaptersXl] = new[] { "models/controlnet/DiffusersIpAdaptersXL" }
};
public override Dictionary<SharedOutputType, IReadOnlyList<string>>? SharedOutputFolders =>
@ -183,39 +185,44 @@ public class A3WebUI(
)
{
progress?.Report(new ProgressReport(-1f, "Setting up venv", isIndeterminate: true));
// Setup venv
await using var venvRunner = new PyVenvRunner(Path.Combine(installLocation, "venv"));
venvRunner.WorkingDirectory = installLocation;
await venvRunner.Setup(true, onConsoleOutput).ConfigureAwait(false);
switch (torchVersion)
var venvRunner = await SetupVenv(installLocation, forceRecreate: true).ConfigureAwait(false);
await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing requirements...", isIndeterminate: true));
var requirements = new FilePath(installLocation, "requirements_versions.txt");
var pipArgs = new PipInstallArgs()
.WithTorch("==2.0.1")
.WithTorchVision("==0.15.2")
.WithTorchExtraIndex(
torchVersion switch
{
TorchVersion.Cpu => "cpu",
TorchVersion.Cuda => "cu118",
TorchVersion.Rocm => "rocm5.1.1",
_ => throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null)
}
)
.WithParsedFromRequirementsTxt(
await requirements.ReadAllTextAsync().ConfigureAwait(false),
excludePattern: "torch"
);
if (torchVersion == TorchVersion.Cuda)
{
case TorchVersion.Cpu:
await InstallCpuTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.Cuda:
await InstallCudaTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.Rocm:
await InstallRocmTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
default:
throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null);
pipArgs = pipArgs.WithXFormers("==0.0.20");
}
// v1.6.0 needs a httpx qualifier to fix a gradio issue
if (versionOptions.VersionTag?.Contains("1.6.0") ?? false)
{
await venvRunner.PipInstall("httpx==0.24.1", onConsoleOutput);
pipArgs = pipArgs.AddArg("httpx==0.24.1");
}
// Install requirements file
progress?.Report(new ProgressReport(-1f, "Installing Package Requirements", isIndeterminate: true));
Logger.Info("Installing requirements_versions.txt");
var requirements = new FilePath(installLocation, "requirements_versions.txt");
await venvRunner
.PipInstallFromRequirements(requirements, onConsoleOutput, excludes: "torch")
.ConfigureAwait(false);
await venvRunner.PipInstall(pipArgs, onConsoleOutput).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Updating configuration", isIndeterminate: true));
@ -261,21 +268,22 @@ public class A3WebUI(
VenvRunner.RunDetached(args.TrimEnd(), HandleConsoleOutput, OnExit);
}
private async Task InstallRocmTorch(
PyVenvRunner venvRunner,
IProgress<ProgressReport>? progress = null,
Action<ProcessOutput>? onConsoleOutput = null
)
/// <inheritdoc />
public override async Task SetupModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod)
{
progress?.Report(new ProgressReport(-1f, "Installing PyTorch for ROCm", isIndeterminate: true));
await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false);
// Migration for `controlnet` -> `controlnet/ControlNet` and `controlnet/T2IAdapter`
// If the original link exists, delete it first
if (installDirectory.JoinDir("models/controlnet") is { IsSymbolicLink: true } controlnetOldLink)
{
Logger.Info("Migration: Removing old controlnet link {Path}", controlnetOldLink);
await controlnetOldLink.DeleteAsync(false).ConfigureAwait(false);
}
await venvRunner
.PipInstall(
new PipInstallArgs().WithTorch("==2.0.1").WithTorchVision().WithTorchExtraIndex("rocm5.1.1"),
onConsoleOutput
)
.ConfigureAwait(false);
// Resume base setup
await base.SetupModelFolders(installDirectory, sharedFolderMethod).ConfigureAwait(false);
}
/// <inheritdoc />
public override Task UpdateModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod) =>
SetupModelFolders(installDirectory, sharedFolderMethod);
}

236
StabilityMatrix.Core/Models/Packages/ComfyUI.cs

@ -10,6 +10,7 @@ using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
using YamlDotNet.Core;
using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
@ -53,12 +54,12 @@ public class ComfyUI(
[SharedFolderType.TextualInversion] = new[] { "models/embeddings" },
[SharedFolderType.VAE] = new[] { "models/vae" },
[SharedFolderType.ApproxVAE] = new[] { "models/vae_approx" },
[SharedFolderType.ControlNet] = new[] { "models/controlnet" },
[SharedFolderType.ControlNet] = new[] { "models/controlnet/ControlNet" },
[SharedFolderType.GLIGEN] = new[] { "models/gligen" },
[SharedFolderType.ESRGAN] = new[] { "models/upscale_models" },
[SharedFolderType.Hypernetwork] = new[] { "models/hypernetworks" },
[SharedFolderType.IpAdapter] = new[] { "models/ipadapter" },
[SharedFolderType.T2IAdapter] = new[] { "models/controlnet" },
[SharedFolderType.T2IAdapter] = new[] { "models/controlnet/T2IAdapter" },
};
public override Dictionary<SharedOutputType, IReadOnlyList<string>>? SharedOutputFolders =>
@ -155,80 +156,48 @@ public class ComfyUI(
venvRunner.WorkingDirectory = installLocation;
await venvRunner.Setup(true, onConsoleOutput).ConfigureAwait(false);
// Install torch / xformers based on gpu info
switch (torchVersion)
await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing Package Requirements...", isIndeterminate: true));
var pipArgs = new PipInstallArgs();
pipArgs = torchVersion switch
{
case TorchVersion.Cpu:
await InstallCpuTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.Cuda:
await venvRunner
.PipInstall(
new PipInstallArgs()
.WithTorch("~=2.1.0")
.WithTorchVision()
.WithXFormers("==0.0.22.post4")
.AddArg("--upgrade")
.WithTorchExtraIndex("cu121"),
onConsoleOutput
)
.ConfigureAwait(false);
break;
case TorchVersion.DirectMl:
await venvRunner
.PipInstall(new PipInstallArgs().WithTorchDirectML(), onConsoleOutput)
.ConfigureAwait(false);
break;
case TorchVersion.Rocm:
await InstallRocmTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
break;
case TorchVersion.Mps:
await venvRunner
.PipInstall(
new PipInstallArgs()
.AddArg("--pre")
.WithTorch()
.WithTorchVision()
.WithTorchExtraIndex("nightly/cpu"),
onConsoleOutput
TorchVersion.DirectMl => pipArgs.WithTorchDirectML(),
TorchVersion.Mps
=> pipArgs.AddArg("--pre").WithTorch().WithTorchVision().WithTorchExtraIndex("nightly/cpu"),
_
=> pipArgs
.AddArg("--upgrade")
.WithTorch("~=2.1.0")
.WithTorchVision()
.WithTorchExtraIndex(
torchVersion switch
{
TorchVersion.Cpu => "cpu",
TorchVersion.Cuda => "cu121",
TorchVersion.Rocm => "rocm5.6",
_ => throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null)
}
)
.ConfigureAwait(false);
break;
default:
throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null);
}
};
// Install requirements file (skip torch)
progress?.Report(new ProgressReport(-1, "Installing Package Requirements", isIndeterminate: true));
if (torchVersion == TorchVersion.Cuda)
{
pipArgs = pipArgs.WithXFormers("==0.0.22.post4");
}
var requirementsFile = new FilePath(installLocation, "requirements.txt");
var requirements = new FilePath(installLocation, "requirements.txt");
await venvRunner
.PipInstallFromRequirements(requirementsFile, onConsoleOutput, excludes: "torch")
.ConfigureAwait(false);
pipArgs = pipArgs.WithParsedFromRequirementsTxt(
await requirements.ReadAllTextAsync().ConfigureAwait(false),
excludePattern: "torch"
);
progress?.Report(new ProgressReport(1, "Installing Package Requirements", isIndeterminate: false));
}
await venvRunner.PipInstall(pipArgs, onConsoleOutput).ConfigureAwait(false);
private async Task AutoDetectAndInstallTorch(PyVenvRunner venvRunner, IProgress<ProgressReport>? progress = null)
{
var gpus = HardwareHelper.IterGpuInfo().ToList();
if (gpus.Any(g => g.IsNvidia))
{
await InstallCudaTorch(venvRunner, progress).ConfigureAwait(false);
}
else if (HardwareHelper.PreferRocm())
{
await InstallRocmTorch(venvRunner, progress).ConfigureAwait(false);
}
else if (HardwareHelper.PreferDirectML())
{
await InstallDirectMlTorch(venvRunner, progress).ConfigureAwait(false);
}
else
{
await InstallCpuTorch(venvRunner, progress).ConfigureAwait(false);
}
progress?.Report(new ProgressReport(1, "Installed Package Requirements", isIndeterminate: false));
}
public override async Task RunPackage(
@ -267,26 +236,55 @@ public class ComfyUI(
}
}
public override Task SetupModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod)
public override Task SetupModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod) =>
sharedFolderMethod switch
{
SharedFolderMethod.Symlink => SetupModelFoldersSymlink(installDirectory),
SharedFolderMethod.Configuration => SetupModelFoldersConfig(installDirectory),
SharedFolderMethod.None => Task.CompletedTask,
_ => throw new ArgumentOutOfRangeException(nameof(sharedFolderMethod), sharedFolderMethod, null)
};
public override Task UpdateModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod) =>
SetupModelFolders(installDirectory, sharedFolderMethod);
public override Task RemoveModelFolderLinks(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod)
{
return sharedFolderMethod switch
{
SharedFolderMethod.Symlink => base.RemoveModelFolderLinks(installDirectory, sharedFolderMethod),
SharedFolderMethod.Configuration => RemoveConfigSection(installDirectory),
SharedFolderMethod.None => Task.CompletedTask,
_ => throw new ArgumentOutOfRangeException(nameof(sharedFolderMethod), sharedFolderMethod, null)
};
}
private async Task SetupModelFoldersSymlink(DirectoryPath installDirectory)
{
switch (sharedFolderMethod)
// Migration for `controlnet` -> `controlnet/ControlNet` and `controlnet/T2IAdapter`
// If the original link exists, delete it first
if (installDirectory.JoinDir("models/controlnet") is { IsSymbolicLink: true } controlnetOldLink)
{
case SharedFolderMethod.None:
return Task.CompletedTask;
case SharedFolderMethod.Symlink:
return base.SetupModelFolders(installDirectory, sharedFolderMethod);
Logger.Info("Migration: Removing old controlnet link {Path}", controlnetOldLink);
await controlnetOldLink.DeleteAsync(false).ConfigureAwait(false);
}
var extraPathsYamlPath = installDirectory + "extra_model_paths.yaml";
// Resume base setup
await base.SetupModelFolders(installDirectory, SharedFolderMethod.Symlink).ConfigureAwait(false);
}
private async Task SetupModelFoldersConfig(DirectoryPath installDirectory)
{
var extraPathsYamlPath = installDirectory.JoinFile("extra_model_paths.yaml");
var modelsDir = SettingsManager.ModelsDirectory;
var exists = File.Exists(extraPathsYamlPath);
if (!exists)
if (!extraPathsYamlPath.Exists)
{
Logger.Info("Creating extra_model_paths.yaml");
File.WriteAllText(extraPathsYamlPath, string.Empty);
extraPathsYamlPath.Create();
}
var yaml = File.ReadAllText(extraPathsYamlPath);
var yaml = await extraPathsYamlPath.ReadAllTextAsync().ConfigureAwait(false);
using var sr = new StringReader(yaml);
var yamlStream = new YamlStream();
yamlStream.Load(sr);
@ -307,7 +305,7 @@ public class ComfyUI(
if (stabilityMatrixNode.Key != null)
{
if (stabilityMatrixNode.Value is not YamlMappingNode nodeValue)
return Task.CompletedTask;
return;
nodeValue.Children["checkpoints"] = Path.Combine(modelsDir, "StableDiffusion");
nodeValue.Children["vae"] = Path.Combine(modelsDir, "VAE");
@ -319,7 +317,11 @@ public class ComfyUI(
+ $"{Path.Combine(modelsDir, "SwinIR")}";
nodeValue.Children["embeddings"] = Path.Combine(modelsDir, "TextualInversion");
nodeValue.Children["hypernetworks"] = Path.Combine(modelsDir, "Hypernetwork");
nodeValue.Children["controlnet"] = Path.Combine(modelsDir, "ControlNet");
nodeValue.Children["controlnet"] = string.Join(
'\n',
Path.Combine(modelsDir, "ControlNet"),
Path.Combine(modelsDir, "T2IAdapter")
);
nodeValue.Children["clip"] = Path.Combine(modelsDir, "CLIP");
nodeValue.Children["diffusers"] = Path.Combine(modelsDir, "Diffusers");
nodeValue.Children["gligen"] = Path.Combine(modelsDir, "GLIGEN");
@ -340,7 +342,10 @@ public class ComfyUI(
},
{ "embeddings", Path.Combine(modelsDir, "TextualInversion") },
{ "hypernetworks", Path.Combine(modelsDir, "Hypernetwork") },
{ "controlnet", Path.Combine(modelsDir, "ControlNet") },
{
"controlnet",
string.Join('\n', Path.Combine(modelsDir, "ControlNet"), Path.Combine(modelsDir, "T2IAdapter"))
},
{ "clip", Path.Combine(modelsDir, "CLIP") },
{ "diffusers", Path.Combine(modelsDir, "Diffusers") },
{ "gligen", Path.Combine(modelsDir, "GLIGEN") },
@ -357,82 +362,45 @@ public class ComfyUI(
newRootNode.Children.Add(stabilityMatrixNode);
var serializer = new SerializerBuilder().WithNamingConvention(UnderscoredNamingConvention.Instance).Build();
var yamlData = serializer.Serialize(newRootNode);
File.WriteAllText(extraPathsYamlPath, yamlData);
var serializer = new SerializerBuilder()
.WithNamingConvention(UnderscoredNamingConvention.Instance)
.WithDefaultScalarStyle(ScalarStyle.Literal)
.Build();
return Task.CompletedTask;
var yamlData = serializer.Serialize(newRootNode);
await extraPathsYamlPath.WriteAllTextAsync(yamlData).ConfigureAwait(false);
}
public override Task UpdateModelFolders(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod) =>
sharedFolderMethod switch
{
SharedFolderMethod.Symlink => base.UpdateModelFolders(installDirectory, sharedFolderMethod),
SharedFolderMethod.Configuration => SetupModelFolders(installDirectory, sharedFolderMethod),
SharedFolderMethod.None => Task.CompletedTask,
_ => Task.CompletedTask
};
public override Task RemoveModelFolderLinks(DirectoryPath installDirectory, SharedFolderMethod sharedFolderMethod)
private static async Task RemoveConfigSection(DirectoryPath installDirectory)
{
return sharedFolderMethod switch
{
SharedFolderMethod.Configuration => RemoveConfigSection(installDirectory),
SharedFolderMethod.None => Task.CompletedTask,
SharedFolderMethod.Symlink => base.RemoveModelFolderLinks(installDirectory, sharedFolderMethod),
_ => Task.CompletedTask
};
}
var extraPathsYamlPath = installDirectory.JoinFile("extra_model_paths.yaml");
private Task RemoveConfigSection(string installDirectory)
{
var extraPathsYamlPath = Path.Combine(installDirectory, "extra_model_paths.yaml");
var exists = File.Exists(extraPathsYamlPath);
if (!exists)
if (!extraPathsYamlPath.Exists)
{
return Task.CompletedTask;
return;
}
var yaml = File.ReadAllText(extraPathsYamlPath);
var yaml = await extraPathsYamlPath.ReadAllTextAsync().ConfigureAwait(false);
using var sr = new StringReader(yaml);
var yamlStream = new YamlStream();
yamlStream.Load(sr);
if (!yamlStream.Documents.Any())
{
return Task.CompletedTask;
return;
}
var root = yamlStream.Documents[0].RootNode;
if (root is not YamlMappingNode mappingNode)
{
return Task.CompletedTask;
return;
}
mappingNode.Children.Remove("stability_matrix");
var serializer = new SerializerBuilder().WithNamingConvention(UnderscoredNamingConvention.Instance).Build();
var yamlData = serializer.Serialize(mappingNode);
File.WriteAllText(extraPathsYamlPath, yamlData);
return Task.CompletedTask;
}
private async Task InstallRocmTorch(
PyVenvRunner venvRunner,
IProgress<ProgressReport>? progress = null,
Action<ProcessOutput>? onConsoleOutput = null
)
{
progress?.Report(new ProgressReport(-1f, "Installing PyTorch for ROCm", isIndeterminate: true));
await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false);
await venvRunner
.PipInstall(
new PipInstallArgs().WithTorch("==2.0.1").WithTorchVision().WithTorchExtraIndex("rocm5.6"),
onConsoleOutput
)
.ConfigureAwait(false);
await extraPathsYamlPath.WriteAllTextAsync(yamlData).ConfigureAwait(false);
}
}

56
StabilityMatrix.Core/Models/Packages/Fooocus.cs

@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Text.RegularExpressions;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
@ -76,11 +77,11 @@ public class Fooocus(
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.IterGpuInfo().Select(gpu => gpu.MemoryLevel).Max() switch
{
MemoryLevel.Low => "--lowvram",
MemoryLevel.Medium => "--normalvram",
MemoryLevel.Low => "--always-low-vram",
MemoryLevel.Medium => "--always-normal-vram",
_ => null
},
Options = { "--highvram", "--normalvram", "--lowvram", "--novram" }
Options = { "--always-high-vram", "--always-normal-vram", "--always-low-vram", "--always-no-vram" }
},
new LaunchOptionDefinition
{
@ -152,39 +153,38 @@ public class Fooocus(
{
var venvRunner = await SetupVenv(installLocation, forceRecreate: true).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing torch...", isIndeterminate: true));
progress?.Report(new ProgressReport(-1f, "Installing requirements...", isIndeterminate: true));
var pipArgs = new PipInstallArgs();
if (torchVersion == TorchVersion.DirectMl)
{
await venvRunner
.PipInstall(new PipInstallArgs().WithTorchDirectML(), onConsoleOutput)
.ConfigureAwait(false);
pipArgs = pipArgs.WithTorchDirectML();
}
else
{
var extraIndex = torchVersion switch
{
TorchVersion.Cpu => "cpu",
TorchVersion.Cuda => "cu121",
TorchVersion.Rocm => "rocm5.6",
_ => throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null)
};
await venvRunner
.PipInstall(
new PipInstallArgs()
.WithTorch("==2.1.0")
.WithTorchVision("==0.16.0")
.WithTorchExtraIndex(extraIndex),
onConsoleOutput
)
.ConfigureAwait(false);
pipArgs = pipArgs
.WithTorch("==2.1.0")
.WithTorchVision("==0.16.0")
.WithTorchExtraIndex(
torchVersion switch
{
TorchVersion.Cpu => "cpu",
TorchVersion.Cuda => "cu121",
TorchVersion.Rocm => "rocm5.6",
_ => throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null)
}
);
}
var requirements = new FilePath(installLocation, "requirements_versions.txt");
await venvRunner
.PipInstallFromRequirements(requirements, onConsoleOutput, excludes: "torch")
.ConfigureAwait(false);
pipArgs = pipArgs.WithParsedFromRequirementsTxt(
await requirements.ReadAllTextAsync().ConfigureAwait(false),
excludePattern: "torch"
);
await venvRunner.PipInstall(pipArgs, onConsoleOutput).ConfigureAwait(false);
}
public override async Task RunPackage(

97
StabilityMatrix.Core/Models/Packages/RuinedFooocus.cs

@ -1,9 +1,11 @@
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Cache;
using StabilityMatrix.Core.Helper.HardwareInfo;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
@ -26,6 +28,79 @@ public class RuinedFooocus(
new("https://raw.githubusercontent.com/runew0lf/pmmconfigs/main/RuinedFooocus_ss.png");
public override PackageDifficulty InstallerSortOrder => PackageDifficulty.Expert;
public override List<LaunchOptionDefinition> LaunchOptions =>
new()
{
new LaunchOptionDefinition
{
Name = "Preset",
Type = LaunchOptionType.Bool,
Options = { "--preset anime", "--preset realistic" }
},
new LaunchOptionDefinition
{
Name = "Port",
Type = LaunchOptionType.String,
Description = "Sets the listen port",
Options = { "--port" }
},
new LaunchOptionDefinition
{
Name = "Share",
Type = LaunchOptionType.Bool,
Description = "Set whether to share on Gradio",
Options = { "--share" }
},
new LaunchOptionDefinition
{
Name = "Listen",
Type = LaunchOptionType.String,
Description = "Set the listen interface",
Options = { "--listen" }
},
new LaunchOptionDefinition
{
Name = "Output Directory",
Type = LaunchOptionType.String,
Description = "Override the output directory",
Options = { "--output-directory" }
},
new()
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.IterGpuInfo().Select(gpu => gpu.MemoryLevel).Max() switch
{
MemoryLevel.Low => "--lowvram",
MemoryLevel.Medium => "--normalvram",
_ => null
},
Options = { "--highvram", "--normalvram", "--lowvram", "--novram" }
},
new LaunchOptionDefinition
{
Name = "Use DirectML",
Type = LaunchOptionType.Bool,
Description = "Use pytorch with DirectML support",
InitialValue = HardwareHelper.PreferDirectML(),
Options = { "--directml" }
},
new LaunchOptionDefinition
{
Name = "Disable Xformers",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu(),
Options = { "--disable-xformers" }
},
new LaunchOptionDefinition
{
Name = "Auto-Launch",
Type = LaunchOptionType.Bool,
Options = { "--auto-launch" }
},
LaunchOptionDefinition.Extras
};
public override async Task InstallPackage(
string installLocation,
TorchVersion torchVersion,
@ -39,13 +114,23 @@ public class RuinedFooocus(
{
var venvRunner = await SetupVenv(installLocation, forceRecreate: true).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing torch...", isIndeterminate: true));
await InstallCudaTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing requirements...", isIndeterminate: true));
var requirements = new FilePath(installLocation, "requirements_versions.txt");
await venvRunner
.PipInstallFromRequirements(requirements, onConsoleOutput, excludes: "torch")
.PipInstall(
new PipInstallArgs()
.WithTorch("==2.0.1")
.WithTorchVision("==0.15.2")
.WithXFormers("==0.0.20")
.WithTorchExtraIndex("cu118")
.WithParsedFromRequirementsTxt(
await requirements.ReadAllTextAsync().ConfigureAwait(false),
excludePattern: "torch"
),
onConsoleOutput
)
.ConfigureAwait(false);
}
else
@ -60,5 +145,9 @@ public class RuinedFooocus(
)
.ConfigureAwait(false);
}
// Create output folder since it's not created by default
var outputFolder = new DirectoryPath(installLocation, OutputFolderName);
outputFolder.Create();
}
}

61
StabilityMatrix.Core/Processes/ProcessArgsBuilder.cs

@ -1,6 +1,5 @@
using System.Diagnostics;
using System.Collections.Immutable;
using System.Diagnostics.Contracts;
using OneOf;
namespace StabilityMatrix.Core.Processes;
@ -9,14 +8,7 @@ namespace StabilityMatrix.Core.Processes;
/// </summary>
public record ProcessArgsBuilder
{
protected ProcessArgsBuilder() { }
public ProcessArgsBuilder(params Argument[] arguments)
{
Arguments = arguments.ToList();
}
public List<Argument> Arguments { get; init; } = new();
public IImmutableList<Argument> Arguments { get; init; } = ImmutableArray<Argument>.Empty;
private IEnumerable<string> ToStringArgs()
{
@ -34,6 +26,11 @@ public record ProcessArgsBuilder
}
}
public ProcessArgsBuilder(params Argument[] arguments)
{
Arguments = arguments.ToImmutableArray();
}
/// <inheritdoc />
public override string ToString()
{
@ -45,8 +42,7 @@ public record ProcessArgsBuilder
return ToStringArgs().ToArray();
}
public static implicit operator ProcessArgs(ProcessArgsBuilder builder) =>
builder.ToProcessArgs();
public static implicit operator ProcessArgs(ProcessArgsBuilder builder) => builder.ToProcessArgs();
}
public static class ProcessArgBuilderExtensions
@ -55,7 +51,33 @@ public static class ProcessArgBuilderExtensions
public static T AddArg<T>(this T builder, Argument argument)
where T : ProcessArgsBuilder
{
return builder with { Arguments = builder.Arguments.Append(argument).ToList() };
return builder with { Arguments = builder.Arguments.Add(argument) };
}
[Pure]
public static T AddArgs<T>(this T builder, params Argument[] argument)
where T : ProcessArgsBuilder
{
return builder with { Arguments = builder.Arguments.AddRange(argument) };
}
[Pure]
public static T UpdateArg<T>(this T builder, string key, Argument argument)
where T : ProcessArgsBuilder
{
var oldArg = builder
.Arguments
.FirstOrDefault(x => x.Match(stringArg => stringArg == key, tupleArg => tupleArg.Item1 == key));
if (oldArg is null)
{
return builder.AddArg(argument);
}
return builder with
{
Arguments = builder.Arguments.Replace(oldArg, argument)
};
}
[Pure]
@ -64,15 +86,10 @@ public static class ProcessArgBuilderExtensions
{
return builder with
{
Arguments = builder.Arguments
.Where(
x =>
x.Match(
stringArg => stringArg != argumentKey,
tupleArg => tupleArg.Item1 != argumentKey
)
)
.ToList()
Arguments = builder
.Arguments
.Where(x => x.Match(stringArg => stringArg != argumentKey, tupleArg => tupleArg.Item1 != argumentKey))
.ToImmutableArray()
};
}
}

33
StabilityMatrix.Core/Python/PipInstallArgs.cs

@ -1,4 +1,7 @@
using StabilityMatrix.Core.Processes;
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Processes;
namespace StabilityMatrix.Core.Python;
@ -9,20 +12,36 @@ public record PipInstallArgs : ProcessArgsBuilder
public PipInstallArgs WithTorch(string version = "") => this.AddArg($"torch{version}");
public PipInstallArgs WithTorchDirectML(string version = "") =>
this.AddArg($"torch-directml{version}");
public PipInstallArgs WithTorchDirectML(string version = "") => this.AddArg($"torch-directml{version}");
public PipInstallArgs WithTorchVision(string version = "") =>
this.AddArg($"torchvision{version}");
public PipInstallArgs WithTorchVision(string version = "") => this.AddArg($"torchvision{version}");
public PipInstallArgs WithXFormers(string version = "") => this.AddArg($"xformers{version}");
public PipInstallArgs WithExtraIndex(string indexUrl) =>
this.AddArg(("--extra-index-url", indexUrl));
public PipInstallArgs WithExtraIndex(string indexUrl) => this.AddArg(("--extra-index-url", indexUrl));
public PipInstallArgs WithTorchExtraIndex(string index) =>
this.AddArg(("--extra-index-url", $"https://download.pytorch.org/whl/{index}"));
public PipInstallArgs WithParsedFromRequirementsTxt(
string requirements,
[StringSyntax(StringSyntaxAttribute.Regex)] string? excludePattern = null
)
{
var requirementsEntries = requirements
.SplitLines(StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
.AsEnumerable();
if (excludePattern is not null)
{
var excludeRegex = new Regex($"^{excludePattern}$");
requirementsEntries = requirementsEntries.Where(s => !excludeRegex.IsMatch(s));
}
return this.AddArgs(requirementsEntries.Select(s => (Argument)s).ToArray());
}
/// <inheritdoc />
public override string ToString()
{

87
StabilityMatrix.Core/Python/PipPackageSpecifier.cs

@ -0,0 +1,87 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using StabilityMatrix.Core.Processes;
namespace StabilityMatrix.Core.Python;
public partial record PipPackageSpecifier
{
public required string Name { get; init; }
public string? Constraint { get; init; }
public string? Version { get; init; }
public string? VersionConstraint => Constraint is null || Version is null ? null : Constraint + Name;
public static PipPackageSpecifier Parse(string value)
{
var result = TryParse(value, true, out var packageSpecifier);
Debug.Assert(result);
return packageSpecifier!;
}
public static bool TryParse(string value, [NotNullWhen(true)] out PipPackageSpecifier? packageSpecifier)
{
return TryParse(value, false, out packageSpecifier);
}
private static bool TryParse(
string value,
bool throwOnFailure,
[NotNullWhen(true)] out PipPackageSpecifier? packageSpecifier
)
{
var match = PackageSpecifierRegex().Match(value);
if (!match.Success)
{
if (throwOnFailure)
{
throw new ArgumentException($"Invalid package specifier: {value}");
}
packageSpecifier = null;
return false;
}
packageSpecifier = new PipPackageSpecifier
{
Name = match.Groups["package_name"].Value,
Constraint = match.Groups["version_constraint"].Value,
Version = match.Groups["version"].Value
};
return true;
}
/// <inheritdoc />
public override string ToString()
{
return Name + VersionConstraint;
}
public static implicit operator Argument(PipPackageSpecifier specifier)
{
return specifier.VersionConstraint is null
? new Argument(specifier.Name)
: new Argument((specifier.Name, specifier.VersionConstraint));
}
public static implicit operator PipPackageSpecifier(string specifier)
{
return Parse(specifier);
}
/// <summary>
/// Regex to match a pip package specifier.
/// </summary>
[GeneratedRegex(
"(?<package_name>[a-zA-Z0-9_]+)(?<version_specifier>(?<version_constraint>==|>=|<=|>|<|~=|!=)(<version>[a-zA-Z0-9_.]+))?",
RegexOptions.CultureInvariant,
1000
)]
private static partial Regex PackageSpecifierRegex();
}
Loading…
Cancel
Save