Content-similarity search surface over the CLIP index (closes #50) #72

Merged
clawbot merged 1 commits from issue-50-ml-search-redo into next 2026-09-22 20:45:59 +02:00
Collaborator

Redo of the reverted first cut of lib.mldata (#50) over the #49 CLIP index. Same scope:

  • forFile({ fileID }) — the stored ML payload from disk, or undefined.
  • similar({ fileID, limit? }) — nearest files by cosine over CLIP embeddings, the file itself excluded.
  • searchByEmbedding({ embedding, limit? }) — nearest files to a caller-supplied query vector; no text encoder is bundled (owner-deferred).

Cosine is one plain loop over the packed Float32Array index, RAM only, no new dependency. Results are { fileID, score }[] descending, ties broken by ascending fileID.

What the diff does not show: the revert (#69) happened because make check runs test/lint/fmt-check but not tsc, so the strict-config typing error in the cosine loops only surfaced in the CI docker build. Under noUncheckedIndexedAccess the indexed reads query[k], embeddings[base+k], and fileIDs[i] are number | undefined. Each read is provably in range by the surrounding bounds check (query length equals the embedding length, the buffer is packed fileIDs.length * embeddingLength), so they are asserted non-null rather than paying a per-element guard in the ~50k×512 hot loop. Gated on both make check and make build green.

Judgement call: score is returned alongside fileID (the issue asks only for ranked ids).

Model: opus-4-8

Redo of the reverted first cut of `lib.mldata` ([#50](https://git.eeqj.de/sneak/quak/issues/50)) over the [#49](https://git.eeqj.de/sneak/quak/issues/49) CLIP index. Same scope: - `forFile({ fileID })` — the stored ML payload from disk, or undefined. - `similar({ fileID, limit? })` — nearest files by cosine over CLIP embeddings, the file itself excluded. - `searchByEmbedding({ embedding, limit? })` — nearest files to a caller-supplied query vector; no text encoder is bundled (owner-deferred). Cosine is one plain loop over the packed `Float32Array` index, RAM only, no new dependency. Results are `{ fileID, score }[]` descending, ties broken by ascending fileID. What the diff does not show: the revert ([#69](https://git.eeqj.de/sneak/quak/pulls/69)) happened because `make check` runs test/lint/fmt-check but not `tsc`, so the strict-config typing error in the cosine loops only surfaced in the CI docker build. Under `noUncheckedIndexedAccess` the indexed reads `query[k]`, `embeddings[base+k]`, and `fileIDs[i]` are `number | undefined`. Each read is provably in range by the surrounding bounds check (query length equals the embedding length, the buffer is packed `fileIDs.length * embeddingLength`), so they are asserted non-null rather than paying a per-element guard in the ~50k×512 hot loop. Gated on **both** `make check` and `make build` green. Judgement call: `score` is returned alongside `fileID` (the issue asks only for ranked ids). Model: opus-4-8
clawbot added 1 commit 2026-09-22 20:33:48 +02:00
Adds lib.mldata search over the CLIP index (#49): forFile returns a
file's stored payload; similar ranks nearest files by cosine on the CLIP
embedding; searchByEmbedding ranks the index against a caller-supplied
query vector. All RAM-only, reusing the packed Float32Array index and id
list. No text encoder is bundled — the caller provides the query vector.

Redo of the reverted first cut: under noUncheckedIndexedAccess the cosine
loops read query[k]/embeddings[base+k]/fileIDs[i] as number|undefined, so
tsc (make build) failed though make check passed. Every such read is now
in range by the surrounding bounds check (query length, packed layout),
so the reads are asserted non-null rather than paying a per-element guard
in the hot ~50k×512 loop. Gated on both make check and make build.

Model: opus-4-8
clawbot added the needs-review label 2026-09-22 20:33:57 +02:00
clawbot self-assigned this 2026-09-22 20:33:57 +02:00
Author
Collaborator

PASS — lib.mldata (forFile / similar / searchByEmbedding) meets the definition of done in #50: correct cosine ranking over the reused #49 packed Float32Array index and id list, RAM-only, no bundled text encoder and no new dependency, the strict-config index-access typing that reverted the first cut is fixed, and the branch is green on both make check and make build and rebases cleanly onto current next.

Model: opus-4-8

PASS — `lib.mldata` (`forFile` / `similar` / `searchByEmbedding`) meets the definition of done in https://git.eeqj.de/sneak/quak/issues/50: correct cosine ranking over the reused #49 packed `Float32Array` index and id list, RAM-only, no bundled text encoder and no new dependency, the strict-config index-access typing that reverted the first cut is fixed, and the branch is green on both `make check` and `make build` and rebases cleanly onto current `next`. Model: opus-4-8
clawbot merged commit e8575780e4 into next 2026-09-22 20:45:59 +02:00
clawbot deleted branch issue-50-ml-search-redo 2026-09-22 20:45:59 +02:00
Sign in to join this conversation.