Browse Source

fix update check timer

pull/240/head
JT 1 year ago
parent
commit
465d74d569
  1. 4
      StabilityMatrix.Avalonia/ViewModels/Dialogs/ExceptionViewModel.cs
  2. 25
      StabilityMatrix.Avalonia/ViewModels/FirstLaunchSetupViewModel.cs
  3. 19
      StabilityMatrix.Avalonia/ViewModels/Inference/StackCardViewModel.cs
  4. 30
      StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs
  5. 128
      StabilityMatrix.Avalonia/ViewModels/NewCheckpointsPageViewModel.cs
  6. 2
      StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs
  7. 20
      StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs
  8. 58
      StabilityMatrix.Avalonia/ViewModels/RefreshBadgeViewModel.cs

4
StabilityMatrix.Avalonia/ViewModels/Dialogs/ExceptionViewModel.cs

@ -11,8 +11,8 @@ namespace StabilityMatrix.Avalonia.ViewModels.Dialogs;
public partial class ExceptionViewModel : ViewModelBase
{
public Exception? Exception { get; set; }
public string? Message => Exception?.Message;
public string? ExceptionType => Exception?.GetType().Name ?? "";
}

25
StabilityMatrix.Avalonia/ViewModels/FirstLaunchSetupViewModel.cs

@ -22,14 +22,17 @@ public partial class FirstLaunchSetupViewModel : ViewModelBase
private string gpuInfoText = string.Empty;
[ObservableProperty]
private RefreshBadgeViewModel checkHardwareBadge = new()
{
WorkingToolTipText = "We're checking some hardware specifications to determine compatibility.",
SuccessToolTipText = "Everything looks good!",
FailToolTipText = "We recommend a GPU with CUDA support for the best experience. " +
"You can continue without one, but some packages may not work, and inference may be slower.",
FailColorBrush = ThemeColors.ThemeYellow,
};
private RefreshBadgeViewModel checkHardwareBadge =
new()
{
WorkingToolTipText =
"We're checking some hardware specifications to determine compatibility.",
SuccessToolTipText = "Everything looks good!",
FailToolTipText =
"We recommend a GPU with CUDA support for the best experience. "
+ "You can continue without one, but some packages may not work, and inference may be slower.",
FailColorBrush = ThemeColors.ThemeYellow,
};
public FirstLaunchSetupViewModel()
{
@ -45,14 +48,16 @@ public partial class FirstLaunchSetupViewModel : ViewModelBase
gpuInfo = await Task.Run(() => HardwareHelper.IterGpuInfo().ToArray());
}
// First Nvidia GPU
var activeGpu = gpuInfo.FirstOrDefault(gpu => gpu.Name?.ToLowerInvariant().Contains("nvidia") ?? false);
var activeGpu = gpuInfo.FirstOrDefault(
gpu => gpu.Name?.ToLowerInvariant().Contains("nvidia") ?? false
);
var isNvidia = activeGpu is not null;
// Otherwise first GPU
activeGpu ??= gpuInfo.FirstOrDefault();
GpuInfoText = activeGpu is null
? "No GPU detected"
: $"{activeGpu.Name} ({Size.FormatBytes(activeGpu.MemoryBytes)})";
return isNvidia;
}

19
StabilityMatrix.Avalonia/ViewModels/Inference/StackCardViewModel.cs

@ -16,14 +16,16 @@ public class StackCardViewModel : StackViewModelBase
public override void LoadStateFromJsonObject(JsonObject state)
{
var model = DeserializeModel<StackCardModel>(state);
if (model.Cards is null) return;
if (model.Cards is null)
return;
foreach (var (i, card) in model.Cards.Enumerate())
{
// Ignore if more than cards than we have
if (i > Cards.Count - 1) break;
if (i > Cards.Count - 1)
break;
Cards[i].LoadStateFromJsonObject(card);
}
}
@ -31,9 +33,8 @@ public class StackCardViewModel : StackViewModelBase
/// <inheritdoc />
public override JsonObject SaveStateToJsonObject()
{
return SerializeModel(new StackCardModel
{
Cards = Cards.Select(x => x.SaveStateToJsonObject()).ToList()
});
return SerializeModel(
new StackCardModel { Cards = Cards.Select(x => x.SaveStateToJsonObject()).ToList() }
);
}
}

30
StabilityMatrix.Avalonia/ViewModels/Inference/StackExpanderViewModel.cs

