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.");
}
}
}