lmjtfy.git / packages / archive / README.md
README.mdpreviewREADME.mdsource136 lines · 7.3 KB · raw
1# Chapter 9: archive, never pay for the same answer twice
2
3Here is the rule, in the owner's own words: never send the same exact shape
4to the same model, ever. If a request has been answered before (the same
5bytes, to the same model) it is not sent again, by anyone, unless someone
6presses ↻ on it (below). The kept response comes back instead. Nothing is
7spent, nothing waits on the network, and the page says "not sent" and when
8it was answered.
9
10This matters more than it sounds. Jev is calibrated and deterministic
11enough that the same question really does deserve the same answer. And the
12way people use a site like this is to drop a link in a chat, where a dozen
13people click it within a minute. Without the archive that is a dozen
14identical calls. With it, it is one.
15
16```mermaid
17sequenceDiagram
18  participant A as Visitor A
19  participant B as Visitor B
20  participant R as Archive
21  participant J as Jev
22  A->>R: this request, please
23  R->>R: kept? no. On the wire? no.
24  R->>J: send it (once)
25  B->>R: the same request, please
26  R->>R: on the wire: wait for it
27  J-->>R: the response
28  R->>R: keep it
29  R-->>A: the response (sent now)
30  R-->>B: the same response (kept)
31```
32
33The archive is a Durable Object (chapter 2), so there is exactly one of it,
34and every visitor's request goes through the same one. It does not just look
35things up: it makes the calls itself. That is what lets two visitors asking
36the same new thing at once share one call, and why a call finishes and is
37kept even if the visitor who started it closes the tab. A call that got no
38response (an error, a timeout) keeps nothing, so it can be tried again.
39
40> **Aside.** Why key on the whole request body and not a hash of it? Because
41> a hash can collide, and the rule is absolute. Two different requests can
42> never be taken for one if what is compared is the requests themselves.
43
44> **Try it.** Open
45> <https://lmjtfy.fun/?q=Is+a+slot+machine+a+good+retirement+plan%3F>,
46> scroll to the calls under the answer, and look for "not sent". Then look at
47> "So far" on the home page: how many requests were never sent because the
48> same one had been answered before.
49
50## Asking again, and every answer kept
51
52Jev is calibrated, but a model can change, and the LLM in front of it is not
53deterministic at all. So every call panel has ↻: it sends that request for
54real, and keeps the response as its newest version. Whatever comes after is
55worked out again from the new answer. A later request that comes out the same
56is served from the archive; one that changes (the LLM wrote different
57questions, say) is new, and is sent.
58
59Every response a request ever got is kept, numbered from 1. A panel with more
60than one shows ◀ answer 2 of 3 ▶, and the newest is the default. Stepping
61back is only looking: it sends nothing, and neither does anything after it.
62If an old answer leads to a request nobody has asked yet, the page says so
63and offers ↻ for it rather than sending it.
64
65```mermaid
66flowchart LR
67  P["the page's $pins: 3f2a9c01be44:2"] --> W["the Worker: what does each call want?"]
68  W -->|"pinned: Version(2)"| A["archive: version 2 of that request"]
69  W -->|"after a pinned one: KeptOnly"| B["archive: the newest kept, or NotKept"]
70  W -->|"↻: Fresh"| C["archive: send now, keep as newest"]
71  W -->|"anything else: Latest"| D["archive: the newest kept, or send"]
72```
73
74> **Aside: the page names calls by a hash, the archive does not.** A panel
75> needs a short name for its buttons, so `call_id` is twelve hex digits of the
76> SHA-256 of where the request went and what it said. The archive still looks
77> responses up by the request itself.
78
79## The feed
80
81The same store keeps the questions people asked, and the home page shows
82them: **asked lately** and **most asked**, with Jev's answer beside each and
83how many times it was asked. Each line is a link that asks it again, for
84free. The feed shows nothing about who asked. (What the site keeps for
85its owner is another matter: chapter 2,
86[What is kept about visitors](../../apps/lmjtfy/#what-is-kept-about-visitors).) A
87question goes on the list only if the rule "list it" holds (chapter 6),
88which needs Jev's `fit` fact: Jev moderates its own feed, and the owner can
89overrule it either way. An unfit question is still answered for whoever
90asked it, and still kept. Asked lately goes back as far as
91the feed does: scroll it to its end and the next fifty load. **So far** counts the questions, the
92share Jev answered with no LLM, the requests sent and not sent, and clones
93and pulls of the code.
94
95## For the people who maintain it
96
97This package is the messages between the Worker and the archive object, with
98no I/O and no clock; the object is `apps/lmjtfy/src/archive.rs`, and its
99storage is drawn in chapter 2.
100
101| Message | What |
102| --- | --- |
103| `Ask::Jev`, `Ask::Llm` | "The response to this request, please", with a `Pick`: the newest (`Latest`), sent again (`Fresh`), a `Version`, or only if kept (`KeptOnly`). Answered with a `Called`: a `Record` (the response, when it was answered, whether this ask sent it, and which version of how many), a failure, a refusal by the day's budget, or `NotKept`. |
104| `Ask::Asked` | A question was answered: its text, Jev's answers, whether an LLM was needed, whether it may be listed, and the asking browser for this question (`Asker`), which counts once. |
105| `Ask::Rate`, `Ask::Rating` | A browser's 👍 or 👎 on Jev's answer as kept (`Vote`), pressed again to take it back; the votes after it (`Rating`). |
106| `Ask::Fetched` | Someone cloned or pulled the code (`Fetch::Clone` or `Fetch::Pull`). |
107| `Ask::Home` | What the home page shows: the questions asked lately and most asked (`Entry`: the text, Jev's answers, how many times, and nothing about who asked), and the archive in numbers (`Stats`). |
108| `Ask::Older` | The next page of asked lately, after a `Cursor`: the last line shown, by when it was asked and then its text, so no line is shown twice or skipped. |
109| `Ask::Answer` | What Jev said to one question, if it was ever asked. For link previews; it sends nothing. |
110| `Ask::Event` | Something happened, to be kept whole: an `Event` (`event.rs`), one row of `events`. |
111
112`Admin` is the other set of messages, for the owner's admin backend, sent
113to the object's `/admin` path by a Worker of its own: `Select` (one
114statement that only reads, checked by `reads_only`) and `Moderate` (the
115owner's say on a question's place in the feed). `Answered` is the reply.
116
117`Live` is what the archive pushes to open pages over `/live`: `Online`,
118`Toast`, `Patch` and `Build`. `Pins` reads the page's `$pins` and says what
119each call should `Pick`; `call_id` names a call for it. `Place` and `Seen`
120are where an open page is, kept on its socket.
121
122An `Answer` is what is kept of what Jev said: the few words the page prints
123large, and the numbers behind them (`Detail`: the probability, or the
124confidence with every option or level and its probability). The feed prints
125the words; a link preview prints and draws the numbers too. Answers kept
126before the numbers were are read as words alone (`stored`). `when` writes a
127moment the way the page shows it.
128
129## In this folder
130
131| Path | What |
132| --- | --- |
133| [src/](src/) | The messages, answers and stats. |
134| [Cargo.toml](Cargo.toml) | The crate: `ask`, `budget`, `http` (for reading a request's headers), `serde`, `serde_json`, `sha2`. |
135
136← Previous: [Chapter 8½, llm/src/](../llm/src/) · Up: [packages](../) · Next: [Chapter 9½, archive/src/](src/) →