Skip to content

Budget & limites

Ongrid impose un plafond de tokens global par jour UTC à travers chaque provider. Le défaut est illimité ; une variable d'env l'active :

bash
ONGRID_LLM_DAILY_TOKEN_LIMIT=2000000   # 2 million tokens per UTC day

<=0 désactive le plafond. Valeur unique, pas par provider — c'est le scope MVP. Quand les tenants atterrissent, ça passe aux settings par org ; ce bouton reste comme plafond global de filet de sécurité.

Comment c'est câblé

Trois pièces, dans internal/pkg/llm/ :

go
// 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)

Le runtime graph-kernel installe le callback handler dans sa chaîne de callbacks eino. À chaque OnStart de ChatModel :

  1. Estimer les tokens de prompt : len(text)/4 (conservateur).
  2. BudgetChecker.Check(ctx, userID, estPromptTokens).
  3. Au rejet — stocker ErrBudgetExceeded dans le contexte pour que le nœud en aval puisse court-circuiter ; le code subséquent le fait surfacer.

À OnEnd, le Usage.TotalTokens réel est enregistré contre le bucket du jour UTC courant.

ErrBudgetExceeded

go
// 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
}

L'erreur propage vers :

  • L'endpoint d'envoi de chat — renvoie HTTP 429 avec un body { "error": "budget_exceeded", "message": "..." } que l'UI de chat rend in-line.
  • Le worker investigator RCA — la ligne de rapport atterrit comme status=failed avec status_reason="budget_exceeded".
  • Le chemin translate — retombe sur « translation unavailable (budget exceeded) » et le texte original est affiché.

Caveats InMemoryBudget

L'implémentation MVP est in-memory :

go
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
}

Conséquences :

  • Pas de persistance — un redémarrage du manager reset le compteur du jour. Si vous voulez vraiment un plafond quotidien dur qui survit aux redémarrages, swappez l'implémentation. L'interface BudgetChecker est le seam.
  • Mono-processus — si vous exécutez plusieurs managers derrière un load balancer (vous ne devriez pas encore, mais bon), chacun a son propre compteur.
  • Global, pas par utilisateuruserID coule à travers l'interface pour qu'une future table MySQL usage_daily soit un drop-in, mais aujourd'hui le plafond est le même chiffre pour tout le monde.

Le pivot vers le mono-tenant a différé le backend par utilisateur ; l'interface est forward-compatible pour quand le multi-utilisateur reviendra.

Estimation de tokens

BudgetCallbackHandler.OnStart estime les tokens de prompt par compte de caractères / 4. C'est intentionnellement conservateur — la tokenisation réelle varie par provider / modèle, et le budget est censé pencher du côté de refuser les appels borderline plutôt que de dépasser.

À OnEnd, le Usage.TotalTokens réel renvoyé par le provider est enregistré — donc le budget track la vérité terrain même quand l'estimation était fausse.

Si le provider ne renvoie pas de comptes de tokens (certains endpoints custom ne le font pas), le callback retombe sur une heuristique response-meta ; voir OnEndUsesResponseMetaFallback dans les tests.

Observer le budget

bash
curl -s localhost:9100/metrics | grep llm_budget
# llm_budget_daily_limit_tokens 2000000
# llm_budget_used_tokens_today 412847
# llm_budget_rejections_total 3

Les métriques sont câblées par BudgetCallbackHandler.Stats(). Le dashboard Prom self-obs les rend comme graphe de dépense quotidienne plus une alerte à 80% du plafond.

Désactiver pour une charge de travail

Il n'y a pas de bouton « disable budget for the investigator ». Si le RCA touche le plafond et que vous préférez qu'il continue à tourner plutôt que le chat, montez le plafond — c'est à ça qu'il sert. L'alternative (quotas par workload) est parquée avec le multi-tenancy.

Voir aussi