Assistant Cost Control¶
Every question put to the ubAI assistant costs money: one turn is a planning step plus however many agents the planner sends, and each of those is a paid call to your language-model provider. Cost control gives each user a monthly budget. The assistant records what every call cost, and once a user has used their budget it declines to start a new turn until the next month.
It is switched on and read entirely in the admin application, under
LLM budgets (route /llm-budgets). Nothing needs to be configured in a
file, and no environment variable is involved.
Note
Usage is recorded from the moment the assistant is running, whether or not you enforce a budget — but the token-count and character-estimate figures need a price table. Until you save prices, every call is recorded at zero and every amount on the page reads 0.00 — except where the provider reports the cost of a call itself (OpenRouter only): those calls are recorded in money even with no price table (see Where provider-reported cost comes from). Recorded calls are never repriced afterwards. So to watch real figures for a month before enforcing anything, save your prices with the enforcement mode set to Observe — not by leaving the page unconfigured.
What a budget is¶
A budget is an amount of money per user per month, in one currency you choose.
The default budget applies to everyone who has no override.
An override applies to one user, and replaces the default for them.
The currency is a label only. No conversion is ever performed, in either direction — enter every rate and every budget in the same currency.
The period is the calendar month in UTC — not your server’s time zone and not your browser’s. At the start of each UTC month every user starts again from zero. There is no proration, no carry-over from an unused month, and no per-day sub-limit.
A budget is a soft ceiling¶
Important
The budget is checked when a turn starts, never while it runs. A turn already under way always finishes. Enforcement is admission-only and there is no per-user limit on how many turns may run at once, so the real ceiling is the budget plus the turns a user can still START while under it: the per-user request rate limit (20 turn starts per minute) bounds how many can be admitted, and each admitted turn can cost up to its effort-capped maximum. A user sitting at 99% of their budget can therefore end well above it — not merely by one turn.
A single thorough turn is many model calls, so leave room for that overshoot rather than entering the number you never want exceeded. This is deliberate — interrupting an answer halfway through wastes everything already paid for and leaves the user with nothing.
Switching it on¶
Open the admin application and go to LLM budgets.
Set Enforcement mode:
Observe — spend is recorded and reported, and no request is ever declined.
Enforce — a user whose recorded spend has reached their budget is declined a new turn.
Set the Currency, the Default monthly budget, and the rates your provider charges (see Prices). The default budget must be non-zero to switch on Enforce: a default of 0 is already reached by everyone, so enforcing it would lock out every user without an individual override. (An individual override of 0 is still possible — it is a deliberate per-user refusal, decided by the presence of the override itself.)
Save.
Note
Until you have saved these settings, nothing is enforced. A deployment that has just been upgraded has no prices and no budget, and enforcing a budget nobody has set would lock every user out of the assistant. Usage keeps being recorded throughout — but with no prices the token-count and estimate figures cannot be costed, so every amount reads 0.00 unless the provider reported the call’s cost itself (OpenRouter only — those calls carry a real money figure even with no price table), and calls are not repriced when you save prices later. If you want figures for the period you spend deciding, save the prices now and leave the mode on Observe. The same applies if the saved settings ever become unreadable: the assistant keeps recording and declines nobody.
Setting an override for one user¶
On the same page, add an override with the user and an amount. An override replaces the default for that user completely — including an override of zero, which means “this user may spend nothing” and is treated as a decision you made, not as a missing value. Remove the override to return the user to the default. A note field is available for recording why.
Prices¶
You enter the prices. ubTrace never fetches a price list from your provider or from anywhere else, on any deployment, connected or air-gapped. A price table that is sometimes fetched and sometimes typed in has two sources of truth that can disagree without anyone noticing, and the result is a believable figure that bills wrongly forever.
The consequence is worth stating plainly: prices go out of date silently. When your provider changes its rates, or you point the assistant at a different model, update the rates on this page. Nothing will warn you.
Rates are given per million tokens, which is how providers publish them, and there are four:
Rate |
Applies to |
|---|---|
Input |
Tokens sent to the model that were not served from its cache. |
Cached input |
Tokens the provider served from its own prompt cache, usually much cheaper. |
Cache write |
Tokens the provider stored into that cache, usually slightly dearer than input. |
Output |
Tokens the model produced. |
Enter a default set of rates, and optionally one set per model name. The default is not optional: some model identifiers never appear in configuration (a provider preset, for example, routes to a model under a different name), and those calls are priced at the default.
How cost is calculated¶
Every call is priced once, by whichever of three methods can produce a figure first. Each recorded call remembers which one was used, so you can always tell how firm a number is. The spend table shows the mix.
Method |
When it is used |
|---|---|
Provider-reported |
The provider returned the cost of the call itself. This is the only ground truth in the system, and it lets you check the method below against reality. Only OpenRouter reports it — see Where provider-reported cost comes from. This method needs no price table: it is recorded even before you have saved one. |
Token counts |
The token counts the provider reported, multiplied by your rates. Every provider reports token counts, so in practice this prices almost every call. |
Character estimate |
Used only when a provider reports no token counts at all. See Estimated records. |
From token counts, the calculation is:
base input = input tokens − cached input tokens − cache-write tokens
cost = base input × input rate
+ cached input tokens × cached-input rate
+ cache-write tokens × cache-write rate
+ output tokens × output rate
Two things about that are easy to get wrong, so they are worth reading twice:
Cache tokens are counted inside the provider’s input total, not alongside it. They are therefore subtracted from it before the input rate applies. Without that subtraction a cached call would be charged twice for the same tokens — once at the input rate and again at the cache rate.
Reasoning tokens are counted inside the output total. They are recorded so you can see them, and never priced separately, so a model that thinks before it answers is not charged twice for its thinking either.
Estimated records¶
When a provider reports no token counts, tokens are estimated by dividing the number of characters by the estimation divisor (default 3), in both directions — the prompt and the answer.
An estimate cannot know what the provider served from its cache, so an estimated record prices its whole input at the input rate, the dearest of the three input rates. Estimated records therefore err toward over-charging rather than under-charging, which is the safe direction for a spend ceiling. A smaller divisor estimates more tokens and so charges more.
A turn you stop is still charged for what the model had already produced. Those tokens were generated, and your provider bills for them, so they are recorded — usually as an estimate, because a provider reports its token counts only when a call finishes. A call that failed before the model produced anything is not charged.
Every estimated call is marked as estimated and written to the log. The spend table has an Estimated column, so you can always see how much of a figure is a guess before you act on it. That column is a share of the amount, not of the number of calls: estimated calls are the ones a provider reported nothing for, which tend to be the long ones, so a few of them can account for most of the money.
Where provider-reported cost comes from¶
Important
Provider-reported cost is available on OpenRouter only. No other provider reports what a call cost.
ubTrace asks OpenRouter to report the cost of every call, on every request, and reads that figure back from the provider’s own usage report. There is no setting to turn it on and none that turns it off: if the assistant is configured against OpenRouter, calls are priced from the provider’s own figure. Unlike the other two methods, this one needs no price table: a deployment that has saved none still records OpenRouter calls in money.
On every other provider the price table is used instead, so a cost is never missing — only less authoritative. If your figures move after an upgrade, that is what a recorded call’s method column is for: it tells you which of the three methods produced each number.
What a user sees when the budget is used up¶
In Enforce mode, a user whose recorded spend has reached their budget gets a message instead of an answer. It says that the budget for this period is used up, that the request was declined before the assistant ran, that the budget resets at the start of next month, and that an administrator can raise it. There is no Retry button, because asking the same question again cannot succeed.
The amounts are not shown to the user. They are on the admin page, and the declined turn is written to the copilot log with the user, the spend and the budget.
Reading the spend table¶
The table lists one row per user with an override or with spend in the period:
Column |
Meaning |
|---|---|
Spent |
Everything recorded for that user in this period. |
Budget |
Their override if they have one, otherwise the default. |
Used |
Spent as a percentage of budget. It can exceed 100% — see A budget is a soft ceiling. |
Estimated |
How much of the amount in Spent came from character estimates — a share of the money, not of the number of calls. Reads Not priced while no prices are saved, because nothing has been costed yet. |
Tier mix |
How many of the user’s calls were priced by each of the three methods — counts, where Estimated is money. |
The list is paginated, because it grows with the number of people using the assistant.
What cost control does not do¶
It does not stop a turn that has already started, and it does not limit how many turns a user may run at once. Enforcement is admission-only, so a budget can be exceeded by more than one turn: the per-user request rate limit (20 turn starts per minute) is the only bound on how many turns can be admitted while under the budget.
There is no organization-wide or team-wide cap — budgets are per user.
There is no currency conversion.
There is no proration, no carry-over and no per-day sub-limit.
Prices are never updated for you.
Only the assistant’s model calls are counted. Nothing else in ubTrace calls a language model.