Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add safetensor metadata parsing and display #1074

Merged
merged 4 commits into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions StabilityMatrix.Avalonia/DesignData/DesignData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,24 @@ public static CompletionList SampleCompletionList
}
};

public static SafetensorMetadataViewModel SafetensorMetadataViewModel =>
DialogFactory.Get<SafetensorMetadataViewModel>(vm =>
{
vm.Metadata = new SafetensorMetadata
{
TagFrequency = Enumerable
.Range(1, 100)
.Select(i => new SafetensorMetadata.Tag("tag" + i, i))
.ToList(),
OtherMetadata = new List<SafetensorMetadata.Metadata>
{
new("Name1", "Value1"),
new("Name2", "Value2"),
new("Name3", "Value3"),
}
};
});

public static ModelMetadataEditorDialogViewModel MetadataEditorDialogViewModel =>
DialogFactory.Get<ModelMetadataEditorDialogViewModel>(vm =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,55 @@ private async Task RenameAsync()
}
}

[RelayCommand]
private async Task OpenSafetensorMetadataViewer()
{
if (!CheckpointFile.SafetensorMetadataParsed)
{
if (
!settingsManager.IsLibraryDirSet
|| new DirectoryPath(settingsManager.ModelsDirectory) is not { Exists: true } modelsDir
)
{
return;
}

try
{
var safetensorPath = CheckpointFile.GetFullPath(modelsDir);

var metadata = await SafetensorMetadata.ParseAsync(safetensorPath);

CheckpointFile.SafetensorMetadataParsed = true;
CheckpointFile.SafetensorMetadata = metadata;
}
catch (Exception ex)
{
logger.LogWarning(ex, "Failed to parse safetensor metadata");
return;
}
}

if (!CheckpointFile.SafetensorMetadataParsed)
{
return;
}

var vm = vmFactory.Get<SafetensorMetadataViewModel>(vm =>
{
vm.ModelName = CheckpointFile.DisplayModelName;
vm.Metadata = CheckpointFile.SafetensorMetadata;
});

var dialog = vm.GetDialog();
dialog.MinDialogHeight = 800;
dialog.MinDialogWidth = 700;
dialog.CloseButtonText = "Close";
dialog.DefaultButton = ContentDialogButton.Close;

await dialog.ShowAsync();
}

[RelayCommand]
private async Task OpenMetadataEditor()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Injectio.Attributes;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.Views.Dialogs;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Models;

namespace StabilityMatrix.Avalonia.ViewModels.Dialogs;

[View(typeof(SafetensorMetadataDialog))]
[ManagedService]
[RegisterSingleton<SafetensorMetadataViewModel>]
public partial class SafetensorMetadataViewModel : ContentDialogViewModelBase
{
[ObservableProperty]
private string? modelName;

[ObservableProperty]
private SafetensorMetadata? metadata;

[RelayCommand]
public void CopyTagToClipboard(string tag)
{
App.Clipboard?.SetTextAsync(tag);
}
}
5 changes: 5 additions & 0 deletions StabilityMatrix.Avalonia/Views/CheckpointsPage.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,11 @@
</ui:MenuFlyoutItem.IsVisible>
</ui:MenuFlyoutItem>

<ui:MenuFlyoutItem
Command="{Binding OpenSafetensorMetadataViewerCommand}"
IconSource="Tag"
IsVisible="{Binding CheckpointFile.IsSafetensorFile}"
Text="View Safetensor Metadata" />
<ui:MenuFlyoutItem
Command="{Binding OpenMetadataEditorCommand}"
IconSource="Edit"
Expand Down
113 changes: 113 additions & 0 deletions StabilityMatrix.Avalonia/Views/Dialogs/SafetensorMetadataDialog.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<controls:UserControlBase
x:Class="StabilityMatrix.Avalonia.Views.Dialogs.SafetensorMetadataDialog"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dialogs="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Dialogs"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core"
xmlns:ui="using:FluentAvalonia.UI.Controls"
d:DataContext="{x:Static mocks:DesignData.SafetensorMetadataViewModel}"
d:DesignHeight="550"
d:DesignWidth="700"
x:DataType="dialogs:SafetensorMetadataViewModel"
mc:Ignorable="d">
<Grid RowDefinitions="Auto,Auto,Auto">
<TextBlock
Margin="8"
HorizontalAlignment="Center"
FontSize="24"
FontWeight="SemiBold"
Text="Safetensor Metadata" />
<TextBlock
Grid.Row="1"
Margin="8"
HorizontalAlignment="Center"
FontSize="16"
FontWeight="SemiBold"
Text="{Binding ModelName}" />

<TextBlock
Grid.Row="2"
Margin="10,20"
HorizontalAlignment="Center"
FontSize="16"
FontStyle="Italic"
IsVisible="{Binding Metadata, Converter={x:Static ObjectConverters.IsNull}}"
Text="No Metadata" />

