using System; using System.Diagnostics; using System.Threading; namespace PolyTrader.Core.Agents { /// Welche Budget-Dimension überschritten wurde. public enum AgentBudgetKind { Steps, Tokens, Time } /// Wird geworfen, sobald eine Budget-Dimension eines Agenten-Laufs überschritten ist. public sealed class AgentBudgetExceededException : Exception { public AgentBudgetKind Kind { get; } public AgentBudgetExceededException(AgentBudgetKind kind, string message) : base(message) { Kind = kind; } } /// /// Erzwingt ein für EINEN Agenten-Lauf (nicht wiederverwenden – pro Lauf neu /// instanziieren). Aus ClawdDotNet übernommen und um ein Zeitbudget ergänzt. Thread-safe (Interlocked), /// damit auch parallele Tool-Aufrufe sauber gezählt werden. /// public sealed class LoopGuard { private readonly AgentBudget _budget; private readonly Stopwatch _clock = Stopwatch.StartNew(); private int _steps; private int _tokens; public LoopGuard(AgentBudget budget) => _budget = budget ?? AgentBudget.Default; public int Steps => Volatile.Read(ref _steps); public int Tokens => Volatile.Read(ref _tokens); public TimeSpan Elapsed => _clock.Elapsed; /// Zählt eine Modell-Runde. Wirft bei Überschreitung der Schrittzahl oder des Zeitbudgets. public void RecordStep() { if (Interlocked.Increment(ref _steps) > _budget.MaxSteps) throw new AgentBudgetExceededException(AgentBudgetKind.Steps, $"Maximale Schrittzahl ({_budget.MaxSteps}) überschritten."); ThrowIfExpired(); } /// Addiert verbrauchte Tokens. Wirft bei Überschreitung des Token-Budgets. public void RecordTokens(int count) { if (count <= 0) return; int total = Interlocked.Add(ref _tokens, count); if (total > _budget.MaxTokens) throw new AgentBudgetExceededException(AgentBudgetKind.Tokens, $"Maximales Token-Budget ({_budget.MaxTokens}) überschritten (verbraucht: {total})."); } /// Wirft, wenn das Zeitbudget abgelaufen ist. public void ThrowIfExpired() { if (_clock.Elapsed > _budget.Timeout) throw new AgentBudgetExceededException(AgentBudgetKind.Time, $"Zeitbudget ({_budget.Timeout.TotalSeconds:0}s) überschritten."); } } }