Budget & limites
O Ongrid impõe um cap global por dia-UTC de tokens em todo provider. O padrão é ilimitado; uma única env var o liga:
ONGRID_LLM_DAILY_TOKEN_LIMIT=2000000 # 2 milhões de tokens por dia UTC<=0 desabilita o cap. Valor único, não por-provider — é o escopo do MVP. Quando tenants pousarem, vai para settings por-org; este knob fica como cap global de rede de segurança.
Como está conectado
Três peças, em internal/pkg/llm/:
// 1. A interface
type BudgetChecker interface {
Check(ctx context.Context, userID uint64, estPromptTokens int) error
Record(ctx context.Context, userID uint64, usage Usage) error
}
// 2. A implementação MVP
budget := llm.NewInMemoryBudget(cfg.LLM.DailyTokenLimit)
// 3. O callback eino que faz ponte com o graph kernel
handler := llm.NewBudgetCallbackHandler(budget, userID)O runtime do graph-kernel instala o callback handler em sua cadeia de callbacks eino. Em cada OnStart de ChatModel:
- Estima tokens de prompt:
len(text)/4(conservador). BudgetChecker.Check(ctx, userID, estPromptTokens).- Em rejeição — armazena
ErrBudgetExceededno contexto para que o nó downstream possa fazer short-circuit; código subsequente o expõe.
Em OnEnd, o Usage.TotalTokens real é registrado contra o bucket atual do dia UTC.
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
}O erro propaga para:
- O endpoint de envio de chat — retorna HTTP 429 com um body
{ "error": "budget_exceeded", "message": "..." }que a UI de chat renderiza inline. - O worker investigator do RCA — a linha do report pousa como
status=failedcomstatus_reason="budget_exceeded". - O caminho de translate — cai para "translation unavailable (budget exceeded)" e o texto original é mostrado.
Caveats do InMemoryBudget
A implementação MVP é in-memory:
type InMemoryBudget struct {
mu sync.Mutex
dailyLimit int // tokens por dia UTC; <=0 significa ilimitado
used map[string]int // key = "YYYY-MM-DD" (UTC)
now func() time.Time
}Consequências:
- Sem persistência — um restart do manager reseta o counter do dia. Se você de fato quer um cap diário hard que sobreviva a restarts, troque a implementação. A interface
BudgetCheckeré a costura. - Single-process — se você rodar múltiplos managers atrás de um load balancer (não deveria ainda, mas se), cada um tem seu próprio counter.
- Global, não por-user —
userIDflui pela interface para que uma futura tabela MySQLusage_dailyseja um drop-in, mas hoje o cap é o mesmo número para todo mundo.
O pivô para single-tenant adiou o backend por-user; a interface é forward-compatible para quando multi-user voltar.
Estimativa de tokens
BudgetCallbackHandler.OnStart estima tokens de prompt por contagem de caracteres / 4. Isso é intencionalmente conservador — tokenização real varia por provider / modelo, e o budget deve errar do lado de recusar chamadas borderline em vez de passar.
Em OnEnd, o Usage.TotalTokens real retornado pelo provider é registrado — então o budget rastreia ground truth mesmo quando a estimativa estava errada.
Se o provider não retorna contagem de tokens (alguns endpoints custom não), o callback cai para uma heurística de response-meta; veja OnEndUsesResponseMetaFallback nos testes.
Observando o budget
curl -s localhost:9100/metrics | grep llm_budget
# llm_budget_daily_limit_tokens 2000000
# llm_budget_used_tokens_today 412847
# llm_budget_rejections_total 3As métricas estão conectadas por BudgetCallbackHandler.Stats(). O dashboard Prom de self-obs as renderiza como um gráfico de spend diário mais um alerta em 80% do cap.
Desabilitando para um workload
Não há knob "desabilita budget para o investigator". Se o RCA está batendo no cap e você prefere que continue rodando ao invés do chat, suba o cap — é para isso que está ali. A alternativa (quotas por-workload) está parqueada junto com multi-tenancy.
Veja também
- Visão geral dos modelos.
- Roteamento — ortogonal ao budget; o cap se aplica por-call independente de qual provider foi escolhido.
- Variáveis de ambiente — knobs
ONGRID_LLM_*.