Budget & Limits
Ongrid erzwingt einen globalen Per-UTC-Tag-Token-Cap über jeden Provider. Der Default ist unbegrenzt; eine Umgebungsvariable schaltet ihn ein:
ONGRID_LLM_DAILY_TOKEN_LIMIT=2000000 # 2 million tokens per UTC day<=0 deaktiviert den Cap. Einzelner Wert, nicht pro Provider — das ist der MVP-Scope. Wenn Tenants landen, wandert er zu Per-Org-Settings; dieser Knopf bleibt als Safety-Net-globaler Cap.
Wie er verdrahtet ist
Drei Teile, in internal/pkg/llm/:
// 1. The interface
type BudgetChecker interface {
Check(ctx context.Context, userID uint64, estPromptTokens int) error
Record(ctx context.Context, userID uint64, usage Usage) error
}
// 2. The MVP implementation
budget := llm.NewInMemoryBudget(cfg.LLM.DailyTokenLimit)
// 3. The eino callback that bridges to the graph kernel
handler := llm.NewBudgetCallbackHandler(budget, userID)Die Graph-Kernel-Runtime installiert den Callback-Handler in ihre eino-Callback-Chain. Bei jedem ChatModel OnStart:
- Prompt-Tokens schätzen:
len(text)/4(konservativ). BudgetChecker.Check(ctx, userID, estPromptTokens).- Bei Ablehnung — speichere
ErrBudgetExceededim Kontext, sodass der Downstream-Knoten kurzschließen kann; nachfolgender Code zeigt es an.
Bei OnEnd wird das tatsächliche Usage.TotalTokens gegen den aktuellen UTC-Tag-Bucket erfasst.
ErrBudgetExceeded
// internal/pkg/llm/budget.go:37
func (b *InMemoryBudget) Check(ctx context.Context, userID uint64, estPromptTokens int) error {
if b.dailyLimit <= 0 {
return nil
}
b.mu.Lock()
defer b.mu.Unlock()
key := b.dayKey()
if b.used[key]+estPromptTokens > b.dailyLimit {
return ErrBudgetExceeded
}
return nil
}Der Fehler propagiert zu:
- Dem Chat-Send-Endpunkt — gibt HTTP 429 mit einem
{ "error": "budget_exceeded", "message": "..." }-Body zurück, den die Chat-UI in-line rendert. - Dem RCA-Investigator-Worker — die Report-Zeile landet als
status=failedmitstatus_reason="budget_exceeded". - Dem Translate-Pfad — fällt auf „Übersetzung nicht verfügbar (Budget überschritten)" zurück und der Originaltext wird gezeigt.
InMemoryBudget-Vorbehalte
Die MVP-Implementierung ist In-Memory:
type InMemoryBudget struct {
mu sync.Mutex
dailyLimit int // tokens per UTC day; <=0 means unlimited
used map[string]int // key = "YYYY-MM-DD" (UTC)
now func() time.Time
}Konsequenzen:
- Keine Persistenz — ein Manager-Restart setzt den Tageszähler zurück. Wenn Sie tatsächlich einen harten Tages-Cap wollen, der Neustarts überlebt, tauschen Sie die Implementierung. Das
BudgetChecker-Interface ist die Naht. - Single-Process — wenn Sie mehrere Manager hinter einem Loadbalancer betreiben (sollten Sie noch nicht, aber falls), hat jeder seinen eigenen Zähler.
- Global, nicht pro Benutzer —
userIDfließt durch das Interface, sodass eine zukünftige MySQL-usage_daily-Tabelle ein Drop-in ist, aber heute ist der Cap dieselbe Zahl für jeden.
Der Pivot zu Single-Tenant verschob das Per-User-Backend; das Interface ist forward-kompatibel, wenn Multi-User zurückkommt.
Token-Schätzung
BudgetCallbackHandler.OnStart schätzt Prompt-Tokens durch Zeichenzahl / 4. Das ist absichtlich konservativ — echte Tokenisierung variiert per Provider / Modell, und das Budget soll lieber auf die Seite ablehnender Grenzaufrufe als drüberzugehen.
Bei OnEnd wird das tatsächliche vom Provider zurückgegebene Usage.TotalTokens erfasst — sodass das Budget die Ground Truth trackt, auch wenn die Schätzung daneben lag.
Wenn der Provider keine Token-Zählung zurückgibt (manche Custom-Endpunkte tun das nicht), fällt der Callback auf eine Response-Meta-Heuristik zurück; siehe OnEndUsesResponseMetaFallback in den Tests.
Das Budget beobachten
curl -s localhost:9100/metrics | grep llm_budget
# llm_budget_daily_limit_tokens 2000000
# llm_budget_used_tokens_today 412847
# llm_budget_rejections_total 3Die Metriken sind durch BudgetCallbackHandler.Stats() verdrahtet. Das Self-Obs-Prom-Dashboard rendert sie als Tages-Ausgaben-Graph plus einen Alarm bei 80% des Caps.
Deaktivierung für eine Workload
Es gibt keinen „Budget für den Investigator deaktivieren"-Knopf. Wenn RCA den Cap trifft und Sie lieber wollten, dass es weiterläuft als Chat, erhöhen Sie den Cap — dafür ist er da. Die Alternative (Per-Workload-Kontingente) ist zusammen mit Multi-Tenancy geparkt.
Siehe auch
- Modelle-Übersicht.
- Routing — orthogonal zum Budget; der Cap gilt pro Aufruf, unabhängig davon, welcher Provider gewählt wurde.
- Umgebungsvariablen —
ONGRID_LLM_*-Knöpfe.