lmjtfy.git / packages / budget / README.md
1# Chapter 10: budget, being fair to strangers with a shared wallet
2
3The site is free to use, so it is free to abuse. Somebody could sit on it
4and ask questions until the money runs out, and then nobody else could ask
5anything. So there are two kinds of limit, and this chapter is both.
6
7**Two daily budgets, shared by everyone.** One for the LLM, counted in
8Workers AI "neurons" (Cloudflare's unit of AI compute), and one for Jev, in
9dollars. They reset at 00:00 UTC. The home page shows what is left of both.
10
11| Budget | Limit | Why |
12| --- | --- | --- |
13| The LLM | 10,000 neurons | Workers AI's free daily allocation, for the whole account. |
14| Jev | `JEV_DOLLARS_PER_DAY` in `apps/lmjtfy/wrangler.toml` | Every request, the one as you type included, costs about $0.00004. Jev is the one thing here that costs money. |
15
16**A limit per visitor.** One visitor may ask 10 full questions a minute,
17and the as-you-type request may run 60 times a minute. Past that the page
18says "Slow down." and sends nothing. The count is kept in memory only, keyed
19by the visitor's address for that minute. (The address is kept, with the
20rest of each request, in the archive: chapter 2,
21[What is kept about visitors](../../apps/lmjtfy/#what-is-kept-about-visitors).) It keeps one person from emptying the day for everyone; the budgets
22remain the hard stop.
23
24> **Aside: hold, then settle.** You cannot know what a call costs until it
25> is done, but you must decide whether to make it before. So each call
26> first *holds* its worst case (the most it could possibly cost), and
27> afterwards *settles* to what it really cost. Two visitors arriving at the
28> same moment cannot both spend the last of the day, because the first hold
29> already took it.
30
31```mermaid
32sequenceDiagram
33  participant A as Archive
34  participant B as Budget object
35  participant J as Jev or the LLM
36  A->>B: hold the worst case
37  B-->>A: held (or: the day is spent)
38  A->>J: the call
39  J-->>A: the response, and what it cost
40  A->>B: settle at the real cost
41```
42
43The neuron budget belongs to the whole Cloudflare account, not just this
44site: the eval, a local dev server and anything else on the account spend
45the same 10,000. So the budget object also reads the account's own figure
46from Cloudflare's analytics API (at most every 30 seconds) and counts from
47there (`observe`). It is raised to that figure and never lowered by it,
48because the figure lags and the meter's own spending since is already on
49top. If the figure cannot be read, it counts its own calls alone and the
50page says so.
51
52The account is on the Workers Free plan (checked 2026-10-02), where
53Cloudflare refuses Workers AI calls past the allocation and bills nothing.
54The counter is what makes the page stop cleanly and say why, and what would
55hold the line if the account moved to a paid plan.
56
57> **Try it.** The home page's "Shared budget" window,
58> <https://lmjtfy.fun>, is these meters, live.
59
60## For the people who maintain it
61
62No I/O and no clock: the arithmetic, and the messages the Worker and the
63budget object (`apps/lmjtfy/src/meter.rs`) exchange. `Meter` with `hold`,
64`settle`, `observe` and `used`; `Hold` and `Exhausted`; `Which` (neurons or
65Jev dollars) and its storage `key`; `day` and `date` for UTC days; `Visits`
66and `VISIT_WINDOW_MS` for the per-visitor count; `Ask`, `Told`, `Counted`
67and `Status` for the messages.
68
69## In this folder
70
71| Path | What |
72| --- | --- |
73| [src/](src/) | The arithmetic and the messages. |
74| [Cargo.toml](Cargo.toml) | The crate. It depends on nothing but `serde`. |
75
76← Previous: [Chapter 9½, archive/src/](../archive/src/) · Up: [packages](../) · Next: [Chapter 10½, budget/src/](src/) →