defuss-vectorsearch 0.0.0-stage โ†’ 0.1.0

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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +479 -2
  3. package/dist/browser.d.mts +18 -0
  4. package/dist/browser.mjs +22 -0
  5. package/dist/client.cjs +62 -0
  6. package/dist/client.d.cts +15 -0
  7. package/dist/client.d.mts +15 -0
  8. package/dist/client.mjs +23 -0
  9. package/dist/core-DSMVtqZ6.mjs +353 -0
  10. package/dist/core-DaRhdYmA.cjs +355 -0
  11. package/dist/core-DnBchjh2.d.mts +39 -0
  12. package/dist/core-UGRF9Eqm.d.cts +39 -0
  13. package/dist/model-cache-qTj4Dnw0.cjs +380 -0
  14. package/dist/model-cache-qd7znbJ5.mjs +38 -0
  15. package/dist/model-cache.browser-BVWrcI3Y.mjs +322 -0
  16. package/dist/model-cache.node-C6M9XdFb.cjs +58 -0
  17. package/dist/model-cache.node-D4J7dSRw.mjs +51 -0
  18. package/dist/model-profiles-BDS-uZ4e.d.cts +48 -0
  19. package/dist/model-profiles-BDS-uZ4e.d.mts +48 -0
  20. package/dist/onnx.cjs +273 -0
  21. package/dist/onnx.d.cts +13 -0
  22. package/dist/onnx.d.mts +13 -0
  23. package/dist/onnx.mjs +34 -0
  24. package/dist/prompts-BbVgTLA6.mjs +163 -0
  25. package/dist/prompts-Cq8h-R6V.cjs +170 -0
  26. package/dist/server.cjs +74 -0
  27. package/dist/server.d.cts +15 -0
  28. package/dist/server.d.mts +15 -0
  29. package/dist/server.mjs +35 -0
  30. package/dist/shared.cjs +49 -0
  31. package/dist/shared.d.cts +29 -0
  32. package/dist/shared.d.mts +29 -0
  33. package/dist/shared.mjs +8 -0
  34. package/dist/turboquant.cjs +239 -0
  35. package/dist/turboquant.d.cts +17 -0
  36. package/dist/turboquant.d.mts +17 -0
  37. package/dist/turboquant.mjs +232 -0
  38. package/dist/vector-math-BDEYyzR7.d.cts +16 -0
  39. package/dist/vector-math-C29mf_rv.d.mts +16 -0
  40. package/dist/vector-math-D8tpjoQx.cjs +121 -0
  41. package/dist/vector-math-DwdA_xwv.mjs +113 -0
  42. package/dist/vector-search.cjs +135 -0
  43. package/dist/vector-search.d.cts +10 -0
  44. package/dist/vector-search.d.mts +10 -0
  45. package/dist/vector-search.mjs +126 -0
  46. package/dist/winzling-embedder-CthXBDRS.mjs +237 -0
  47. package/dist/winzling-embedder-Dqq64fVb.d.cts +245 -0
  48. package/dist/winzling-embedder-Dqq64fVb.d.mts +245 -0
  49. package/package.json +147 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 - 2026 Aron Homberg
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.
package/README.md CHANGED
@@ -1,3 +1,480 @@
1
- # Temporary Holding Version
1
+ # defuss-vectorsearch
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![License: MIT](https://img.shields.io/badge/license-MIT-informational)](LICENSE)
4
+ [![Node](https://img.shields.io/badge/node-%E2%89%A522.18-success)](package.json)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](tsconfig.json)
6
+ [![Live demo](https://img.shields.io/badge/demo-vectorsearch.defuss.org-success)](https://vectorsearch.defuss.org)
7
+
8
+ **Vector** search for JavaScript: a 39.76 MB (all-in) multilingual embedding model and a 4-bit index, in the browser and in Node.js.
9
+
10
+ The Winzling embedding model runs on ONNX Runtime Web (WASM, experimental WebGPU) in the browser and on ONNX Runtime (WASM or native CPU) in Node.js, behind the same API. TurboQuant stores each vector as 4-bit codes, so 2,000 passages take 0.49 MiB instead of 2.93 MiB in float32.
11
+
12
+ ## TL;DR
13
+
14
+ Build the index server-side and publish it. The browser downloads the index, embeds the search query locally and searches the TurboQuant index locally. Once the model is cached, a reload loads it in ~0.4 s; embedding a query then takes 29.7 ms and searching 2,000 passages 0.46 ms (headless Chromium, see [Quick start](#quick-start) and [Benchmark](#benchmark)).
15
+
16
+ - ๐Ÿ” **Isomorphic:** one embedder API in browsers and in Node.js; both produce the same Winzling vectors (minimum cosine 1 over 40 passages)
17
+ - ๐Ÿชถ **Small model:** 39.76 MB to download (30.46 MB ONNX graph, 9.29 MB tokenizer) instead of 304.68 MB for Harrier, and 206 MiB of memory at runtime in Chromium
18
+ - ๐Ÿ—œ๏ธ **4-bit index:** TurboQuant keeps 256 B per vector instead of 1,536 B in float32; the live demo's 2,000 passages are 431 kB gzipped
19
+ - ๐ŸŽฏ **Multilingual recall:** R@5 of 92.2% over nine languages, 94 to 100% for German, Russian and English, against a 2,000-passage haystack in 20 languages
20
+ - โšก **Fast queries:** 29.7 ms to embed a query, 0.46 ms to search 2,000 passages (means, Chromium, WASM, 1 thread)
21
+ - ๐Ÿ”’ **Pinned and checked:** one pinned model revision; every file is SHA-256 checked, cache hits included, and cached in the Cache API and IndexedDB (browser) or on the filesystem (Node.js)
22
+ - ๐Ÿ“ฆ **Lean browser bundle:** `browser.js` builds to 340 KB of JavaScript plus the 26.78 MB ONNX Runtime Web WASM, with no Transformers.js or Node.js code
23
+ - ๐Ÿงช **Oracle-tested:** exact token IDs, and embeddings within `2e-5` of Rust tokenizers and Python ONNX Runtime, on 24 multilingual and adversarial cases
24
+
25
+ ## How it works
26
+
27
+ ```mermaid
28
+ flowchart LR
29
+ D(["๐Ÿ“„ documents"]) --> E1
30
+
31
+ subgraph build ["๐Ÿ—๏ธ ahead of time, Node.js"]
32
+ E1["embedDocuments<br/>Winzling, native CPU"] --> B["buildTurboQuantIndex<br/>4-bit codes"]
33
+ end
34
+
35
+ B -->|"your format + gzip"| H[("static host")]
36
+
37
+ subgraph browser ["๐ŸŒ per query, browser"]
38
+ Q(["๐Ÿ™‹ query"]) --> E2["embedQuery<br/>ONNX Runtime Web"]
39
+ E2 --> S["searchTurboQuantIndex<br/>top k"]
40
+ end
41
+
42
+ H -->|"download once"| S
43
+ S --> R(["hits"])
44
+ ```
45
+
46
+ The package does not serialize indexes; the live demo writes its own format (`scripts/docs/index-format.ts`, see [Patterns](#patterns)).
47
+
48
+ | Entry point | When you reach for it | What it does |
49
+ |---|---|---|
50
+ | **`browser.js`** | a browser app | Winzling embedder cached in the Cache API and IndexedDB, TurboQuant, `normalizeVector(s)`. ESM-only. A Vite production build contains no Transformers.js, `defuss-multicore`, `onnxruntime-node` or Node built-in code. |
51
+ | **`onnx.js`** | Node.js or isomorphic code | The same Winzling embedder, with a filesystem cache in Node.js and the browser cache in browsers. Does not import Transformers.js. ESM + CJS. |
52
+ | **`server.js` / `client.js`** | Harrier, OpenAI-compatible endpoints, or one factory for several models | `createEmbeddingServer` / `createEmbeddingClient`. The no-argument factory defaults to Harrier; known model IDs select their profile. |
53
+ | **`turboquant.js`** | a small index | `buildTurboQuantIndex`, `searchTurboQuantIndex`, `searchTurboQuantIndexRerank`, `scoreTurboQuantIndex`, `rerankSearchHits`, `selfCheckTurboQuant`. |
54
+ | **`vector-search.js`** | exact (bruteforce) search | `searchTopK`, `searchTopKMulticore`, `dotProduct`, `normalizeVector(s)`, `topKFromScores`, `attachRecords`. |
55
+
56
+ ## Install
57
+
58
+ 0.1.0 is not on the npm registry. Use it from a checkout of [the repository](https://github.com/kyr0/defuss-vectorsearch); see [From a checkout](#from-a-checkout).
59
+
60
+ ## Quick start
61
+
62
+ ### Browser: Winzling + TurboQuant
63
+
64
+ ```ts
65
+ import { buildTurboQuantIndex, createWinzlingEmbedder, searchTurboQuantIndex } from "defuss-vectorsearch/browser.js";
66
+
67
+ const embedder = createWinzlingEmbedder(); // one ONNX Runtime Web session, reused for every call
68
+ const documents = ["The cat sleeps on the sofa.", "Die Bรถrse schloss heute im Plus.", "ะšะพัˆะบะฐ ัะฟะธั‚ ะฝะฐ ะดะธะฒะฐะฝะต."];
69
+ const index = buildTurboQuantIndex(await embedder.embedDocuments(documents));
70
+ const hits = searchTurboQuantIndex(index, await embedder.embedQuery("A sleeping cat on a couch."), 2);
71
+ console.log(hits.map((hit) => documents[hit.index])); // both cat sentences
72
+ ```
73
+
74
+ VERIFIED by `scripts/test-browser-bundle.ts` (part of `make e2e`) and one run against huggingface.co:
75
+
76
+ - **Auto-download and cache.** The first call downloads the pinned Winzling release from Hugging Face (39.76 MB) and checks every file's SHA-256. It stores the files in the Cache API and IndexedDB. A reload makes no model request: 3.7 s cold, 0.4 s warm in headless Chromium.
77
+ - **Bundle contents.** `browser.js` holds the Winzling embedder, TurboQuant search and `normalizeVector`. A Vite production build of the snippet contains no Transformers.js, `defuss-multicore`, `onnxruntime-node` or Node built-in code. It is 340 KB of JavaScript plus the 26.78 MB ONNX Runtime Web WASM, which Vite emits next to the bundle by itself.
78
+ - **Bundlers.** Only Vite was tested. Under Vite's dev server, keep `onnxruntime-web` out of dependency pre-bundling (`optimizeDeps: { exclude: ["onnxruntime-web"] }`), as this repo's `vite.config.ts` does. With another bundler, pass `wasmPaths` pointing at a copy of `onnxruntime-web/dist/`, as the demo does with `"/ort/"`.
79
+ - **Format.** The entry is ESM-only and accepts `device: "wasm"` (default) or `"webgpu"`.
80
+
81
+ This is the smallest and fastest setup in the [benchmark](#benchmark): 39.8 MB instead of 305 MB to download, 2.8ร— faster embedding in Chromium, and a TurboQuant index 6ร— smaller than float32. It retrieves less well than Harrier in the nine languages both models target (German, Russian, English, French, Indonesian, Dutch, Italian, Portuguese, Spanish). R@3 is 83.8% against 98.9%, and R@25 is 98.9% against 100% (Node.js, bruteforce). Its weakest target languages are Portuguese and Spanish, at 76% and 74% R@3. When the top 3 must be right, use Harrier through `client.js`.
82
+
83
+ ### Node.js: Winzling only
84
+
85
+ ```ts
86
+ import { createWinzlingEmbedder } from "defuss-vectorsearch/onnx.js";
87
+
88
+ const embedder = createWinzlingEmbedder({ device: "wasm" });
89
+ try {
90
+ const documents = await embedder.embedDocuments([
91
+ "The cat sleeps on the sofa.",
92
+ "Die Katze schlรคft auf dem Sofa.",
93
+ "ะšะพัˆะบะฐ ัะฟะธั‚ ะฝะฐ ะดะธะฒะฐะฝะต.",
94
+ ]);
95
+ const query = await embedder.embedQuery("A sleeping cat on a couch.");
96
+ console.log(documents.length, query.length); // 3, 384
97
+ } finally {
98
+ await embedder.dispose();
99
+ }
100
+ ```
101
+
102
+ `onnx.js` does not import Transformers.js. The retained legacy dependencies remain in the package's installation footprint. This release does not split or remove them.
103
+
104
+ ### From a checkout
105
+
106
+ Bun installs dependencies and runs scripts. Node **22.18+** executes the TypeScript (native type stripping) and the native ONNX Runtime; tested with Node 24.19.0 on Linux x64.
107
+
108
+ ```sh
109
+ make setup # bun install, Playwright Chromium, build, model assets
110
+ node examples/node.ts # CPU via ONNX Runtime Web/WASM
111
+ node examples/node.ts --cpu # native Node CPU
112
+ bun run demo # open the printed localhost URL
113
+ ```
114
+
115
+ `make setup` skips the optional CUDA download; the native CPU binaries ship in the `onnxruntime-node` package, so Bun's blocked postinstall for it is harmless.
116
+
117
+ The ZIP includes the exact model graph and tokenizer under `public/models/winzling/`. `models:download` validates their SHA-256 hashes; when absent, it downloads the pinned release. It also copies the installed ORT WASM/JavaScript assets into `public/ort/` for the local browser demo. The model and runtime assets are ignored by Git and excluded from the npm tarball. After dependency installation and asset preparation, the examples need no network.
118
+
119
+ Without `make` (e.g. Windows PowerShell), set `$env:ONNXRUNTIME_NODE_INSTALL_CUDA='skip'`, then run `bun install`, `bunx playwright install chromium`, `bun run build` and `bun run models:download`. A minimal Linux host needs `make setup PLAYWRIGHT_FLAGS=--with-deps`.
120
+
121
+ ## Live demo
122
+
123
+ [vectorsearch.defuss.org](https://vectorsearch.defuss.org) serves `docs/index.html`, a standalone page built with defuss-shadcn from its CDN. On page load it fetches a prebuilt index of the 2,000 benchmark passages; the first search loads the pinned Winzling model with byte progress, and later searches run as you type. The โŒ˜K palette lists the 1,000 benchmark questions by language; for those, the results mark the gold passage and its translations. A second tab is a vector database you fill yourself: each note you write is embedded in the browser 500 ms after you stop typing (or when it loses focus) into a TurboQuant index you can search right beside it. The "Use it" section gives a prompt that points a coding agent at this repository.
124
+
125
+ ```sh
126
+ python3 -m http.server -d docs 8080 # serves docs/ at http://localhost:8080/
127
+ make docs # rebuilds docs/data/ (~9 min) and docs/assets/search-worker.js
128
+ ```
129
+
130
+ VERIFIED by `scripts/test-docs.ts` (part of `make e2e`), which serves the model and ONNX Runtime from the local mirrors:
131
+
132
+ - **Prebuilt index.** In Chromium, Winzling embeds a passage in 123 ms on average and a query in 21 ms (p50, `bench.json`). Indexing 2,000 passages in the browser would take about 4 minutes, so `make docs` embeds them once in Node.js. `docs/data/vectors.bin.gz` holds the 4-bit TurboQuant index (0.51 MB raw, 431 kB gzipped); `docs/data/documents.json.gz` the passages and questions (438 kB gzipped). They are gzipped at build time, so every host sends the same bytes, and the worker unpacks them with the browser's `DecompressionStream`.
133
+ - **No bundler on the page.** `examples/search-worker.ts` becomes one 294 KB file, `docs/assets/search-worker.js`. ONNX Runtime's `onnxruntime-web-use-extern-wasm` export condition keeps its WASM out of the bundle; the worker fetches it from jsDelivr at the installed version, and the model from Hugging Face. Both answer cross-origin requests.
134
+ - **Downloads on request.** Typing alone downloads nothing; the model downloads with the first search (the Search button, an example query) or "Load the model". The worker keeps the three files in the Cache API, and a reload loads them from there with no model request.
135
+ - **Steady scroll.** Picking an example query keeps the scroll position, so the re-ranked results stay in view.
136
+
137
+ ## API
138
+
139
+ ### `createWinzlingEmbedder(options?)`
140
+
141
+ From `browser.js` (browser cache, `device: "wasm" | "webgpu"`) or `onnx.js` (isomorphic, adds `device: "cpu"` and `cacheDir` in Node.js).
142
+
143
+ ```ts
144
+ const embedder = createWinzlingEmbedder(options?);
145
+ const vectors = await embedder.embedDocuments(texts); // Float32Array[], 384 dims
146
+ const query = await embedder.embedQuery(text); // Float32Array
147
+ await embedder.dispose();
148
+ ```
149
+
150
+ Methods: `load()`, `embed()`, `embedOne()`, `embedDocuments()`, `embedQuery()`, `embedQueries()`, `dispose()`. `load()` is optional; inference initializes lazily. Embeddings are `Float32Array`s. Empty batches return `[]`; empty strings are encoded with the model's special tokens. Query whitespace is preserved. Nonempty `instruction` or `preset` options are rejected for Winzling.
151
+
152
+ | Option | Default / behavior |
153
+ |---|---|
154
+ | `device` | `"wasm"`; `"cpu"` is Node-only; `"webgpu"` is experimental |
155
+ | `batchSize` | 8; bounded microbatches with right padding |
156
+ | `maxLength` | 8192 tokens including BOS/EOS; valid range 2โ€“8192 |
157
+ | `truncate` | `false`; excess length throws. `true` preserves terminal EOS |
158
+ | `normalize` | `true`; per-call `normalize: false` returns the mean vector |
159
+ | `modelBaseUrl` | Optional HTTP(S) mirror of the pinned release |
160
+ | `loadFile` | Optional `(relativePath) => Promise<Uint8Array>` overriding all asset I/O |
161
+ | `cache` | `true`; filesystem in Node; Cache API + IndexedDB in browsers |
162
+ | `cacheDir` | Node OS temporary directory / `defuss-vectorsearch` |
163
+ | `allowRemoteModels` | `true`; `false` requires cached files unless `loadFile` is supplied |
164
+ | `wasmPaths` | ORT default; self-hosting example: `"/ort/"` |
165
+ | `numThreads` | 1; avoids a cross-origin-isolation requirement for WASM |
166
+
167
+ **Concurrency and lifecycle:** concurrent calls are serialized per instance. `dispose()` waits for accepted work, releases the session, is idempotent, and rejects subsequent inference. Initialization failures can be retried. There is no hosted-inference fallback.
168
+
169
+ **Cache keys:** the provider fingerprint identifies the preset. Include `maxLength`, `truncate`, and per-call normalization overrides in application embedding-cache keys.
170
+
171
+ ### TurboQuant
172
+
173
+ ```ts
174
+ import { buildTurboQuantIndex, searchTurboQuantIndex, searchTurboQuantIndexRerank } from "defuss-vectorsearch/turboquant.js";
175
+
176
+ const index = buildTurboQuantIndex(vectors); // options: { clip = 3.0, seed = 0x12345678 }
177
+ const hits = searchTurboQuantIndex(index, query, 10); // SearchHit[]: { index, score }, best first
178
+ const { rerankedTopK } = searchTurboQuantIndexRerank(index, vectors, query, 100, 10);
179
+ ```
180
+
181
+ - `buildTurboQuantIndex(vectors, options?)` pads the dimensions to the next power of two (384 โ†’ 512, 640 โ†’ 1024) and packs two 4-bit codes per byte. All vectors must share one dimensionality.
182
+ - `searchTurboQuantIndex(index, query, k)` scores every code against the query without reconstructing dense vectors, then returns the top `k`.
183
+ - `searchTurboQuantIndexRerank(index, vectors, query, approximateK, rerankK)` re-scores the approximate top `approximateK` with exact dot products against the float32 `vectors` and keeps `rerankK`. It needs those vectors in memory.
184
+
185
+ `searchTopK(haystack, needle, k)` from `vector-search.js` is the exact baseline: a dot product against every vector.
186
+
187
+ ### `createEmbeddingServer(options?)` / `createEmbeddingClient(options?)`
188
+
189
+ ```ts
190
+ import { createEmbeddingServer, WINZLING_MODEL_ID } from "defuss-vectorsearch/server.js";
191
+
192
+ const embedder = createEmbeddingServer({
193
+ model: WINZLING_MODEL_ID,
194
+ device: "cpu", // use "wasm" for portable CPU execution
195
+ winzling: { batchSize: 8 },
196
+ });
197
+ try {
198
+ const vector = await embedder.embedQuery("Eine Suchanfrage");
199
+ console.log(vector.length); // 384
200
+ } finally {
201
+ await embedder.dispose();
202
+ }
203
+ ```
204
+
205
+ The browser equivalent is `createEmbeddingClient` from `client.js`. Known model IDs select their profile automatically. A Winzling mirror can use `{ model: "https://example.com/models/winzling", modelProfile: "winzling" }`. The folder must contain the unchanged pinned files at their original paths. Use `WINZLING_MODEL_ID`, `WINZLING_PROFILE`, and `SUPPORTED_MODELS` for discovery.
206
+
207
+ Winzling's `dtype: "q4"` is selected automatically; its exact graph filename is `onnx/model_uint4.onnx`. There is no external `.onnx_data` file. Arbitrary model revisions, other dtypes, and non-mean pooling are not supported by this preset.
208
+
209
+ The wrapper APIs take the `createWinzlingEmbedder` options inside `winzling: { ... }`; their existing top-level `device`, `normalize`, `cacheDir`, and `allowRemoteModels` settings are also supported. Prefer the top-level settings when using the wrapper APIs. For filesystem assets, use `winzling.loadFile`, not the legacy Transformers.js `localModelPath` option.
210
+
211
+ Legacy `prefetchModel()`, `inspectModelCache()`, and `clearModelCache()` know the Winzling asset manifest. Actual inference verifies all three asset hashes, including cache hits. `loadFile` bypasses this cache, so cache-management methods do not inspect an application's custom loader/storage.
212
+
213
+ ### Supported models
214
+
215
+ | Model | Runtime | Pooling / queries |
216
+ |---|---|---|
217
+ | `kyr0/Winzling-Embed-a8m-64k` | Direct ONNX + `@huggingface/tokenizers` | Masked mean; unchanged, unprefixed input |
218
+ | `tss-deposium/harrier-oss-v1-270m-onnx-int8` | Existing Transformers.js pipeline | Existing last-token pooling and instruction formatting |
219
+ | `onnx-community/harrier-oss-v1-270m-ONNX` | Existing Transformers.js pipeline, `dtype: "q4"` | Existing Harrier behavior |
220
+
221
+ The no-argument client/server factory defaults to Harrier. Explicit OpenAI-compatible endpoints and the vector-search exports are retained. See [the preserved upstream README](docs/UPSTREAM_README.md) for those APIs and historical benchmarks; those benchmark results were not reproduced for this release.
222
+
223
+ ### Runtime and deployment
224
+
225
+ Native Node CPU uses `onnxruntime-node@1.21.0`, matching the retained Transformers.js dependency so two incompatible native ORT libraries are not loaded into one process. The native peer is optional; the development checkout installs it. ORT Web is pinned independently to `1.30.0` and the tokenizer to `0.2.0`.
226
+
227
+ The included demo runs tokenization and inference in a dedicated module Worker, serves all assets from localhost, and uses exact cosine similarity for display. Do not enable ORT's proxy-worker mode for WebGPU. The provider sets it to false. WASM paths and thread settings are global ORT configuration: use consistent settings across instances and configure other users of ORT before initialization. HTTPS or localhost is needed for browser cryptography, storage, and WebGPU APIs.
228
+
229
+ WebGPU requests require a usable adapter; absence throws instead of quietly selecting CPU. When an adapter is available, the session requests WebGPU with WASM for unsupported operators. Hardware WebGPU inference was **not verified** in this environment. CPU paths are the validated defaults. GPU token states are currently read back for pooling in TypeScript; GPU graph pooling is deferred.
230
+
231
+ 8192 is the model's context limit, not a promise of low memory use at that length. Long inputs can create large attention tensors. For retrieval, chunk upstream or choose a smaller explicit `maxLength`; automatic chunking is outside this package.
232
+
233
+ ## Benchmark
234
+
235
+ ```sh
236
+ make bench # downloads bench/dataset.json once (make download-bench), then writes bench.json and the tables below
237
+ ```
238
+
239
+ The benchmark uses [tiny-embedding-bench-v1](https://github.com/kyr0/tiny-embedding-bench/tree/main/datasets/tiny-embedding-bench-v1):
240
+ 100 Belebele passages and 50 questions, each in 20 languages, with one gold passage per question.
241
+ `download-bench` checks the file against the release's pinned SHA-256.
242
+
243
+ - **Scope.** Overall R@k and *Rank ยฑ10%* count only queries in the nine target languages: `deu_Latn`, `rus_Cyrl`, `eng_Latn`, `fra_Latn`, `ind_Latn`, `nld_Latn`, `ita_Latn`, `por_Latn`, `spa_Latn` (`TARGET_LANGUAGES` in `scripts/bench/dataset.ts`). That is 450 of the 1,000 queries. The other 11 languages' passages stay in the haystack as distractors. `bench.json` keeps per-language results for all 20 languages, and its `overall` fields average all 20; the README derives the nine-language values from the per-language ones. Timing and memory cover all 2,000 passages and 1,000 queries.
244
+ - **Haystack.** All 2,000 localized passages form one index. A query also competes with the 19
245
+ translations of its own gold passage. The target is the gold passage in the query's language,
246
+ so R@100 measures a 5% slice of the haystack.
247
+ - **Strategies.** *Bruteforce* is a dot product of the query against every normalized passage vector:
248
+ float32, no quantization, no ANN. *TurboQuant* scores the same vectors from the 4-bit index
249
+ (`buildTurboQuantIndex`) without exact reranking.
250
+ - **Metrics.**
251
+ - R@k is the share of queries whose target ranks within the top k.
252
+ - *Rank ยฑ10%* is the share of queries where TurboQuant places the target within ยฑ10% of its bruteforce rank. An exact rank below 10 must be matched exactly.
253
+ - Embedding time is the mean per text, one text per call, on one warm session per model.
254
+ - Search time is the mean per query for the top 100.
255
+ - Passage ms per method is the passage's embedding time plus an equal share of that method's index build: normalization for bruteforce, 4-bit quantization for TurboQuant.
256
+ - Query ms per method is the query's embedding time plus its own search time.
257
+ - Model runtime memory is the growth in resident memory over load and embedding, measured after a forced GC, minus the embedding arrays held at that point. That is process RSS in Node.js and renderer RSS in Chromium.
258
+ - Runtime MiB per method is the model runtime plus the exact index size. Both indexes live in one process, and their difference (0.5 to 4 MiB) is smaller than RSS noise, so it is added rather than measured.
259
+ - **Environments.**
260
+ - Node.js runs the built package on native ONNX Runtime (`device: "cpu"`). Winzling uses 1 thread. Harrier uses ONNX Runtime's default thread count, because Transformers.js sets none.
261
+ - Chromium runs the same build on ONNX Runtime Web (`device: "wasm"`, 1 thread) in headless Playwright. It loads the model files the Node run cached in `bench/cache/`.
262
+
263
+ <!-- bench:start -->
264
+ Generated by `make bench` on 2026-10-07 ยท Apple M4, 10 cores, darwin arm64.
265
+ Runtimes: Node.js v24.14.0; Chromium 140.0.7339.16 headless (Playwright).
266
+
267
+ ### Embedding
268
+
269
+ One text per call, warm session; model runtime = memory growth over load + embedding, without the held vectors:
270
+
271
+ | Environment | Model | Device | Load ms | Embed passage ms | Embed query ms | Model runtime MiB |
272
+ | --- | --- | --- | ---: | ---: | ---: | ---: |
273
+ | Node.js | Winzling | cpu | 212 | 54.2 (p95 172.1) | 11.1 (p95 29.9) | 233.31 |
274
+ | Node.js | Harrier | cpu | 1440 | 37.2 (p95 52.2) | 20.7 (p95 24.9) | 497.61 |
275
+ | Chromium | Winzling | wasm | 685 | 123.1 (p95 375.5) | 29.7 (p95 66.8) | 205.87 |
276
+ | Chromium | Harrier | wasm | 2478 | 347.8 (p95 480.3) | 162.7 (p95 215.5) | 230.38 |
277
+
278
+ ### Retrieval Quality
279
+
280
+ The 9 target languages: `deu_Latn`, `rus_Cyrl`, `eng_Latn`, `fra_Latn`, `ind_Latn`, `nld_Latn`, `ita_Latn`, `por_Latn`, `spa_Latn`; their 450 queries against all 2000 passages in 20 languages; R@k = % of queries whose gold passage ranks in the top k:
281
+
282
+ | Environment | Model | Strategy | R@3 | R@5 | R@25 | R@100 | Rank ยฑ10% |
283
+ | --- | --- | --- | ---: | ---: | ---: | ---: | ---: |
284
+ | Node.js | Winzling | bruteforce | 83.8 | 92.2 | 98.9 | 99.3 | n/a |
285
+ | Node.js | Winzling | TurboQuant | 82.9 | 91.6 | 98.9 | 99.3 | 88.9% |
286
+ | Node.js | Harrier | bruteforce | 98.9 | 99.3 | 100 | 100 | n/a |
287
+ | Node.js | Harrier | TurboQuant | 98.9 | 99.3 | 100 | 100 | 98.7% |
288
+ | Chromium | Winzling | bruteforce | 83.8 | 92.2 | 98.9 | 99.3 | n/a |
289
+ | Chromium | Winzling | TurboQuant | 82.9 | 91.6 | 98.9 | 99.3 | 88.9% |
290
+ | Chromium | Harrier | bruteforce | 98.4 | 99.3 | 100 | 100 | n/a |
291
+ | Chromium | Harrier | TurboQuant | 98.9 | 99.1 | 100 | 100 | 97.3% |
292
+
293
+ ### Cost per Method
294
+
295
+ Passage = embed + share of the index build; query = embed + top-100 search; runtime = model runtime + index:
296
+
297
+ | Environment | Model | Strategy | Passage ms | Query ms | Search ms mean | Index build ms | Runtime MiB | Index MiB |
298
+ | --- | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |
299
+ | Node.js | Winzling | bruteforce | 54.2 (p95 172.1) | 11.5 (p95 30.3) | 0.405 | 5.3 | 236.24 | 2.93 |
300
+ | Node.js | Winzling | TurboQuant | 54.2 (p95 172.1) | 11.6 (p95 30.3) | 0.463 | 13.6 | 233.8 | 0.49 |
301
+ | Node.js | Harrier | bruteforce | 37.2 (p95 52.2) | 21.3 (p95 25.6) | 0.659 | 4.0 | 502.5 | 4.88 |
302
+ | Node.js | Harrier | TurboQuant | 37.3 (p95 52.2) | 21.7 (p95 25.9) | 0.977 | 24.6 | 498.59 | 0.98 |
303
+ | Chromium | Winzling | bruteforce | 123.2 (p95 375.5) | 30.1 (p95 67.1) | 0.382 | 1.8 | 208.8 | 2.93 |
304
+ | Chromium | Winzling | TurboQuant | 123.2 (p95 375.5) | 30.1 (p95 67.3) | 0.455 | 10.9 | 206.36 | 0.49 |
305
+ | Chromium | Harrier | bruteforce | 347.8 (p95 480.3) | 163.3 (p95 216.1) | 0.629 | 4.8 | 235.26 | 4.88 |
306
+ | Chromium | Harrier | TurboQuant | 347.8 (p95 480.3) | 163.6 (p95 216.4) | 0.946 | 21.9 | 231.36 | 0.98 |
307
+
308
+ ### Per Language, Node.js
309
+
310
+ Target languages, best first by Winzling bruteforce; cells: R@3 / R@5 / R@25 / R@100; Chromium and all 20 languages are in `bench.json`:
311
+
312
+ | Language | Winzling bruteforce | Winzling TurboQuant | Harrier bruteforce | Harrier TurboQuant |
313
+ | --- | --- | --- | --- | --- |
314
+ | `deu_Latn` | 92 / 96 / 100 / 100 | 92 / 94 / 100 / 100 | 98 / 98 / 100 / 100 | 98 / 98 / 100 / 100 |
315
+ | `rus_Cyrl` | 92 / 94 / 98 / 100 | 92 / 94 / 98 / 100 | 98 / 98 / 100 / 100 | 98 / 98 / 100 / 100 |
316
+ | `eng_Latn` | 90 / 100 / 100 / 100 | 90 / 100 / 100 / 100 | 100 / 100 / 100 / 100 | 100 / 100 / 100 / 100 |
317
+ | `fra_Latn` | 84 / 94 / 100 / 100 | 84 / 92 / 100 / 100 | 100 / 100 / 100 / 100 | 100 / 100 / 100 / 100 |
318
+ | `ind_Latn` | 84 / 92 / 96 / 96 | 76 / 92 / 96 / 96 | 98 / 100 / 100 / 100 | 98 / 100 / 100 / 100 |
319
+ | `nld_Latn` | 82 / 90 / 98 / 98 | 82 / 88 / 98 / 98 | 98 / 100 / 100 / 100 | 98 / 100 / 100 / 100 |
320
+ | `ita_Latn` | 80 / 86 / 100 / 100 | 84 / 90 / 100 / 100 | 100 / 100 / 100 / 100 | 100 / 100 / 100 / 100 |
321
+ | `por_Latn` | 76 / 94 / 100 / 100 | 74 / 92 / 100 / 100 | 100 / 100 / 100 / 100 | 100 / 100 / 100 / 100 |
322
+ | `spa_Latn` | 74 / 84 / 98 / 100 | 72 / 82 / 98 / 100 | 98 / 98 / 100 / 100 | 98 / 98 / 100 / 100 |
323
+
324
+ Node.js vs Chromium vectors for the same passages: Winzling min cosine 1 over 40 passages; Harrier min cosine 0.98881 over 40 passages.
325
+ <!-- bench:end -->
326
+
327
+ ## Findings
328
+
329
+ `make bench` regenerates the tables above. This analysis is written by hand and cites the 2026-10-07 run.
330
+
331
+ ### Index Size
332
+
333
+ VERIFIED: TurboQuant stores 5 to 6 times less than bruteforce. Bruteforce keeps a float32 vector per passage. TurboQuant keeps 4-bit codes over the dimensions padded to a power of two (384 โ†’ 512, 640 โ†’ 1024). The index grows with the corpus while the model stays fixed, so the gap matters most in browsers. It decides the IndexedDB footprint, what must be downloaded or synced, and what stays resident.
334
+
335
+ | Model | Bruteforce per vector | TurboQuant per vector | 2,000 passages (measured) | 100,000 passages (projected) |
336
+ |---|---:|---:|---:|---:|
337
+ | Winzling (384 dims) | 1,536 B | 256 B | 2.93 โ†’ 0.49 MiB | 146.5 โ†’ 24.4 MiB |
338
+ | Harrier (640 dims) | 2,560 B | 512 B | 4.88 โ†’ 0.98 MiB | 244.1 โ†’ 48.8 MiB |
339
+
340
+ At 2,000 passages the model's 200 to 500 MiB runtime still dwarfs either index.
341
+
342
+ ### Download Size
343
+
344
+ VERIFIED: measured from the cached model files and from the Chromium page's network requests up to the "loaded" event. MB = 10โถ bytes.
345
+
346
+ | Model | Model files | Of which ONNX graph | Of which tokenizer | Browser runtime WASM (uncompressed) |
347
+ |---|---:|---:|---:|---:|
348
+ | Winzling | 39.76 MB (37.92 MiB) | 30.46 MB | 9.29 MB | 26.78 MB, ONNX Runtime Web 1.30 asyncify build, self-hosted from `/ort/` |
349
+ | Harrier | 304.68 MB (290.56 MiB) | 270.14 MB | 34.54 MB | 21.60 MB, the ONNX Runtime Web build bundled with Transformers.js 3.8.1, loaded from jsDelivr |
350
+
351
+ - Hugging Face sends the model files uncompressed: no `Content-Encoding` header, and `Content-Length` equals the file size.
352
+ - A server that compresses them saves little on the graph and most of the tokenizer. Winzling's three files total 28.37 MB with `brotli -q 11`, 28.59 MB with `zstd -19` and 29.02 MB with `gzip -9` (graph 27.50 MB, tokenizer 0.86 MB with brotli).
353
+ - jsDelivr sends Harrier's runtime WASM brotli-compressed, 4.11 MB on the wire. A self-hosted `/ort/` gets the same saving only if its server compresses `.wasm` files.
354
+ - Node.js downloads only the model files. Its ONNX Runtime ships as a native npm binary.
355
+ - Winzling's sizes are fixed by its pinned revision. Harrier loads its repository's `main` branch, so its files can change.
356
+ - Node.js caches every file on the filesystem, so later loads skip the network. In headless Chromium, Transformers.js could not store Harrier's 270 MB graph in the Cache API ("Unexpected internal error"). Whether a regular browser profile keeps it between visits was not tested.
357
+
358
+ ### Quality, Speed, and Caveats
359
+
360
+ **Quality.** VERIFIED: over the nine target languages, TurboQuant without reranking stays within 0.9 points of bruteforce at every R@k, for both models in both environments, and is sometimes slightly ahead. The largest gap is Winzling R@3 (82.9 vs 83.8). From R@25 on, both strategies agree for both models. Harrier keeps the gold passage within ยฑ10% of its bruteforce rank for 97 to 99% of queries, Winzling for 89%.
361
+
362
+ **Speed.** VERIFIED: TurboQuant gives no speed advantage at 2,000 passages. Its search is slightly slower than bruteforce (Winzling 0.46 vs 0.40 ms, Harrier 0.98 vs 0.66 ms in Node.js). Embedding dominates both: a query costs 11 to 164 ms end to end, and the search method changes that by under 0.4 ms.
363
+
364
+ **Caveats.**
365
+
366
+ - VERIFIED: the memory saving holds only without exact reranking. `searchTurboQuantIndexRerank` re-scores candidates against the float32 vectors, so it needs them in memory. To keep the small footprint, use `searchTurboQuantIndex`, or store the float32 vectors elsewhere (IndexedDB, a server) and fetch only the top candidates. The benchmark measured `searchTurboQuantIndex` only, with no reranking variant.
367
+ - VERIFIED: the evidence base is small. Overall recall rests on 450 target-language queries against 2,000 passages, so a 0.9-point gap is 4 queries, on one dataset.
368
+ - VERIFIED: nothing here covers corpora larger than 2,000 passages. [docs/BENCHMARK.md](docs/BENCHMARK.md) lists the open questions for larger corpora and how to test them.
369
+
370
+ ## When to use it
371
+
372
+ Winzling gives up top-3 precision (R@3 83.8% against Harrier's 98.9%) for a download 7.7 times smaller (39.76 MB against 304.68 MB), and TurboQuant shrinks its index 6 times.
373
+
374
+ ### Use it for
375
+
376
+ - **Search on the visitor's device:** the browser embeds and searches the query itself; the live demo makes no network request during a search
377
+ - **Corpora you can index ahead of time:** the demo embeds its 2,000 passages once at build time (`make docs`) instead of in each visitor's browser, where that would take about 4 minutes, and ships them as a 431 kB index
378
+ - **German, Russian and English:** R@5 of 96, 94 and 100% (Node.js, bruteforce); the other six target languages reach 84 to 94%
379
+ - **Hybrid search:** combined with `defuss-search`, which offers exact, phonetic and fuzzy search plus rank fusion with vector search, it covers structured and unstructured data in the browser and in Node.js
380
+
381
+ ### Use something else for
382
+
383
+ - **Top-3 precision in all nine languages:** Harrier reaches R@3 98.9% against Winzling's 83.8%; use it through `client.js` or `server.js`
384
+ - **Faster search than bruteforce:** TurboQuant saves memory and download, not time, at 2,000 passages
385
+ - **Exact reranking at TurboQuant's footprint:** `searchTurboQuantIndexRerank` needs the float32 vectors in memory
386
+ - **Long documents as one vector:** the 8192-token context limit is no promise of low memory use; chunk upstream
387
+ - **Corpora beyond 2,000 passages:** recall, speed and memory there are not measured
388
+ - **GPU inference you rely on:** WebGPU on real hardware is not verified
389
+
390
+ ### Key principles
391
+
392
+ 1. **Embed documents and queries with the same model.** The demo's index records the model ID, revision and fingerprint its passages were embedded with, and its queries use that model.
393
+ 2. **Load the model before the first query when 39.8 MB is acceptable.** `load()` starts the download and session; otherwise the first `embedQuery` pays for it. The live demo waits for the first search, so typing alone downloads nothing.
394
+ 3. **Run the model in a Worker.** Both demos run tokenization and inference in a dedicated module Worker and post results to the page. The live demo's worker keeps only the newest pending query instead of queueing every keystroke.
395
+ 4. **Reuse one embedder.** Each instance holds one ONNX Runtime session and serializes its calls; `dispose()` releases it.
396
+
397
+ ## Patterns
398
+
399
+ ### Prebuilt index, browser search
400
+
401
+ The live demo indexes in Node.js and searches in the browser:
402
+
403
+ ```mermaid
404
+ flowchart LR
405
+ A["scripts/docs/build-index.ts<br/>Node.js, native CPU"] -->|"encodeDemoIndex + gzip"| B[("docs/data/<br/>documents.json.gz<br/>vectors.bin.gz")]
406
+ B -->|"fetch + DecompressionStream"| C["examples/search-worker.ts<br/>decodeDemoIndex"]
407
+ C --> D["searchTurboQuantIndex"]
408
+ ```
409
+
410
+ `scripts/docs/index-format.ts` stores the passages and the TurboQuant header as JSON, and the signs and code bytes as one binary file. Binary instead of base64 in JSON keeps the vectors at 431 kB gzipped instead of 475 kB. `scripts/docs/index-format.test.ts` checks the round trip.
411
+
412
+ ### Model files from disk
413
+
414
+ ```ts
415
+ import { readFile } from "node:fs/promises";
416
+ import { createWinzlingEmbedder } from "defuss-vectorsearch/onnx.js";
417
+
418
+ const embedder = createWinzlingEmbedder({
419
+ device: "cpu",
420
+ loadFile: async (file) => new Uint8Array(await readFile(`public/models/winzling/${file}`)),
421
+ });
422
+ ```
423
+
424
+ `loadFile` replaces all asset I/O, and the embedder still checks every file's SHA-256. `scripts/test-package.ts` runs this against both the ESM and the CJS build. In the browser, `modelBaseUrl` and `wasmPaths` serve the same purpose for a self-hosted mirror (`examples/browser-worker.ts`).
425
+
426
+ ## What "verified" means
427
+
428
+ ```sh
429
+ make verify # lint โ†’ test โ†’ coverage โ†’ e2e; CI runs the same after `make setup`
430
+ ```
431
+
432
+ - `make lint`: oxlint with `--deny-warnings`
433
+ - `make test`: the Node and Chromium regression suites, and real Winzling inference on native CPU and in the browser
434
+ - `make coverage`: the Node suite's line coverage
435
+ - `make e2e`: builds ESM/CJS/declarations, typechecks the examples and scripts against them, exercises the built package exports, builds the `browser.js` bundle and runs it in Chromium, tests the Worker demo (report in `output/`), builds the demo, rebuilds the static demo's worker and drives `docs/` in Chromium
436
+ - `make bench`: regenerates `bench.json` and the [benchmark](#benchmark) tables
437
+
438
+ Individual commands are listed in `Makefile` and `package.json`.
439
+
440
+ The checked-in oracle contains **24 multilingual and adversarial cases** made with Rust tokenizers and Python native ONNX Runtime. Tests require exact token-ID agreement and compare normalized embeddings within `2e-5` maximum absolute error, including padded batches against independently generated singleton vectors. Fixtures cover Unicode, whitespace, special tokens, empty text, and byte fallback. Other tests cover pooling, normalization, explicit truncation, corruption, initialization retry, concurrent calls, disposal, and offline browser-cache reuse.
441
+
442
+ To regenerate the independent oracle:
443
+
444
+ ```sh
445
+ python -m pip install -r scripts/reference-requirements.txt
446
+ python scripts/generate-reference.py
447
+ ```
448
+
449
+ See [source provenance](docs/SOURCE_PROVENANCE.json) and [model details and licensing](docs/MODEL.md). This verifies runtime behavior, not retrieval quality on your application's corpus.
450
+
451
+ ### Deliberate limits
452
+
453
+ No automatic chunking, no hosted-inference fallback, no index serialization format in the package. The Winzling preset loads one pinned revision with `dtype: "q4"` and mean pooling only. `browser.js` is ESM-only, and Vite is the only tested bundler. WebGPU was not verified on hardware, and GPU token states are read back for pooling in TypeScript. The retained legacy dependencies stay in the installation footprint, although `onnx.js` and `browser.js` do not import Transformers.js. Implementation history lives in [CHANGELOG.md](CHANGELOG.md).
454
+
455
+ ## Requirements
456
+
457
+ - **Node.js** 22.18 or later (`engines`), which runs the TypeScript examples through native type stripping; tested with Node 24.19.0 on Linux x64 and Node 24.14.0 on macOS arm64
458
+ - **Bun** 1.4.2 (`packageManager`) installs dependencies and runs the scripts
459
+ - **Native CPU in Node.js:** the optional peer `onnxruntime-node@1.21.0`
460
+ - **Browser:** tested in headless Chromium 140 (Playwright); HTTPS or localhost for browser cryptography, storage and WebGPU
461
+
462
+ ## Citation
463
+
464
+ If you use defuss-vectorsearch in research or want to reference it, cite it as:
465
+
466
+ ```bibtex
467
+ @misc{homberg2026defussvectorsearch,
468
+ author = {Homberg, Aron},
469
+ affiliation = {Independent Researcher},
470
+ title = {defuss-vectorsearch: Isomorphic Multilingual Embeddings and 4-Bit TurboQuant Vector Search for JavaScript},
471
+ year = {2026},
472
+ version = {0.1.0},
473
+ howpublished = {\url{https://github.com/kyr0/defuss-vectorsearch}},
474
+ note = {MIT License}
475
+ }
476
+ ```
477
+
478
+ ## License
479
+
480
+ MIT, see [LICENSE](LICENSE).
@@ -0,0 +1,18 @@
1
+ import { W as WinzlingEmbedderCore, a as WinzlingOptions } from './winzling-embedder-Dqq64fVb.mjs';
2
+ export { S as SearchHit, T as TurboQuantRerankResult, b as TurboQuantRerankedSearchHit, c as TurboQuantSearchIndex, V as Vectors, d as WinzlingAssetCache, e as WinzlingAssetLocation, p as poolWinzling } from './winzling-embedder-Dqq64fVb.mjs';
3
+ export { buildTurboQuantIndex, rerankSearchHits, scoreTurboQuantIndex, searchTurboQuantIndex, searchTurboQuantIndexRerank, selfCheckTurboQuant } from './turboquant.mjs';
4
+ export { n as normalizeVector, a as normalizeVectors } from './vector-math-C29mf_rv.mjs';
5
+ export { W as WINZLING_FILES, a as WINZLING_MODEL_ID, b as WINZLING_PROFILE, c as WINZLING_REVISION } from './model-profiles-BDS-uZ4e.mjs';
6
+
7
+ declare class WinzlingEmbedder extends WinzlingEmbedderCore {
8
+ constructor(options?: Omit<WinzlingOptions, "device" | "cacheDir"> & {
9
+ device?: "wasm" | "webgpu" | undefined;
10
+ });
11
+ }
12
+ /**
13
+ * Downloads the pinned Winzling release from Hugging Face on first use (SHA-256 checked), caches it in the
14
+ * Cache API and IndexedDB, and reuses one ONNX Runtime Web session for every call.
15
+ */
16
+ declare const createWinzlingEmbedder: (options?: ConstructorParameters<typeof WinzlingEmbedder>[0]) => WinzlingEmbedder;
17
+
18
+ export { WinzlingEmbedder, WinzlingOptions, createWinzlingEmbedder };
@@ -0,0 +1,22 @@
1
+ import { s as storeBrowserCachedModelFile, l as loadBrowserCachedModelFile } from './model-cache.browser-BVWrcI3Y.mjs';
2
+ export { W as WINZLING_FILES, d as WINZLING_MODEL_ID, e as WINZLING_PROFILE, f as WINZLING_REVISION } from './model-cache.browser-BVWrcI3Y.mjs';
3
+ import { W as WinzlingEmbedderCore } from './winzling-embedder-CthXBDRS.mjs';
4
+ export { p as poolWinzling } from './winzling-embedder-CthXBDRS.mjs';
5
+ export { buildTurboQuantIndex, rerankSearchHits, scoreTurboQuantIndex, searchTurboQuantIndex, searchTurboQuantIndexRerank, selfCheckTurboQuant } from './turboquant.mjs';
6
+ export { n as normalizeVector, b as normalizeVectors } from './vector-math-DwdA_xwv.mjs';
7
+ import 'defuss-db';
8
+ import 'defuss-db/client.js';
9
+ import '@huggingface/tokenizers';
10
+
11
+ const browserCache = {
12
+ load: async ({ cacheKey, remoteUrl }) => (await loadBrowserCachedModelFile(cacheKey, remoteUrl))?.bytes ?? null,
13
+ store: ({ cacheKey, remoteUrl }, file) => storeBrowserCachedModelFile({ ...file, cacheKey, remoteUrl })
14
+ };
15
+ class WinzlingEmbedder extends WinzlingEmbedderCore {
16
+ constructor(options = {}) {
17
+ super(options, browserCache);
18
+ }
19
+ }
20
+ const createWinzlingEmbedder = (options = {}) => new WinzlingEmbedder(options);
21
+
22
+ export { WinzlingEmbedder, createWinzlingEmbedder };
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ var core = require('./core-DaRhdYmA.cjs');
4
+ var modelCache = require('./model-cache-qTj4Dnw0.cjs');
5
+ var prompts = require('./prompts-Cq8h-R6V.cjs');
6
+ var vectorSearch = require('./vector-search.cjs');
7
+ var turboquant = require('./turboquant.cjs');
8
+ var vectorMath = require('./vector-math-D8tpjoQx.cjs');
9
+ require('./onnx.cjs');
10
+ require('@huggingface/tokenizers');
11
+ require('defuss-db');
12
+ require('defuss-db/client.js');
13
+
14
+ class DefussEmbeddingClient extends core.BaseEmbeddingRuntime {
15
+ constructor(options = {}) {
16
+ super(options, { allowLocalModels: false, useBrowserCache: true });
17
+ }
18
+ }
19
+ const createEmbeddingClient = (options = {}) => {
20
+ return new DefussEmbeddingClient(options);
21
+ };
22
+
23
+ exports.BaseEmbeddingRuntime = core.BaseEmbeddingRuntime;
24
+ exports.DEFAULT_MODEL_ID = modelCache.DEFAULT_MODEL_ID;
25
+ exports.DEFAULT_MODEL_REMOTE_HOST = modelCache.DEFAULT_MODEL_REMOTE_HOST;
26
+ exports.DEFAULT_MODEL_REMOTE_PATH_TEMPLATE = modelCache.DEFAULT_MODEL_REMOTE_PATH_TEMPLATE;
27
+ exports.DEFAULT_TRANSFORMERS_CACHE_NAME = modelCache.DEFAULT_TRANSFORMERS_CACHE_NAME;
28
+ exports.LEGACY_MODEL_ID = modelCache.LEGACY_MODEL_ID;
29
+ exports.SUPPORTED_MODELS = modelCache.SUPPORTED_MODELS;
30
+ exports.WINZLING_FILES = modelCache.WINZLING_FILES;
31
+ exports.WINZLING_MODEL_ID = modelCache.WINZLING_MODEL_ID;
32
+ exports.WINZLING_PROFILE = modelCache.WINZLING_PROFILE;
33
+ exports.WINZLING_REVISION = modelCache.WINZLING_REVISION;
34
+ exports.buildNodeCacheKey = modelCache.buildNodeCacheKey;
35
+ exports.buildRemoteModelFileUrl = modelCache.buildRemoteModelFileUrl;
36
+ exports.getRequiredModelFiles = modelCache.getRequiredModelFiles;
37
+ exports.pathJoin = modelCache.pathJoin;
38
+ exports.resolveModelSource = modelCache.resolveModelSource;
39
+ exports.DEFAULT_QUERY_INSTRUCTION_PRESET = prompts.DEFAULT_QUERY_INSTRUCTION_PRESET;
40
+ exports.QUERY_INSTRUCTION_PRESETS = prompts.QUERY_INSTRUCTION_PRESETS;
41
+ exports.clearModelCache = prompts.clearModelCache;
42
+ exports.formatInstructionQuery = prompts.formatInstructionQuery;
43
+ exports.inspectModelCache = prompts.inspectModelCache;
44
+ exports.prefetchModel = prompts.prefetchModel;
45
+ exports.resolveQueryInstruction = prompts.resolveQueryInstruction;
46
+ exports.attachRecords = vectorSearch.attachRecords;
47
+ exports.searchTopKMulticore = vectorSearch.searchTopKMulticore;
48
+ exports.buildTurboQuantIndex = turboquant.buildTurboQuantIndex;
49
+ exports.rerankSearchHits = turboquant.rerankSearchHits;
50
+ exports.scoreTurboQuantIndex = turboquant.scoreTurboQuantIndex;
51
+ exports.searchTurboQuantIndex = turboquant.searchTurboQuantIndex;
52
+ exports.searchTurboQuantIndexRerank = turboquant.searchTurboQuantIndexRerank;
53
+ exports.selfCheckTurboQuant = turboquant.selfCheckTurboQuant;
54
+ exports.dotProduct = vectorMath.dotProduct;
55
+ exports.dotProductJS = vectorMath.dotProductJS;
56
+ exports.normalizeVector = vectorMath.normalizeVector;
57
+ exports.normalizeVectors = vectorMath.normalizeVectors;
58
+ exports.searchTopK = vectorMath.searchTopK;
59
+ exports.toFloat32 = vectorMath.toFloat32;
60
+ exports.topKFromScores = vectorMath.topKFromScores;
61
+ exports.DefussEmbeddingClient = DefussEmbeddingClient;
62
+ exports.createEmbeddingClient = createEmbeddingClient;
@@ -0,0 +1,15 @@
1
+ import { B as BaseEmbeddingRuntime } from './core-UGRF9Eqm.cjs';
2
+ import { E as EmbedderInitOptions } from './winzling-embedder-Dqq64fVb.cjs';
3
+ export { B as BuildTurboQuantIndexOptions, f as EmbedOptions, g as Embedder, h as EmbeddingDType, i as EmbeddingDevice, j as ExactSearchMulticoreOptions, F as FeatureExtractorLike, M as ModelCacheClearFileResult, k as ModelCacheClearResult, l as ModelCacheFileStatus, m as ModelCacheInspectionResult, n as ModelCacheLocation, o as ModelCacheOptions, q as ModelLoadOptions, r as ModelPrefetchFile, s as ModelPrefetchOptions, t as ModelPrefetchResult, u as ModelSourceKind, O as OpenAICompatibleEmbeddingEndpointOptions, P as PoolingStrategy, Q as QueryEmbedOptions, v as QueryInstructionPreset, R as ResolvedModelSource, S as SearchHit, w as SearchResult, x as TensorLike, y as TransformersEnvLike, z as TransformersModuleLike, T as TurboQuantRerankResult, b as TurboQuantRerankedSearchHit, c as TurboQuantSearchIndex, A as Vector, V as Vectors } from './winzling-embedder-Dqq64fVb.cjs';
4
+ export { DEFAULT_MODEL_ID, DEFAULT_MODEL_REMOTE_HOST, DEFAULT_MODEL_REMOTE_PATH_TEMPLATE, DEFAULT_QUERY_INSTRUCTION_PRESET, DEFAULT_TRANSFORMERS_CACHE_NAME, LEGACY_MODEL_ID, QUERY_INSTRUCTION_PRESETS, buildNodeCacheKey, buildRemoteModelFileUrl, clearModelCache, formatInstructionQuery, getRequiredModelFiles, inspectModelCache, pathJoin, prefetchModel, resolveModelSource, resolveQueryInstruction } from './shared.cjs';
5
+ export { attachRecords, searchTopKMulticore } from './vector-search.cjs';
6
+ export { buildTurboQuantIndex, rerankSearchHits, scoreTurboQuantIndex, searchTurboQuantIndex, searchTurboQuantIndexRerank, selfCheckTurboQuant } from './turboquant.cjs';
7
+ export { S as SUPPORTED_MODELS, W as WINZLING_FILES, a as WINZLING_MODEL_ID, b as WINZLING_PROFILE, c as WINZLING_REVISION } from './model-profiles-BDS-uZ4e.cjs';
8
+ export { d as dotProduct, b as dotProductJS, n as normalizeVector, a as normalizeVectors, s as searchTopK, t as toFloat32, c as topKFromScores } from './vector-math-BDEYyzR7.cjs';
9
+
10
+ declare class DefussEmbeddingClient extends BaseEmbeddingRuntime {
11
+ constructor(options?: EmbedderInitOptions);
12
+ }
13
+ declare const createEmbeddingClient: (options?: EmbedderInitOptions) => DefussEmbeddingClient;
14
+
15
+ export { BaseEmbeddingRuntime, DefussEmbeddingClient, EmbedderInitOptions, createEmbeddingClient };