Merge branch 'claudedo/f359858ac98a439593e459df9c5d0a5d'

This commit is contained in:
mika kuns
2026-08-05 16:49:08 +02:00
26 changed files with 1169 additions and 43 deletions
@@ -143,6 +143,9 @@ public interface IWorkerClient : INotifyPropertyChanged
/// <summary>Raised whenever the worker's usage poller ticks (success or failure).</summary>
event Action<UsageSnapshotDto>? UsageUpdatedEvent;
Task<UsageSnapshotDto?> GetUsageSnapshotAsync();
/// <summary>Forces an out-of-band usage poll on the worker and returns the fresh snapshot.</summary>
Task<UsageSnapshotDto?> RefreshUsageAsync();
Task<IReadOnlyList<ModelUsageRowDto>> GetModelUsageAsync(DateOnly from, DateOnly to);
Task<IReadOnlyList<TaskUsageRowDto>> GetTaskUsageAsync(DateOnly from, DateOnly to);
}
+3
View File
@@ -582,6 +582,9 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
public Task<UsageSnapshotDto?> GetUsageSnapshotAsync()
=> TryInvokeAsync<UsageSnapshotDto>("GetUsageSnapshot");
public Task<UsageSnapshotDto?> RefreshUsageAsync()
=> TryInvokeAsync<UsageSnapshotDto>("RefreshUsage");
public async Task<IReadOnlyList<ModelUsageRowDto>> GetModelUsageAsync(DateOnly from, DateOnly to)
=> await TryInvokeAsync<List<ModelUsageRowDto>>("GetModelUsage", from, to) ?? [];
@@ -40,6 +40,8 @@ public sealed partial class UsageMonitorModalViewModel : ViewModelBase
[NotifyPropertyChangedFor(nameof(ModelsEmpty))]
private bool _isBusy;
[ObservableProperty] private bool _isRefreshing;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ModelsEmpty))]
private IReadOnlyList<ModelUsageDisplayRow> _modelRows = Array.Empty<ModelUsageDisplayRow>();
@@ -92,6 +94,28 @@ public sealed partial class UsageMonitorModalViewModel : ViewModelBase
private void OnUsageUpdated(UsageSnapshotDto snapshot) => Snapshot = snapshot;
/// <summary>
/// Manual refresh: the worker polls usage on a slow cadence (15 min idle / 5 min while a
/// task runs) to stay clear of the endpoint's 429s, so this is the way to get a number now.
/// </summary>
[RelayCommand]
private async Task RefreshAsync()
{
if (IsRefreshing) return;
IsRefreshing = true;
try
{
var snapshot = await _worker.RefreshUsageAsync();
if (snapshot is not null) Snapshot = snapshot;
await LoadUsageDataAsync();
}
catch (Exception ex)
{
ErrorReported?.Invoke(Loc.T("vm.usageMonitor.refreshFailed", ex.Message));
}
finally { IsRefreshing = false; }
}
private void ApplyPresetRange(int days)
{
var today = DateOnly.FromDateTime(DateTime.Today);
@@ -60,6 +60,18 @@
</Border>
</StackPanel>
<!-- Refresh: the worker polls on a slow cadence to avoid the endpoint's 429s -->
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Spacing="8"
Margin="20,12,20,0" VerticalAlignment="Center">
<Button Classes="btn" Content="{loc:Tr modals.usageMonitor.refresh}"
Command="{Binding RefreshCommand}"
IsEnabled="{Binding !IsRefreshing}"/>
<Ellipse Classes="spinner" Width="14" Height="14" VerticalAlignment="Center"
IsVisible="{Binding IsRefreshing}"/>
<TextBlock Classes="meta" VerticalAlignment="Center"
Text="{loc:Tr modals.usageMonitor.refreshHint}"/>
</StackPanel>
<!-- Gauges -->
<ItemsControl DockPanel.Dock="Top" Margin="20,12,20,0" ItemsSource="{Binding GaugeRows}">
<ItemsControl.ItemsPanel>