Skip to the content.

Context

Some LLM-backed features are triggered directly by a user’s action and bill a shared provider account the instant the user acts. Consumption is therefore driven by user behaviour, unbounded from the system’s side, and can be extremely uneven across users.

Example

Several of Zeeguu’s LLM actions are triggered directly by a user’s click and charge a shared provider account the instant the user acts: on-demand “Ask AI” translation, on-demand article simplification, and audio-lesson generation (LLM script + text-to-speech). Because the spend is attached to individual user behaviour, a single enthusiastic user (or a buggy client, or a script) can run up the bill or exhaust shared rate limits for everyone.

Of all the above, the one that Zeeguu protects against is multiple lesson generations in parallel, because this is the most expensive operation. Audio-lesson generation allows only one active generation per user at a time: AudioLessonGenerationProgress.create_for_user deletes any existing record and find_active_for_user gates new requests. That is a per-user concurrency budget of exactly one, the cheapest possible bound, chosen precisely because the audio pipeline is among the most expensive actions in the system. On top of that, each user can generate only one audio lesson per day, a second, count-based budget.

The general pattern extends this idea to any LLM-backed resource: cap each user’s consumption over a time window, denominated in whatever proxy is cheap to measure and good enough: number of on-demand translations per day, characters simplified per week, generations per hour, or, at the precise end, actual token cost.

Problem

How can one user, or a buggy client, be stopped from running up the shared bill or exhausting shared rate limits for everyone?

Forces

Solution

Give each user a budget on an LLM-backed resource and refuse or degrade once it is exhausted. Denominate the budget in the coarsest proxy that adequately tracks the risk:

When a budget is exhausted, degrade rather than fail where possible: fall back to the cheaper non-LLM path (serve the Google Translate result and simply stop escalating to the LLM; see Escalate to the LLM), queue the request, or show a friendly “try again later.”

The precise variant, per-user cost attribution. At the accurate end, meter every LLM call as (user, feature, model, provider, input_tokens, output_tokens, timestamp), store tokens (provider-neutral) and derive cost through a central price table, and aggregate per user. This is the most accurate budget denomination and doubles as observability: it answers which users and which features drive the bill. It also composes with Centralized Model Selection (price table keyed by the same model constants) and LLM Output Provenance (the per-artifact provenance record is the natural place to also stamp token counts: provenance says how was it made, this adds what did it cost, and to whom). Prefer a coarse proxy unless this precision is genuinely needed.

Consequences

Relationship to LLM Gateways

Gateways provide per-key / per-user rate limiting and spend caps out of the box (LiteLLM virtual-key budgets and RPM/TPM limits, Portkey, Cloudflare AI Gateway), so both the coarse and the cost-based variants can be partly delegated, but only if the application tags each call with a user id (and, for per-feature budgets, with the feature). As with metering generally (see What Makes These Patterns LLM-Specific? → Relationship to LLM Gateways), the gateway supplies the enforcement primitive (count, throttle, cap); the pattern owns the policy: which resource, which window, and crucially what the user gets when the budget is exhausted (degrade to Google Translate vs. hard refuse), which is a product decision no gateway can make.

Status

A crude instance (single active audio generation per user) already ships. The general per-user budget across on-demand actions, and the cost-accounting variant, are not yet implemented, included as a candidate to invite discussion on whether this is one pattern with several denominations (concurrency / count / volume / cost) or several distinct patterns.

Known Uses

Notes


💬 Open an issue about this pattern