elcooooo's picture
Model card, prompts and integration guide
55d6529 verified
|
Raw
History Blame Contribute Delete
13.4 kB
# ClickBook on-device model — integration guide
For the iOS and Android teams. Every number here is measured on this repo's
benchmark or its ship gate; where something is untested it says so explicitly.
**Start here:** ship `models/wide_iq4xs_e2.gguf` and `out/prompts.json`. Everything
else in this document explains those two files.
---
## 1. The two shipping assets
### `models/wide_iq4xs_e2.gguf` — 1.844 GB
| | |
|---|---|
| base | Gemma 4 E2B instruction-tuned |
| quantisation | IQ4_XS body, Q2_K token embeddings, imatrix-guided |
| vocabulary | 231,955 tokens (of 262,144; the rest are pruned scripts) |
| runtime | llama.cpp b10326 or later |
| benchmark | **79.2** on the 90-item set, 15 failures, no empty or truncated panels |
That 79.2 is measured on this exact file and is level with the 1.666 GB
eleven-language build (79.0 at the same seed, 78.4 averaged over two). **Restoring
seven scripts costs nothing on the languages already served** — a earlier
intermediate build read 1.4 marks lower, which a second seed showed to be noise.
### `out/prompts.json` — 32 KB
Every prompt, per language, with `{{word}}`, `{{sentence}}` and `{{passage}}`
placeholders, plus the required request settings and per-language token caps.
**Substitute into these strings; do not retype or paraphrase them.** The wording is
the result of seven measured revisions and small changes have moved benchmark
scores by double digits. Regenerate the file after any prompt change:
```bash
BENCH_PROMPTS=v7 npx tsx scripts/export-prompts.ts > out/prompts.json
```
### Other builds, same recipe, if the size budget changes
| file | size | scripts |
|---|---:|---|
| `iq4xs_e2.gguf` | 1.666 GB | Latin, Cyrillic, Arabic |
| `cjkkrhi_iq4xs_e2.gguf` | 1.815 GB | + CJK, Hangul, Devanagari |
| **`wide_iq4xs_e2.gguf`** | **1.844 GB** | **+ Tamil, Thai, Greek, Hebrew** |
| `allscripts_iq4xs_e2.gguf` | 1.950 GB | every script — over a 1.9 GB cap |
Vocabulary costs **3.49 KB per token**: Gemma 4 keeps per-layer embeddings, so a
token carries 10,496 parameters. Script coverage is therefore expensive, and the
shipped subset is deliberate. `scripts/build-script-variant.sh` builds any other
combination.
---
## 2. Language support
`out/prompts.json` carries `book`, `reader` and `evidence` for every language.
Honour those flags — they are ship gates, not preferences.
| language | book | reader | evidence |
|---|:--:|:--:|---|
| English, German, Arabic | ✅ | ✅ | benchmark, 90 items × 2 seeds |
| French, Portuguese, Spanish, Italian, Russian, Dutch, Polish, Turkish | ✅ | — | benchmark, 183 items each on an older build: 69–86 |
| Chinese, Japanese, Korean, Hindi, Thai, Hebrew | ✅ | ✅ | ship gate: 4/4 tabs pass |
| **Tamil** | ✅ | ❌ | ship gate: CONTEXT fails |
| **Greek** | ❌ | ❌ | ship gate: output corrupted |
Reader-language support for French through Turkish needs TRANSLATION and CONTEXT
templates, which do not exist yet. Those languages can be read *from*, not
answered *in*.
### Greek — do not enable
Output mixes scripts and invents words:
```
"η σκέψη του μυаλу" Cyrillic а and у inside a Greek word
"που άμεσως führte" a German verb
"για να β Olympic" nonsense
```
Only 1,511 Greek tokens were retained, and that is not enough. Leaving them in the
file is harmless; just never offer Greek as a book or reader language.
### Tamil — book language only
MEANING, EXAMPLE and TRANSLATION pass. CONTEXT does not: translating a passage
*into* Tamil produced `அவர்店の앞 அதிக நேரம்…` — Han, Kana and Hangul spliced into
Tamil. A Tamil speaker can read a Tamil book with English, German or Arabic
answers; do not offer Tamil as the answer language.
### The six smoke-tested languages
Each was probed with taps where the obvious reading is wrong. All six read them
correctly:
| language | tap | got |
|---|---|---|
| Chinese | 不是个东西 | a scornful insult, not "a thing" |
| Japanese | 手が空いた | became free, not "hand is empty" |
| Korean | 발이 넓다 | well connected, not "wide feet" |
| Hindi | हाथ धोना | to lose something, not "wash hands" |
| Thai | ใจดี | kind-hearted |
| Hebrew | יד חמה | a knack for it, not "warm hand" |
**Two taps per language is not a benchmark.** English, German and Arabic have 90
items across two seeds; these have a handful of sentences. Treat them as beta and
expect to find failures the gate does not cover.
---
## 3. Request configuration
```jsonc
{
"n_ctx": 2048, // 555 suffices for one tap and is faster
"n_threads": 6,
"n_gpu_layers": 99, // 0 on CPU-only devices
"temperature": 0.1,
"top_k": 40,
"top_p": 0.9,
"repeat_penalty": 1.05,
"seed": 42,
"chat_template_kwargs": { "enable_thinking": false } // REQUIRED
}
```
### `enable_thinking: false` is not optional
Without it the model reasons before answering, spends the whole budget in
`reasoning_content`, and returns **empty `content` with `finish_reason: "length"`**.
In the UI that is indistinguishable from an unsupported language or a broken
model. It has cost this project three separate debugging sessions, including one
where nine languages appeared to fail and none of them had.
If you ever enable thinking deliberately, raise every cap below to 1000 first.
### Token caps
Defaults, and the per-language values in `prompts.json` under `caps.perLanguage`:
| tab | default | ko / th | hi | ta | he |
|---|---:|---:|---:|---:|---:|
| MEANING | 111 | 136 | 144 | 160 | 192 |
| EXAMPLE | 222 | 272 | 288 | 320 | 384 |
| TRANSLATION | 111 | 136 | 144 | 160 | 192 |
| CONTEXT | 444 | 544 | 576 | 640 | 768 |
Chinese and Japanese use the defaults — measured, they cost the same tokens as
English for equivalent content (`scripts/token-cost.ts`). Hebrew is the most
expensive script at 1.67×.
Verified headroom at these caps: the longest MEANING answer across all seven new
languages was 25 of 112 tokens, and no tab in any language came within 10% of its
cap. Nothing truncates.
### Performance
12.1 s per popup on the device configuration (CPU, ctx 555, all four tabs);
roughly 1.7 s per tab with GPU offload.
---
## 4. How a tap becomes four answers
| tab | prompt & answer language | asks for |
|---|---|---|
| MEANING | **book** | define the tapped word in the sense this sentence gives it |
| EXAMPLE | **book** | three new short sentences using that same sense |
| TRANSLATION | **reader** | what the word means, answered in the reader's language |
| CONTEXT | **reader** | translate the whole passage |
Two rules that carry measured weight:
- **`{{sentence}}` must come from the tap's character offsets**, not from splitting
the passage on punctuation. When the same word appears twice in a passage, the
wrong sentence produces a confidently wrong answer.
- **`{{passage}}`** is the whole block, or roughly 520 characters centred on the
tap when the block is longer.
The idiom clause inside each prompt — "if the word is part of an idiom, explain the
whole phrase instead" — is what makes idiom taps work. Do not trim it for brevity.
---
## 5. Verifying a build before release
```bash
npx tsx scripts/newlang-verify.ts models/wide_iq4xs_e2.gguf
```
Exits non-zero if any language fails. It checks, per tab: non-empty output, no
foreign-script contamination, inside the token cap with headroom, and that EXAMPLE
returns three unnumbered lines. Current state: **27 of 28 checks pass**, the
exception being Tamil CONTEXT, which is why Tamil is book-only.
To read the answers rather than assert on them:
```bash
npx tsx scripts/newlang-probe.ts models/wide_iq4xs_e2.gguf zh,ja,ko
```
For the eleven benchmarked languages:
```bash
SENSE10_MODEL=models/wide_iq4xs_e2.gguf bash scripts/full90.sh myarm
```
---
## 6. What is not in this build
There is **no on-device dictionary**. A purpose-built sense resource measured
+12.7 marks (79.0 → 91.7 on the 90-item benchmark) but needs a generation run that
has not been funded; the version buildable from free public lexicons measured
**+0.7** and is not worth its 82.5 MB. Format and integration points are in
`docs/sense10.md`. `wide_iq4xs_e2.gguf` leaves 56 MB under a 1.9 GB cap if one
ships later.
Known open issues, both reproducible with the ship gate:
- Tamil CONTEXT script contamination (§2).
- EXAMPLE occasionally numbers its sentences despite the prompt forbidding it.
Strip a leading `1.` / `-` / `•` client-side.
---
## 7. Packaging and delivery
The model is delivered by the stores themselves — Play Asset Delivery on Android,
Background Assets on iOS. Both host and serve it free, both resume interrupted
downloads, and both tie the asset to an app version, which matters here because
`prompts.json` describes gates and caps for **this** model file. A plain CDN URL
lets those drift apart; the store mechanisms do not.
### Integrity
```
file ClickBook-Gemma-4-E2B-multi-IQ4_XS.gguf
bytes 1,844,170,944
sha256 0ad915db36b04976d4a217e264d5a46c2d08fb77632875795d7ac080b8d5f6fe
```
Verify after assembly, before first load. A truncated or mis-ordered file will
often still load in llama.cpp and produce fluent nonsense — the failure does not
announce itself.
### Android — Play Asset Delivery, two packs
The per-pack limit is **1.5 GB** and this file is 1.844 GB, so it must be split.
Note the limit applies to *compressed download size*, which does not help: a GGUF
is quantised weights and compresses by only 3% (ratio 0.970, measured), so
compressed size is effectively file size.
**Every variant of this model exceeds 1.5 GB**, including the 1.666 GB
eleven-language build, so splitting is unavoidable and model size is therefore not
a packaging consideration when choosing which build to ship.
Use **fast-follow** delivery: the app opens immediately and the model arrives in
the background behind a progress UI you control. Two packs of 922.1 MB, split at
the exact byte midpoint and concatenated in order:
```bash
split -b 922085472 ClickBook-Gemma-4-E2B-multi-IQ4_XS.gguf model.part
# model.partaa bytes 0..922,085,471
# sha256 d98cccc0847c9f2200d2eeba1f0d928d70f945528d08c52abfcd1231c7a003b1
# model.partab bytes 922,085,472..1,844,170,943
# sha256 d09e50a639d5f3d28eae17d9708217f9fedfb240063ec3dade5bcf6b46b43393
```
Two packs sit far inside the 30 GB cumulative fast-follow allowance, so there is
no second limit to design around.
### iOS — Background Assets, one pack
**On-Demand Resources is deprecated as of iOS 27.** Build on **Background Assets**
with Apple-hosted asset packs, which allow 200 GB across up to 200 packs.
The per-pack limit on **iOS 18+ is 8 GB**, so the model ships as a **single
1.844 GB pack** with no splitting and no reassembly. On iOS 17 and earlier the
limit is 512 MB per tag, which would mean four parts — if the minimum deployment
target is below iOS 18, raise it or accept the extra client complexity.
### Consequence: the two platforms need different packaging
Android reassembles two parts; iOS receives one file. **Do not design one chunking
scheme for both.** Keep the split and concatenation logic on the Android side
only, and let iOS treat the model as an ordinary single asset.
| | limit per pack | parts for 1.844 GB |
|---|---:|---:|
| Android, Play Asset Delivery | 1.5 GB | **2** |
| iOS 18+, Background Assets | 8 GB | **1** |
| iOS ≤ 17, On-Demand Resources | 512 MB | 4 (and deprecated) |
---
## 8. Release checklist
Everything below has been run against the assets in this repo. Re-run after any
change to the model, the prompts or the caps.
| # | check | command | current |
|---|---|---|---|
| 1 | model is the expected size | `ls -l models/wide_iq4xs_e2.gguf` | 1.844 GB ✅ |
| 2 | no regression on the benchmarked languages | `SENSE10_MODEL=models/wide_iq4xs_e2.gguf bash scripts/full90.sh rel` | 79.2 vs 79.0 ✅ |
| 3 | new languages pass the ship gate | `npx tsx scripts/newlang-verify.ts models/wide_iq4xs_e2.gguf` | 27/28 ✅ (Tamil CONTEXT known) |
| 4 | prompts export is current and at v7 | `BENCH_PROMPTS=v7 npx tsx scripts/export-prompts.ts > out/prompts.json` | 32 KB, v7 ✅ |
| 5 | client sends `enable_thinking: false` | inspect one request | **verify per platform** |
| 6 | client honours `book` / `reader` flags | Greek hidden, Tamil not offered as answer language | **verify per platform** |
| 7 | client derives `{{sentence}}` from tap offsets | not from punctuation splitting | **verify per platform** |
| 8 | assembled model matches the checksum | `sha256sum` after concatenation | **verify per platform** |
Rows 5–8 cannot be checked from this repo and are the ones most likely to be got
wrong. Two of them fail silently: row 5 looks like a model defect, and row 8 —
a mis-assembled model — loads without complaint and generates fluent nonsense.
### Reproducing every number in this document
```bash
bash scripts/full90.sh <arm> # 90-item benchmark
npx tsx scripts/newlang-verify.ts <model.gguf> # ship gate, exits non-zero
npx tsx scripts/newlang-probe.ts <model.gguf> zh,ja,ko # read the answers
npx tsx scripts/token-cost.ts <model.gguf> # token cost per script
bash scripts/build-script-variant.sh <name> <Scripts,Comma> # a different script set
```