@ -18,23 +18,25 @@ public partial class StackExpanderViewModel : StackViewModelBase
[ObservableProperty]
[property: JsonIgnore]
private string? title;
[ObservableProperty]
[ObservableProperty]
private bool isEnabled;
/// <inheritdoc />
public override void LoadStateFromJsonObject(JsonObject state)
{
var model = DeserializeModel<StackExpanderModel>(state);
IsEnabled = model.IsEnabled;
if (model.Cards is null) return;
if (model.Cards is null)
return;
foreach (var (i, card) in model.Cards.Enumerate())
{
// Ignore if more than cards than we have
if (i > Cards.Count - 1) break;
if (i > Cards.Count - 1)
break;
Cards[i].LoadStateFromJsonObject(card);
}
}
@ -42,10 +44,12 @@ public partial class StackExpanderViewModel : StackViewModelBase
/// <inheritdoc />
public override JsonObject SaveStateToJsonObject()
{
return SerializeModel(new StackExpanderModel
{
IsEnabled = IsEnabled,
Cards = Cards.Select(x => x.SaveStateToJsonObject()).ToList()
});
return SerializeModel(
new StackExpanderModel
{
IsEnabled = IsEnabled,
Cards = Cards.Select(x => x.SaveStateToJsonObject()).ToList()
}
);
}
}

128
StabilityMatrix.Avalonia/ViewModels/NewCheckpointsPageViewModel.cs