<Grid
Grid.Row="2"
IsVisible="{Binding Metadata, Converter={x:Static ObjectConverters.IsNotNull}}"
RowDefinitions="Auto,Auto,Auto,Auto">

<!-- List of tags -->
<TextBlock
Grid.Row="0"
Margin="8"
HorizontalAlignment="Left"
FontSize="16"
FontWeight="SemiBold"
IsVisible="{Binding Metadata.TagFrequency, Converter={x:Static ObjectConverters.IsNotNull}}"
Text="Trained Tags" />

<ui:ItemsRepeater
Grid.Row="1"
Margin="8"
IsVisible="{Binding Metadata.TagFrequency, Converter={x:Static ObjectConverters.IsNotNull}}"
ItemsSource="{Binding Metadata.TagFrequency}">
<ui:ItemsRepeater.Layout>
<ui:FlowLayout
MinColumnSpacing="4"
MinRowSpacing="4"
Orientation="Horizontal" />
</ui:ItemsRepeater.Layout>
<ui:ItemsRepeater.ItemTemplate>
<DataTemplate>
<Button
Command="{Binding $parent[ui:ItemsRepeater].((dialogs:SafetensorMetadataViewModel)DataContext).CopyTagToClipboardCommand}"
CommandParameter="{Binding Name}"
Cursor="Hand">
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="SemiBold" Text="{Binding Name}" />
<TextBlock Margin="5,0,0,0" Text="{Binding Frequency}" />
</StackPanel>
</Button>
</DataTemplate>
</ui:ItemsRepeater.ItemTemplate>
</ui:ItemsRepeater>

<!-- All other metadata -->
<TextBlock
Grid.Row="2"
Margin="8"
FontSize="16"
FontWeight="SemiBold"
Text="Other Metadata" />
<TextBlock
Grid.Row="3"
Margin="8"
FontSize="16"
FontStyle="Italic"
IsVisible="{Binding !Metadata.OtherMetadata.Count}"
Text="No Other Metadata" />
<ui:ItemsRepeater
Grid.Row="3"
Margin="8"
IsVisible="{Binding !!Metadata.OtherMetadata.Count}"
ItemsSource="{Binding Metadata.OtherMetadata}">
<ui:ItemsRepeater.ItemTemplate>
<DataTemplate>
<StackPanel Margin="5" Orientation="Vertical">
<TextBlock FontWeight="SemiBold" Text="{Binding Name}" />
<TextBlock Text="{Binding Value}" />
</StackPanel>
</DataTemplate>
</ui:ItemsRepeater.ItemTemplate>
</ui:ItemsRepeater>
</Grid>
</Grid>
</controls:UserControlBase>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Injectio.Attributes;

namespace StabilityMatrix.Avalonia.Views.Dialogs;

[RegisterTransient<SafetensorMetadataDialog>]
public partial class SafetensorMetadataDialog : UserControl
{
public SafetensorMetadataDialog()
{
InitializeComponent();
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
4 changes: 4 additions & 0 deletions StabilityMatrix.Core/Helper/Compat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,11 @@ public static string GetExecutableName()
var appImage = Environment.GetEnvironmentVariable("APPIMAGE");
if (string.IsNullOrEmpty(appImage))
{
#if DEBUG
return "DEBUG_NOT_RUNNING_IN_APPIMAGE";
#else
throw new Exception("Could not find APPIMAGE environment variable");
#endif
}
return Path.GetFileName(appImage);
}
Expand Down
11 changes: 10 additions & 1 deletion StabilityMatrix.Core/Models/Database/LocalModelFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,10 @@ public override int GetHashCode()
/// <summary>
/// Blake3 hash of the file.
/// </summary>
public string? HashBlake3 => ConnectedModelInfo?.Hashes.BLAKE3;
public string? HashBlake3 => ConnectedModelInfo?.Hashes?.BLAKE3;

[BsonIgnore]
public bool IsSafetensorFile => Path.GetExtension(RelativePath) == ".safetensors";

[BsonIgnore]
public string? PreviewImageFullPathGlobal =>
Expand Down Expand Up @@ -151,6 +154,12 @@ public override int GetHashCode()
[MemberNotNullWhen(true, nameof(ConnectedModelInfo))]
public bool HasCivitMetadata => HasConnectedModel && ConnectedModelInfo.ModelId != null;

[BsonIgnore]
public SafetensorMetadata? SafetensorMetadata { get; set; }

[BsonIgnore]
public bool SafetensorMetadataParsed { get; set; }

public string GetFullPath(string rootModelDirectory)
{
return Path.Combine(rootModelDirectory, RelativePath);
Expand Down
Loading
Loading