#608: Ein wegen erschoepften Budgets abgelehnter Lauf hinterlaesst jetzt einen Beleg (Status BudgetExceeded) an Aufgabe und Verlauf; die Log-Warnung kommt einmal je Agent, Grenze und Tag. Die Kostenprognose zaehlt Ablehnungen nicht als Lauf und warnt, wenn die geplanten Aufgaben eines Agenten sein Tagesbudget uebersteigen (API + Verbrauchsansicht). #607: Passt schon der erste Treffer nicht in den Abruf, wird er gekuerzt (Anfang und Ende bleiben) statt ganz zu fehlen. remember weist auf gleichnamige Eintraege ohne Schluessel und auf Eintraege nahe der Abrufgrenze hin. Version 0.1.9. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using ClawdDotNet.Core.Api;
|
||||
using ClawdDotNet.Core.Audit;
|
||||
using ClawdDotNet.Core.Config;
|
||||
using ClawdDotNet.Core.Engine;
|
||||
using ClawdDotNet.Core.Tasks;
|
||||
|
||||
namespace ClawdDotNet.Core.Accounting;
|
||||
@@ -34,6 +35,12 @@ public sealed record AufgabenPrognose(
|
||||
int Stichprobe,
|
||||
decimal KostenProTag);
|
||||
|
||||
/// <summary>
|
||||
/// Ein Agent, dessen Aufgaben laut Prognose mehr kosten, als sein Tagesbudget erlaubt (#608).
|
||||
/// Die spaeten Laeufe eines solchen Tages lehnt die Engine ab.
|
||||
/// </summary>
|
||||
public sealed record BudgetWarnung(string AgentId, decimal KostenProTag, decimal BudgetProTag);
|
||||
|
||||
/// <summary>Die Prognose der ganzen Instanz.</summary>
|
||||
public sealed record InstanzPrognose(
|
||||
IReadOnlyList<AufgabenPrognose> Aufgaben,
|
||||
@@ -42,7 +49,8 @@ public sealed record InstanzPrognose(
|
||||
decimal SummeProTag,
|
||||
decimal SummeProMonat,
|
||||
decimal EinmaligOffen,
|
||||
int OhneKostenbasis);
|
||||
int OhneKostenbasis,
|
||||
IReadOnlyList<BudgetWarnung> Budgetwarnungen);
|
||||
|
||||
/// <summary>
|
||||
/// Rechnet aus Terminen und bisherigen Laeufen hoch, was eine Instanz kuenftig kostet (#595).
|
||||
@@ -81,6 +89,11 @@ public static class KostenPrognose
|
||||
jetztUtc = DateTime.SpecifyKind(jetztUtc, DateTimeKind.Utc);
|
||||
var seit = jetztUtc.AddDays(-TageRueckblick);
|
||||
|
||||
// #608: Eine Ablehnung wegen erschoepften Budgets hinterlaesst einen Beleg ueber 0 USD.
|
||||
// Sie ist kein Lauf und darf den Schnitt nicht druecken.
|
||||
belegeJeAufgabe = belegeJeAufgabe.ToDictionary(
|
||||
e => e.Key, e => (IReadOnlyList<RunReceipt>)e.Value.Where(IstLauf).ToList());
|
||||
|
||||
var agentNachId = agenten
|
||||
.GroupBy(a => a.AgentId, StringComparer.OrdinalIgnoreCase)
|
||||
.ToDictionary(g => g.Key, g => g.First(), StringComparer.OrdinalIgnoreCase);
|
||||
@@ -139,9 +152,26 @@ public static class KostenPrognose
|
||||
Runde(summeProTag),
|
||||
Runde(summeProTag * TageJeMonat),
|
||||
Runde(zeilen.Where(z => z.EinmaligOffen).Sum(z => z.KostenJeLauf ?? 0m)),
|
||||
zeilen.Count(z => z.Grundlage == PrognoseGrundlage.Unbekannt && (z.LaeufeProTag > 0 || z.EinmaligOffen)));
|
||||
zeilen.Count(z => z.Grundlage == PrognoseGrundlage.Unbekannt && (z.LaeufeProTag > 0 || z.EinmaligOffen)),
|
||||
Budgetwarnungen(zeilen, agentNachId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #608: Reicht das Tagesbudget eines Agenten fuer seine geplanten Aufgaben nicht, soll das
|
||||
/// vorher auffallen - nicht erst am Abend, wenn die ersten Laeufe abgelehnt werden.
|
||||
/// Gezaehlt werden nur die Aufgaben; sonstige Laeufe lassen sich keinem Agenten sicher
|
||||
/// zuordnen und kaemen noch hinzu.
|
||||
/// </summary>
|
||||
private static List<BudgetWarnung> Budgetwarnungen(
|
||||
IEnumerable<AufgabenPrognose> zeilen, IReadOnlyDictionary<string, AgentConfig> agentNachId)
|
||||
=> zeilen
|
||||
.GroupBy(z => z.AgentId, StringComparer.OrdinalIgnoreCase)
|
||||
.Select(g => (Agent: agentNachId.GetValueOrDefault(g.Key), Summe: g.Sum(z => z.KostenProTag)))
|
||||
.Where(x => x.Agent is { Budget.DailyCostUsd: > 0 } a && x.Summe > a.Budget.DailyCostUsd)
|
||||
.Select(x => new BudgetWarnung(x.Agent!.AgentId, Runde(x.Summe), x.Agent.Budget.DailyCostUsd))
|
||||
.OrderByDescending(w => w.KostenProTag - w.BudgetProTag)
|
||||
.ToList();
|
||||
|
||||
private static AufgabenPrognose Arbeit(
|
||||
TaskItem aufgabe, string agentId, AgentConfig? agent, IReadOnlyList<RunReceipt> belege,
|
||||
IReadOnlyDictionary<string, List<RunReceipt>> bekannteJeAgent,
|
||||
@@ -215,6 +245,9 @@ public static class KostenPrognose
|
||||
return jeSchritt * AnnahmeSchritte;
|
||||
}
|
||||
|
||||
private static bool IstLauf(RunReceipt beleg)
|
||||
=> beleg.Status != nameof(AgentRunStatus.BudgetExceeded);
|
||||
|
||||
private static DateTime Utc(DateTime wert)
|
||||
=> wert.Kind == DateTimeKind.Utc ? wert : wert.ToUniversalTime();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user