@ -43,12 +43,17 @@ public partial class NewCheckpointsPageViewModel : PageViewModelBase
private readonly ServiceManager<ViewModelBase> dialogFactory;
private readonly INotificationService notificationService;
public override string Title => "Checkpoint Manager";
public override IconSource IconSource => new SymbolIconSource
{Symbol = Symbol.Cellular5g, IsFilled = true};
public override IconSource IconSource =>
new SymbolIconSource { Symbol = Symbol.Cellular5g, IsFilled = true };
public NewCheckpointsPageViewModel(ILogger<NewCheckpointsPageViewModel> logger,
ISettingsManager settingsManager, ILiteDbContext liteDbContext, ICivitApi civitApi,
ServiceManager<ViewModelBase> dialogFactory, INotificationService notificationService)
public NewCheckpointsPageViewModel(
ILogger<NewCheckpointsPageViewModel> logger,
ISettingsManager settingsManager,
ILiteDbContext liteDbContext,
ICivitApi civitApi,
ServiceManager<ViewModelBase> dialogFactory,
INotificationService notificationService
)
{
this.logger = logger;
this.settingsManager = settingsManager;
@ -62,23 +67,27 @@ public partial class NewCheckpointsPageViewModel : PageViewModelBase
[NotifyPropertyChangedFor(nameof(ConnectedCheckpoints))]
[NotifyPropertyChangedFor(nameof(NonConnectedCheckpoints))]
private ObservableCollection<CheckpointFile> allCheckpoints = new();
[ObservableProperty]
private ObservableCollection<CivitModel> civitModels = new();
public ObservableCollection<CheckpointFile> ConnectedCheckpoints => new(
AllCheckpoints.Where(x => x.IsConnectedModel)
.OrderBy(x => x.ConnectedModel!.ModelName)
.ThenBy(x => x.ModelType)
.GroupBy(x => x.ConnectedModel!.ModelId)
.Select(x => x.First()));
public ObservableCollection<CheckpointFile> ConnectedCheckpoints =>
new(
AllCheckpoints
.Where(x => x.IsConnectedModel)
.OrderBy(x => x.ConnectedModel!.ModelName)
.ThenBy(x => x.ModelType)
.GroupBy(x => x.ConnectedModel!.ModelId)
.Select(x => x.First())
);
public ObservableCollection<CheckpointFile> NonConnectedCheckpoints => new(
AllCheckpoints.Where(x => !x.IsConnectedModel).OrderBy(x => x.ModelType));
public ObservableCollection<CheckpointFile> NonConnectedCheckpoints =>
new(AllCheckpoints.Where(x => !x.IsConnectedModel).OrderBy(x => x.ModelType));
public override async Task OnLoadedAsync()
{
if (Design.IsDesignMode) return;
if (Design.IsDesignMode)
return;
var files = CheckpointFile.GetAllCheckpointFiles(settingsManager.ModelsDirectory);
AllCheckpoints = new ObservableCollection<CheckpointFile>(files);
@ -88,17 +97,17 @@ public partial class NewCheckpointsPageViewModel : PageViewModelBase
{
CommaSeparatedModelIds = string.Join(',', connectedModelIds)
};
// See if query is cached
var cachedQuery = await liteDbContext.CivitModelQueryCache
.IncludeAll()
.FindByIdAsync(ObjectHash.GetMd5Guid(modelRequest));
// If cached, update model cards
if (cachedQuery is not null)
{
CivitModels = new ObservableCollection<CivitModel>(cachedQuery.Items);
// Start remote query (background mode)
// Skip when last query was less than 2 min ago
var timeSinceCache = DateTimeOffset.UtcNow - cachedQuery.InsertedAt;
@ -112,24 +121,34 @@ public partial class NewCheckpointsPageViewModel : PageViewModelBase
await CivitQuery(modelRequest);
}
}
public async Task ShowVersionDialog(int modelId)
{
var model = CivitModels.FirstOrDefault(m => m.Id == modelId);
if (model == null)
{
notificationService.Show(new Notification("Model has no versions available",
"This model has no versions available for download", NotificationType.Warning));
notificationService.Show(
new Notification(
"Model has no versions available",
"This model has no versions available for download",
NotificationType.Warning
)
);
return;
}
var versions = model.ModelVersions;
if (versions is null || versions.Count == 0)
{
notificationService.Show(new Notification("Model has no versions available",
"This model has no versions available for download", NotificationType.Warning));
notificationService.Show(
new Notification(
"Model has no versions available",
"This model has no versions available for download",
NotificationType.Warning
)
);
return;
}
var dialog = new BetterContentDialog
{
Title = model.Name,
@ -138,19 +157,21 @@ public partial class NewCheckpointsPageViewModel : PageViewModelBase
IsFooterVisible = false,
MaxDialogWidth = 750,
};
var viewModel = dialogFactory.Get<SelectModelVersionViewModel>();
viewModel.Dialog = dialog;
viewModel.Versions = versions.Select(version =>
new ModelVersionViewModel(
settingsManager.Settings.InstalledModelHashes ?? new HashSet<string>(), version))
viewModel.Versions = versions
.Select(
version =>
new ModelVersionViewModel(
settingsManager.Settings.InstalledModelHashes ?? new HashSet<string>(),
version
)
)
.ToImmutableArray();
viewModel.SelectedVersionViewModel = viewModel.Versions[0];
dialog.Content = new SelectModelVersionDialog
{
DataContext = viewModel
};
dialog.Content = new SelectModelVersionDialog { DataContext = viewModel };
var result = await dialog.ShowAsync();
@ -170,8 +191,10 @@ public partial class NewCheckpointsPageViewModel : PageViewModelBase
var modelResponse = await civitApi.GetModels(request);
var models = modelResponse.Items;
// Filter out unknown model types and archived/taken-down models
models = models.Where(m => m.Type.ConvertTo<SharedFolderType>() > 0)
.Where(m => m.Mode == null).ToList();
models = models
.Where(m => m.Type.ConvertTo<SharedFolderType>() > 0)
.Where(m => m.Mode == null)
.ToList();
// Database update calls will invoke `OnModelsUpdated`
// Add to database
@ -185,7 +208,8 @@ public partial class NewCheckpointsPageViewModel : PageViewModelBase
Request = request,
Items = models,
Metadata = modelResponse.Metadata
});
}
);
if (cacheNew)
{
@ -194,26 +218,42 @@ public partial class NewCheckpointsPageViewModel : PageViewModelBase
}
catch (OperationCanceledException)
{
notificationService.Show(new Notification("Request to CivitAI timed out",
"Could not check for checkpoint updates. Please try again later."));
notificationService.Show(
new Notification(
"Request to CivitAI timed out",
"Could not check for checkpoint updates. Please try again later."
)
);
logger.LogWarning($"CivitAI query timed out ({request})");
}
catch (HttpRequestException e)
{
notificationService.Show(new Notification("CivitAI can't be reached right now",
"Could not check for checkpoint updates. Please try again later."));
notificationService.Show(
new Notification(
"CivitAI can't be reached right now",
"Could not check for checkpoint updates. Please try again later."
)
);
logger.LogWarning(e, $"CivitAI query HttpRequestException ({request})");
}
catch (ApiException e)
{
notificationService.Show(new Notification("CivitAI can't be reached right now",
"Could not check for checkpoint updates. Please try again later."));
notificationService.Show(
new Notification(
"CivitAI can't be reached right now",
"Could not check for checkpoint updates. Please try again later."
)
);
logger.LogWarning(e, $"CivitAI query ApiException ({request})");
}
catch (Exception e)
{
notificationService.Show(new Notification("CivitAI can't be reached right now",
$"Unknown exception during CivitAI query: {e.GetType().Name}"));
notificationService.Show(
new Notification(
"CivitAI can't be reached right now",
$"Unknown exception during CivitAI query: {e.GetType().Name}"
)
);
logger.LogError(e, $"CivitAI query unknown exception ({request})");
}
}

2
StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs

@ -141,7 +141,7 @@ public partial class OutputsPageViewModel : PageViewModelBase
if (!settingsManager.IsLibraryDirSet)
return;
Directory.CreateDirectory(settingsManager.ImagesDirectory);
var packageCategories = settingsManager.Settings.InstalledPackages
.Where(x => !x.UseSharedOutputFolder)

