dejavuu 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. dejavuu-0.1.0/CHANGELOG.md +60 -0
  2. dejavuu-0.1.0/CONTRIBUTING.md +63 -0
  3. dejavuu-0.1.0/LICENSE +21 -0
  4. dejavuu-0.1.0/PKG-INFO +317 -0
  5. dejavuu-0.1.0/README.md +273 -0
  6. dejavuu-0.1.0/pyproject.toml +158 -0
  7. dejavuu-0.1.0/src/dejavuu/__init__.py +10 -0
  8. dejavuu-0.1.0/src/dejavuu/__main__.py +7 -0
  9. dejavuu-0.1.0/src/dejavuu/api.py +184 -0
  10. dejavuu-0.1.0/src/dejavuu/cli.py +86 -0
  11. dejavuu-0.1.0/src/dejavuu/config.py +59 -0
  12. dejavuu-0.1.0/src/dejavuu/core/__init__.py +16 -0
  13. dejavuu-0.1.0/src/dejavuu/core/engine.py +146 -0
  14. dejavuu-0.1.0/src/dejavuu/core/sampling.py +61 -0
  15. dejavuu-0.1.0/src/dejavuu/core/tree.py +85 -0
  16. dejavuu-0.1.0/src/dejavuu/core/verifier.py +92 -0
  17. dejavuu-0.1.0/src/dejavuu/decoders/__init__.py +8 -0
  18. dejavuu-0.1.0/src/dejavuu/decoders/hf.py +136 -0
  19. dejavuu-0.1.0/src/dejavuu/decoders/ort.py +180 -0
  20. dejavuu-0.1.0/src/dejavuu/decoders/text.py +64 -0
  21. dejavuu-0.1.0/src/dejavuu/decoders/vlm.py +143 -0
  22. dejavuu-0.1.0/src/dejavuu/drafters/__init__.py +109 -0
  23. dejavuu-0.1.0/src/dejavuu/drafters/adapld.py +117 -0
  24. dejavuu-0.1.0/src/dejavuu/drafters/anpd.py +54 -0
  25. dejavuu-0.1.0/src/dejavuu/drafters/asam.py +172 -0
  26. dejavuu-0.1.0/src/dejavuu/drafters/base.py +113 -0
  27. dejavuu-0.1.0/src/dejavuu/drafters/lookahead.py +61 -0
  28. dejavuu-0.1.0/src/dejavuu/drafters/pld_plus.py +98 -0
  29. dejavuu-0.1.0/src/dejavuu/drafters/prompt_lookup.py +50 -0
  30. dejavuu-0.1.0/src/dejavuu/drafters/rest.py +47 -0
  31. dejavuu-0.1.0/src/dejavuu/drafters/sam_decoding.py +58 -0
  32. dejavuu-0.1.0/src/dejavuu/drafters/suffix_decoding.py +41 -0
  33. dejavuu-0.1.0/src/dejavuu/drafters/suffix_index.py +118 -0
  34. dejavuu-0.1.0/src/dejavuu/drafters/token_recycling.py +70 -0
  35. dejavuu-0.1.0/src/dejavuu/eval/__init__.py +2 -0
  36. dejavuu-0.1.0/src/dejavuu/eval/harness.py +270 -0
  37. dejavuu-0.1.0/src/dejavuu/eval/mmspec.py +233 -0
  38. dejavuu-0.1.0/src/dejavuu/eval/specbench.py +188 -0
  39. dejavuu-0.1.0/src/dejavuu/py.typed +0 -0
  40. dejavuu-0.1.0/src/dejavuu/tools/__init__.py +0 -0
  41. dejavuu-0.1.0/src/dejavuu/tools/artifact.py +90 -0
  42. dejavuu-0.1.0/src/dejavuu/tools/build_specbench_corpus.py +97 -0
  43. dejavuu-0.1.0/src/dejavuu/tools/build_tree_decoder.py +220 -0
  44. dejavuu-0.1.0/src/dejavuu/tools/build_vlm_decoder.py +129 -0
  45. dejavuu-0.1.0/src/dejavuu/tools/eval_tree.py +154 -0
  46. dejavuu-0.1.0/src/dejavuu/tools/specbench_entropy.py +100 -0
