Kosten je Einheit aufrechnen und Abweichungen melden (#699)

- IUsageRepository.GetDailyByAgentAsync: Tagessummen je Agent, Abo-Tokens getrennt
- UnitCosts: je Einheit samt Untereinheiten heute/7/30 Tage, Monat, Vormonat,
  Durchschnittstag (28 Tage, ab 7 Tagen Verlauf), Maximum = Summe der Agentengrenzen
- Meldungen: Abweichung vom eigenen Verlauf, Naehe zur Grenzsumme, Monatsschwellen
  der Instanz; CostWatch alle 15 min, je Einheit/Tag einmal (Tabelle CostAlerts),
  als Aufgabe an den Menschen; schaltet nichts ab
- API /usage/units, /usage/cost-alerts; Zeitreihe gruppiert nach Einheit
- WebUI: Karte "Kosten je Einheit" mit Meldungen in der Verbrauchsansicht; i18n en/de
- Einstellungen instance.json costWatch
- Tests: UnitCostsTests (8)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-10-01 09:09:42 +02:00
co-authored by Claude Opus 5.5
parent 7eb211fd06
commit da34b215f7
15 changed files with 667 additions and 2 deletions
+11
View File
@@ -129,6 +129,9 @@ public sealed class AppHost : IAsyncDisposable
/// <summary>#660: Usage analysis, run details and prompt history for the user interfaces.</summary>
public RunInsights? Insights { get; private init; }
/// <summary>#699: Costs per unit and the alerts on them.</summary>
public Core.Accounting.CostWatch? CostWatch { get; private set; }
/// <summary>Modellpreise von OpenRouter - fuer Budget und Kostenprognose (#595). Laedt im Hintergrund.</summary>
public ModelPricingCatalog? Pricing { get; private init; }
@@ -704,6 +707,14 @@ public sealed class AppHost : IAsyncDisposable
historyKeeper.Start();
host._shutdown.Add(historyKeeper.DisposeAsync);
// #699: costs per unit, and what changed in them.
var costWatch = new Core.Accounting.CostWatch(
storage, usage, () => Core.Organisation.OrgChart.From(instance), () => instance.CostWatch, board,
loggerFactory.CreateLogger("ClawdDotNet.Core.Accounting.CostWatch"));
host.CostWatch = costWatch;
costWatch.Start();
host._shutdown.Add(costWatch.DisposeAsync);
// #593: Angelegt wurde der Dienst schon immer, gestartet nie — weder kopflos noch
// mit Fenster. Die Kopfzeile der Weboberflaeche stand deshalb dauerhaft auf
// "OpenRouter: —" und /api/status auf "Prüfe…".