20
StabilityMatrix.Avalonia/ViewModels/PackageManagerViewModel.cs

@ -65,14 +65,14 @@ public partial class PackageManagerViewModel : PageViewModelBase
public IObservableCollection<PackageCardViewModel> PackageCards { get; } =
new ObservableCollectionExtended<PackageCardViewModel>();
private DispatcherTimer timer;
public PackageManagerViewModel(
ISettingsManager settingsManager,
ServiceManager<ViewModelBase> dialogFactory,
INotificationService notificationService,
ILogger<PackageManagerViewModel> logger)
ILogger<PackageManagerViewModel> logger
)
{
this.settingsManager = settingsManager;
this.dialogFactory = dialogFactory;
@ -99,11 +99,7 @@ public partial class PackageManagerViewModel : PageViewModelBase
.Bind(PackageCards)
.Subscribe();
timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(5),
IsEnabled = true
};
timer = new DispatcherTimer { Interval = TimeSpan.FromMinutes(15), IsEnabled = true };
timer.Tick += async (_, _) => await CheckPackagesForUpdates();
}
@ -129,7 +125,7 @@ public partial class PackageManagerViewModel : PageViewModelBase
var currentUnknown = await Task.Run(IndexUnknownPackages);
unknownInstalledPackages.Edit(s => s.Load(currentUnknown));
timer.Start();
}
@ -186,9 +182,13 @@ public partial class PackageManagerViewModel : PageViewModelBase
{
await package.OnLoadedAsync();
}
catch
catch (Exception e)
{
logger.LogWarning("Failed to check for updates for {Package}", package?.Package?.PackageName);
logger.LogError(
e,
"Failed to check for updates for {Package}",
package?.Package?.PackageName
);
}
}
}

58
StabilityMatrix.Avalonia/ViewModels/RefreshBadgeViewModel.cs

@ -21,7 +21,7 @@ namespace StabilityMatrix.Avalonia.ViewModels;
public partial class RefreshBadgeViewModel : ViewModelBase
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public string WorkingToolTipText { get; set; } = "Loading...";
public string SuccessToolTipText { get; set; } = "Success";
public string InactiveToolTipText { get; set; } = "";
@ -34,7 +34,7 @@ public partial class RefreshBadgeViewModel : ViewModelBase
public IBrush SuccessColorBrush { get; set; } = ThemeColors.ThemeGreen;
public IBrush InactiveColorBrush { get; set; } = ThemeColors.ThemeYellow;
public IBrush FailColorBrush { get; set; } = ThemeColors.ThemeYellow;
public Func<Task<bool>>? RefreshFunc { get; set; }
[ObservableProperty]
@ -43,7 +43,7 @@ public partial class RefreshBadgeViewModel : ViewModelBase
[NotifyPropertyChangedFor(nameof(CurrentToolTip))]
[NotifyPropertyChangedFor(nameof(Icon))]
private ProgressState state;
public bool IsWorking => State == ProgressState.Working;
/*public ControlAppearance Appearance => State switch
@ -53,36 +53,40 @@ public partial class RefreshBadgeViewModel : ViewModelBase
ProgressState.Failed => ControlAppearance.Danger,
_ => ControlAppearance.Secondary
};*/
public IBrush ColorBrush => State switch
{
ProgressState.Success => SuccessColorBrush,
ProgressState.Inactive => InactiveColorBrush,
ProgressState.Failed => FailColorBrush,
_ => Brushes.Gray
};
public string CurrentToolTip => State switch
{
ProgressState.Working => WorkingToolTipText,
ProgressState.Success => SuccessToolTipText,
ProgressState.Inactive => InactiveToolTipText,
ProgressState.Failed => FailToolTipText,
_ => ""
};
public Symbol Icon => State switch
{
ProgressState.Success => SuccessIcon,
ProgressState.Failed => FailIcon,
_ => InactiveIcon
};
public IBrush ColorBrush =>
State switch
{
ProgressState.Success => SuccessColorBrush,
ProgressState.Inactive => InactiveColorBrush,
ProgressState.Failed => FailColorBrush,
_ => Brushes.Gray
};
public string CurrentToolTip =>
State switch
{
ProgressState.Working => WorkingToolTipText,
ProgressState.Success => SuccessToolTipText,
ProgressState.Inactive => InactiveToolTipText,
ProgressState.Failed => FailToolTipText,
_ => ""
};
public Symbol Icon =>
State switch
{
ProgressState.Success => SuccessIcon,
ProgressState.Failed => FailIcon,
_ => InactiveIcon
};
[RelayCommand]
private async Task Refresh()
{
Logger.Info("Running refresh command...");
if (RefreshFunc == null) return;
if (RefreshFunc == null)
return;
State = ProgressState.Working;
try

Loading…
Cancel
Save