@@ -0,0 +1,60 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-07-12
10
+
11
+ First public release of `dejavuu`.
12
+
13
+ ### Added
14
+ - Hugging Face transformers backend (`backend="hf"`, `hf` extra): run any
15
+ `AutoModelForCausalLM` through the spec-decode engine with no ONNX export. Chain **and
16
+ tree** verification, both bit-exact with greedy and tested lossless on every drafter.
17
+ Tree needs no re-export: eager attention honours the engine's 4D additive mask +
18
+ explicit position_ids, so tree verification and the representation drafters
19
+ (`pld_plus`/`adapld`) work on any HF causal LM. KV management moved behind the backend
20
+ interface (`Verifier.rollback_kv`/`gather_kv`, numpy default), so a torch cache plugs
21
+ in without touching the engine. `attn_implementation` is selectable ("eager" default,
22
+ "sdpa" validated lossless for GPU perf). Validated bit-exact on real weights
23
+ (SmolLM2-135M) as well as a random tiny model, chain and tree.
24
+ - The PyPI distribution, import package, and CLI are all named `dejavuu`.
25
+ - `dejavuu.drafters.DRAFTERS` registry and `make_drafter` now live in the library, so
26
+ the CLI, the `DejaVu` API, and the benchmark harnesses share one source of truth and
27
+ a plain `import dejavuu` no longer pulls the benchmark dependencies.
28
+ - `vlm`, `bench`, and `build` optional-dependency extras. The base install is the
29
+ text-path library only (no torch, onnx, or pillow).
30
+ - Chain and tree verification for every registered method; `pld` and the `asam` family
31
+ now emit genuine branching drafts under tree verification.
32
+ - Registry conformance suite (`tests/test_conformance.py`): every method must be valid
33
+ and bit-exact with the baseline under both chain and tree verification.
34
+ - Packaging metadata (keywords, classifiers, project URLs) and a clean-env install-smoke
35
+ CI job that proves the base wheel imports without the extras.
36
+ - Tooling gate: ruff (full ruleset), pyrefly, deptry, pip-audit, prek pre-commit hooks,
37
+ and coverage gating at 90%.
38
+ - Validated configuration via pydantic (`dejavuu.config`): `GenerationConfig` and
39
+ `ModelConfig` check bounds (temperature, top_p, budget, ...), the method name, and the
40
+ backend/device combination at the API and CLI boundary. Unknown methods raise a clear
41
+ error listing valid names instead of a bare `KeyError`.
42
+ - `DejaVu.generate` now exposes `tree=`/`width=` for tree verification; decode arguments
43
+ are keyword-only.
44
+
45
+ ### Changed
46
+ - Registry entries are typed `DrafterSpec`s (factory + capabilities) instead of a bare
47
+ name->class map, removing the `None` baseline sentinel, the `partial` presets, and the
48
+ separate `_DATASTORE_METHODS` set. `METHODS` lists `baseline` plus every drafter.
49
+ - VLM backends are identified by `Verifier.is_vlm`/`prepare` on the contract rather than
50
+ by `hasattr` duck-typing in the API.
51
+ - Dependency floors relaxed from newest-release pins to conservative lower bounds on the
52
+ APIs actually used.
53
+ - Moved the offline model-build toolchain dependencies (`onnx`, `onnxruntime-genai`,
54
+ `onnxscript`, `onnx-ir`) out of the base install into the `build` extra.
55
+
56
+ ### Removed
57
+ - Unused `num2words` dependency.
58
+
59
+ [Unreleased]: https://github.com/icnatspell/dejavuu/compare/v0.1.0...HEAD
60
+ [0.1.0]: https://github.com/icnatspell/dejavuu/releases/tag/v0.1.0
@@ -0,0 +1,63 @@
1
+ # Contributing
2
+
3
+ Thanks for helping out. This is a research-grade library with a hard correctness bar:
4
+ speculative decoding must be **lossless**, so most changes come with a test that proves
5
+ bit-exactness against the plain baseline.
6
+
7
+ ## Setup
8
+
9
+ You need Python 3.13+ and [uv](https://docs.astral.sh/uv/).
10
+
11
+ ```bash
12
+ uv sync --all-extras --index-strategy unsafe-best-match # library + vlm/hf/build extras
13
+ uv run prek install # git pre-commit hooks
14
+ ```
15
+
16
+ ## The gate
17
+
18
+ Run the full suite before pushing. CI runs the same thing and is the final word.
19
+
20
+ ```bash
21
+ uv run prek run --all-files
22
+ ```
23
+
24
+ That is: `ruff` (lint + format), `pyrefly` (types), `deptry` (dependency hygiene),
25
+ `uv lock --check`, the offline test suite, and coverage (must stay >= 90%). Security
26
+ auditing (`pip-audit`) runs as a separate CI job.
27
+
28
+ Tests that need a downloaded model are marked `model` and are opt-in:
29
+
30
+ ```bash
31
+ uv run pytest # fast, offline (default)
32
+ uv run pytest -m model # also runs the real-model correctness checks
33
+ ```
34
+
35
+ ## Adding a drafter
36
+
37
+ A drafter proposes candidate tokens from raw token ids only -- never model internals --
38
+ so one instance drives every backend and both text and vision.
39
+
40
+ 1. Subclass `Drafter` (`dejavuu/drafters/base.py`); implement `propose` (chain). Override
41
+ `propose_tree` if you can branch; otherwise it defaults to the chain.
42
+ 2. Register it in `DRAFTERS` (`dejavuu/drafters/__init__.py`). That alone wires it into
43
+ the CLI, the `DejaVu` API, both benches, and the conformance suite.
44
+ 3. `tests/test_conformance.py` then requires it to emit a valid `DraftTree` and stay
45
+ bit-exact with the baseline under **both** chain and tree verification. If it can't
46
+ pass both, it isn't done.
47
+
48
+ Losslessness is the verifier's job, not the drafter's. A drafter may propose anything;
49
+ never weaken the accept rule to make one "work".
50
+
51
+ ## Adding a backend
52
+
53
+ A backend implements the `Verifier` contract (`dejavuu/core/verifier.py`): `forward`,
54
+ `empty_kv`, and -- if its KV isn't a numpy list -- `rollback_kv`/`gather_kv`. See
55
+ `decoders/hf.py` for the transformers backend as a worked example. New backends should
56
+ carry a lossless test on a small real model (mirror `tests/test_hf_backend.py`).
57
+
58
+ ## Conventions
59
+
60
+ `AGENTS.md` is the working agreement (the drafter contract, cost tiers, and the "never
61
+ call a technique dead on an unoptimized implementation" rule). Use
62
+ [Conventional Commits](https://www.conventionalcommits.org/), keep `CHANGELOG.md`
63
+ current under `[Unreleased]`, and follow [SemVer](https://semver.org/) for releases.
dejavuu-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 icnatspell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
dejavuu-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,317 @@
1
+ Metadata-Version: 2.4
2
+ Name: dejavuu
3
+ Version: 0.1.0
4
+ Summary: Retrieval-based speculative decoding for ONNX Runtime and Hugging Face transformers
5
+ Keywords: speculative-decoding,onnxruntime,transformers,llm,inference,retrieval,training-free
6
+ Author: icnatspell
7
+ Author-email: icnatspell <spellicnat@gmail.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Classifier: Operating System :: OS Independent
16
+ Requires-Dist: huggingface-hub>=0.26
17
+ Requires-Dist: loguru>=0.7
18
+ Requires-Dist: numpy>=2.0
19
+ Requires-Dist: onnxruntime>=1.20
20
+ Requires-Dist: pydantic>=2.0
21
+ Requires-Dist: rich>=13.0
22
+ Requires-Dist: tqdm>=4.66
23
+ Requires-Dist: transformers>=4.45
24
+ Requires-Dist: typer>=0.12
25
+ Requires-Dist: dejavuu[vlm] ; extra == 'bench'
26
+ Requires-Dist: dejavuu[vlm] ; extra == 'build'
27
+ Requires-Dist: onnx>=1.16 ; extra == 'build'
28
+ Requires-Dist: onnxruntime-genai>=0.5 ; extra == 'build'
29
+ Requires-Dist: onnxscript>=0.7 ; extra == 'build'
30
+ Requires-Dist: onnx-ir>=0.1 ; extra == 'build'
31
+ Requires-Dist: torch>=2.4 ; extra == 'hf'
32
+ Requires-Dist: pillow>=10.0 ; extra == 'vlm'
33
+ Requires-Dist: torch>=2.4 ; extra == 'vlm'
34
+ Requires-Dist: torchvision>=0.19 ; extra == 'vlm'
35
+ Requires-Python: >=3.13
36
+ Project-URL: Homepage, https://github.com/icnatspell/dejavuu
37
+ Project-URL: Repository, https://github.com/icnatspell/dejavuu
38
+ Project-URL: Issues, https://github.com/icnatspell/dejavuu/issues
39
+ Provides-Extra: bench
40
+ Provides-Extra: build
41
+ Provides-Extra: hf
42
+ Provides-Extra: vlm
43
+ Description-Content-Type: text/markdown
44
+
45
+ # dejavuu
46
+
47
+ Training-free speculative decoding for [ONNX Runtime](https://onnxruntime.ai/) and
48
+ Hugging Face PyTorch models.
49
+
50
+ [![CI](https://github.com/icnatspell/dejavuu/actions/workflows/ci.yml/badge.svg)](https://github.com/icnatspell/dejavuu/actions/workflows/ci.yml)
51
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/icnatspell/dejavuu/blob/main/LICENSE)
52
+ ![Python 3.13+](https://img.shields.io/badge/python-3.13%2B-blue.svg)
53
+
54
+ Speculative decoding drafts several tokens cheaply, then checks them in one model
55
+ forward pass. The target model's accept rule keeps only the tokens it would have
56
+ produced anyway, so a wrong guess wastes one pass and never changes the output. The
57
+ speedup comes from replacing many single-token passes with fewer multi-token ones.
58
+
59
+ `dejavuu` is model-free in the drafting sense: it drops the small auxiliary draft model
60
+ that speculative decoding usually needs. Its drafters copy their guesses straight from
61
+ text the target model has already seen—the prompt, the generation so far, or a fixed
62
+ corpus. A guess is an index lookup, not a second network. Every drafter works on raw
63
+ token ids, so one instance drives both a text LLM and a vision-language model through
64
+ the same verifier.
65
+
66
+ - No draft model to load, train, or keep in memory.
67
+ - Strictly lossless on the text path, checked bit-for-bit in CI.
68
+ - One model spans both modalities: SmolVLM2 runs the text and the vision benchmark.
69
+ - Chain and tree verification both work for every method.
70
+
71
+ [docs/methods.md](docs/methods.md) explains how the drafters differ and when each one wins.
72
+
73
+ ## Where dejavuu fits
74
+
75
+ Dejavuu is designed for **input-grounded** generation: tasks where a useful part of the
76
+ answer is present in, or closely constrained by, the prompt or an attached datastore.
77
+ That includes retrieval-augmented generation (RAG), function calling and other
78
+ structured output, summarization, code completion, and similar copy- or
79
+ context-heavy workloads. These methods can turn repeated token patterns into drafts
80
+ without training or loading a second model.
81
+
82
+ For tasks whose answer is weakly grounded in the input—such as open-ended translation
83
+ or generation—a learned, model-based drafter such as EAGLE-3 or DFlash may be a better
84
+ fit. This is a workload distinction, not a universal ranking: measure acceptance,
85
+ verify cost, and end-to-end decode throughput on the target model and prompts before
86
+ choosing a method.
87
+
88
+ ## Quickstart
89
+
90
+ Install the library from PyPI (Python 3.13+). The base install is the text path and is
91
+ torch-free; the import package is `dejavuu`:
92
+
93
+ ```bash
94
+ pip install dejavuu # library + text path + the `dejavuu` CLI
95
+ pip install "dejavuu[hf]" # run any transformers model (torch), no ONNX export
96
+ pip install "dejavuu[vlm]" # add a vision-language model (torch, torchvision)
97
+ pip install "dejavuu[build]" # add the offline model-build toolchain
98
+ ```
99
+
100
+ Run a local ONNX export (the default backend):
101
+
102
+ ```python
103
+ from dejavuu import DejaVu
104
+
105
+ model = DejaVu.from_pretrained("onnx-community/gemma-3-270m-ONNX", method="pld")
106
+ print(model.generate("def add(a, b):\n return a + b\n\ndef sub(a, b):\n return"))
107
+ ```
108
+
109
+ Or run any Hugging Face causal LM with no export (`backend="hf"`, explicit `device`).
110
+ Chain and tree verification both work with no re-export, on any model:
111
+
112
+ ```python
113
+ model = DejaVu.from_pretrained(
114
+ "meta-llama/Llama-3.2-1B", backend="hf", device="cuda", method="pld"
115
+ )
116
+ print(model.generate("The capital of France is"))
117
+ ```
118
+
119
+ The same generation from the CLI, no VLM build required:
120
+
121
+ ```bash
122
+ dejavuu "def add(a, b):\n return a + b\n\ndef sub(a, b):\n return" --method pld
123
+ ```
124
+
125
+ `--method` (or `method=`) takes any name from the [table below](#methods). Add
126
+ `--temperature`, `--top-p`, or `--seed` to switch from greedy to sampling. To hack on
127
+ the repo instead of installing, use `uv sync` and prefix commands with `uv run`.
128
+
129
+ ## Results
130
+
131
+ SmolVLM2 (int4) on Spec-Bench, 480 prompts, CPU. Speedup is decode-only throughput
132
+ over the plain autoregressive baseline. Retrieval pays off most where the output
133
+ repeats the input, so the best method varies by task:
134
+
135
+ | task | best method | speedup | token match |
136
+ |---|---|---:|---:|
137
+ | retrieval-augmented generation | `suffix_decoding` | 1.66x | 79% |
138
+ | multi-turn conversation | `sam_decoding` | 1.32x | 55% |
139
+ | summarization | `asam` | 1.31x | 73% |
140
+ | mathematical reasoning | `anpd` | 1.29x | 49% |
141
+ | question answering | `anpd` | 1.15x | 48% |
142
+ | translation | `anpd` | 2.00x | 27% |
143
+
144
+ The strongest methods average about 1.2x across the six tasks. The clean wins are RAG
145
+ and summarization, where the drafted output tracks the baseline closely. Translation
146
+ and QA decode faster but diverge more, because the quantized SmolVLM decoder is not
147
+ length-invariant: its speculative output is *near*-lossless (a token-match percentage
148
+ against the baseline), not bit-exact. The Gemma text path stays strictly lossless, and
149
+ the bit-exactness unit tests guard it. Reproduce these numbers in
150
+ [Reproduce the benchmark](#reproduce-the-benchmark).
151
+
152
+ ## Methods
153
+
154
+ Every drafter runs on raw token ids, so the same instance drives both the LLM and the
155
+ VLM. REST, SuffixDecoding, SAM, and ASAM share one reusable token-only `SuffixIndex`.
156
+
157
+ | method | idea |
158
+ |---|---|
159
+ | `baseline` | plain autoregressive, no drafter |
160
+ | `pld` | prompt-lookup: longest suffix match within the context |
161
+ | `pld_plus` | `pld` plus hidden-state reranking of matches (needs a hidden-emitting decoder; else falls back to `pld`) |
162
+ | `adapld` | `pld_plus` with a semantic fallback, plus a branched tree under `--tree` |
163
+ | `anpd` | adaptive n-gram draft length |
164
+ | `lookahead` | multi-candidate n-gram pool |
165
+ | `token_recycling` | tree drafts from the verifier's own logits |
166
+ | `rest` | retrieval from a static datastore |
167
+ | `suffix_decoding` | online suffix index over global and per-request history |
168
+ | `sam_decoding` | static datastore plus live generation; drafts from the longer match |
169
+ | `asam` / `asam_verify` | adaptive SAM with an acceptance-calibrated cap, plus verify-cost-aware sizing |
170
+ | `asd` / `asd_verify` | `asam` without a datastore, so an adaptive suffix decoder |
171
+
172
+ The retrieval drafters are lossless under greedy decoding: they only copy tokens the
173
+ model has seen, and draft sizing shortens a draft without changing which tokens it
174
+ proposes. Every method emits a chain by default and a branching tree under `--tree`
175
+ (`pld_plus` and `anpd` fall back to a chain, since neither has a natural fork).
176
+
177
+ ## How it works
178
+
179
+ Each decode step drafts several tokens, verifies them in one forward pass, and keeps
180
+ only the prefix the model would have produced on its own:
181
+
182
+ ```
183
+ prompt --prefill--> KV cache, anchor token
184
+ loop until max_new / EOS:
185
+ 1. drafter.propose(ctx) ------------------> DraftTree (chain, or a branching tree)
186
+ 2. flatten the tree -----------------------> position_ids + attention mask
187
+ 3. backend.forward(tokens, kv, mask) ------> logits (+ optional hidden states)
188
+ 4. accept: descend while the model's pick --> emitted tokens + accepted path
189
+ matches a drafted child (argmax, or a position-seeded sample)
190
+ 5. roll the KV cache back to the accepted path; the last token is the next anchor
191
+ ```
192
+
193
+ A wrong guess costs one forward pass and is discarded at step 4, so the output is
194
+ identical to plain decoding. Steps 1, 2, and 4 are **backend-agnostic** (they work on
195
+ token ids and a logits array). Only step 3 (the forward) and the KV rollback in step 5
196
+ are **backend-specific**, and both live behind the `Verifier` contract:
197
+
198
+ ```
199
+ Verifier # the backend seam (dejavuu/core/verifier.py)
200
+ forward(tokens, kv, pos, mask) -> logits, kv, hidden
201
+ rollback_kv / gather_kv # chain / tree KV trim (numpy default; a backend overrides)
202
+ ```
203
+
204
+ Two backends implement it. **ORT** (`backend="ort"`, default) runs a local ONNX export
205
+ and auto-derives its I/O contract from the graph. **HF** (`backend="hf"`) runs any
206
+ transformers `AutoModelForCausalLM` with no export; eager attention honours the tree's
207
+ 4D mask, so chain and tree verification and the hidden-state drafters all work on any
208
+ model. A drafter never sees a tensor or a model config, so the same instance drives both
209
+ backends and both text and vision.
210
+
211
+ ## Reproduce the benchmark
212
+
213
+ A fresh clone reaches the full numbers in a few steps. Both benchmarks run the one
214
+ SmolVLM2 model, text Spec-Bench and vision MMSpec, so the results compare across
215
+ datasets.
216
+
217
+ ```bash
218
+ # 1. install (--extra vlm pulls torch/torchvision, for image preprocessing only)
219
+ uv sync --extra vlm
220
+
221
+ # 2. sanity: unit tests + a drafter self-check
222
+ uv run pytest -q
223
+ uv run python -m dejavuu.drafters.asam
224
+
225
+ # 3. one-time: build the SmolVLM2 tree+hidden decoder (weights auto-download from HF;
226
+ # this decoder isn't published, so we build it). Emits fp32 + int8 + int4 (int4 body
227
+ # with an int8 lm-head, ~234MB) into ~/.cache/dejavuu/smolvlm2_decoder_tree_embeds/
228
+ # (NOT in the repo -- rebuild it per machine). The runtime auto-prefers int4. It is
229
+ # tree-capable AND emits hidden states, so pld_plus/adapld and --tree both work.
230
+ # --with onnx_ir enables the int4 step (without it you still get fp32 + int8).
231
+ uv run --extra vlm --with onnx_ir --index-strategy unsafe-best-match \
232
+ python -m dejavuu.tools.build_tree_decoder
233
+ # re-quantize only (fast, skips the ~4-min torch re-export) if the fp32 model exists:
234
+ # uv run --extra vlm --with onnx_ir --index-strategy unsafe-best-match \
235
+ # python -m dejavuu.tools.build_tree_decoder --quant-only
236
+
237
+ # 4. one-time: build the retrieval datastore (regenerable; gitignored)
238
+ uv run python -m dejavuu.tools.build_specbench_corpus # -> data/specbench_corpus.txt
239
+
240
+ # 5. smoke: 1 prompt/topic, both benches, all methods, downscaled images
241
+ ./scripts/bench_all.sh 1 512
242
+
243
+ # 6. full run: all samples/topic, detached so it survives your shell
244
+ rm -f results/specbench.* results/mmspec.*
245
+ nohup ./scripts/bench_all.sh 80 512 > results/run.out 2>&1 &
246
+ tail -f results/run.out
247
+
248
+ # 7. same sweep with tree-based verification instead of chain (4th arg = 1)
249
+ ./scripts/bench_all.sh 80 512 0 1
250
+ ```
251
+
252
+ Results land in `results/{specbench,mmspec}.{csv,log}`. The four knobs are positional,
253
+ `scripts/bench_all.sh <K> <IMG> <THREADS> <TREE>`:
254
+
255
+ - `K` is prompts per topic (80 covers all of Spec-Bench; MMSpec saturates at 10 or more).
256
+ - `IMG` is the MMSpec image longest-edge in pixels. Smaller means fewer, smaller tiles
257
+ and a faster run (512 is one tile, 256 downscales further, `0` is full resolution).
258
+ The text bench ignores it.
259
+ - `THREADS` is ORT intra-op threads on CPU (`0` is the ORT default, `4` pins to 4 cores).
260
+ - `TREE` is `0` for chain verify (default) or `1` for tree-based verification.
261
+
262
+ ```bash
263
+ ./scripts/bench_all.sh 80 512 4 # 80/topic, 4 CPU threads, chain verify
264
+ ./scripts/bench_all.sh 80 512 0 1 # tree-based verification (needs the step-3 decoder)
265
+ ./scripts/bench_all.sh 80 256 4 1 # all knobs: 256px images, 4 threads, tree ON
266
+ ./scripts/bench_all.sh 80 256 4 0 # same but chain (the with/without-tree pair)
267
+ ```
268
+
269
+ To call a bench directly (both datasets go through the one SmolVLM harness), drop
270
+ `--tree` for chain, and set `--width` for max children per node under a tree:
271
+
272
+ ```bash
273
+ uv run --extra vlm python -m dejavuu.eval.mmspec --dataset specbench \
274
+ --methods baseline,pld,pld_plus,adapld --per-category 80 --threads 4 # chain
275
+ uv run --extra vlm python -m dejavuu.eval.mmspec --dataset mmspec \
276
+ --methods baseline,pld,pld_plus,adapld --per-category 80 \
277
+ --image-size 256 --threads 4 --tree --width 2 # tree
278
+ ```
279
+
280
+ `--provider cpu` selects `CPUExecutionProvider`, and `--threads N` sets its
281
+ `intra_op_num_threads`. `--tree` needs the tree+hidden decoder from step 3, and warns
282
+ and falls back to chain without it.
283
+
284
+ ## How results are reported
285
+
286
+ `eval/harness.py` renders a per-topic table and CSV. `tok/s` and the speedups are
287
+ decode-only, since prefill is a one-time prompt tax that has nothing to do with
288
+ speculative decoding and would dilute the decode-loop speedup (the table reports it in
289
+ its own column). Every per-prompt column is `mean ± std`, and the per-step time splits
290
+ as `total = prefill + draft + verify + overhead`. A strict exactness gate, or a
291
+ token-match percentage for the VLM, guards every method against its baseline.
292
+
293
+ ## Layout
294
+
295
+ ```
296
+ dejavuu/
297
+ api.py DejaVu.from_pretrained(...).generate(...) (drop-in)
298
+ cli.py the `dejavuu` single-generation command
299
+ core/ model-agnostic spec-decode: verifier (contract), engine,
300
+ tree, sampling
301
+ decoders/ Verifier implementations: ort, text (Model), vlm (VLM)
302
+ drafters/ the method zoo (base, suffix_index, one file per method)
303
+ eval/ benchmark harnesses: harness (shared), specbench, mmspec
304
+ tools/ build_tree_decoder (tree+hidden, quantized), build_vlm_decoder,
305
+ build_specbench_corpus, specbench_entropy, eval_tree
306
+ scripts/bench_all.sh one-command specbench + mmspec sweep over all methods
307
+ ```
308
+
309
+ ## Development
310
+
311
+ `AGENTS.md` is the working agreement: the drafter contract, the cost tiers, and how new
312
+ methods stay lossless under both chain and tree verification.
313
+
314
+ ```bash
315
+ uv run prek run --all-files # the full gate: ruff, pyrefly, deptry, tests, coverage
316
+ uv run pytest # offline unit tests only (add -m model for the Gemma run)
317
+ ```