Content-similarity search surface over the CLIP index #50

Closed
opened 2026-09-22 09:11:11 +02:00 by clawbot · 3 comments
Collaborator

Cache/API implementation (#36), phase 4.
Depends on the ML-data/index unit.

Goal

The content-similarity surface over the CLIP index, in RAM.

Scope

  • lib.mldata.forFile({ fileID }) → the stored MLData payload or undefined.
  • lib.mldata.similar({ fileID, limit? }) → nearest fileIDs by CLIP embedding
    (cosine), RAM only.
  • lib.mldata.searchByEmbedding({ embedding, limit? }) → cosine similarity over
    the index, RAM only. quak has no text encoder; the caller (the app, or a later
    unit) supplies the query vector — do not bundle a model here (owner-deferred;
    a bundled CLIP text model + ONNX runtime is its own future issue).
  • Cosine over a Float32Array index of ~50k×512 must be a plain, fast loop; no
    new dependency.

Definition of done

  • forFile/similar/searchByEmbedding return correct rankings on a small
    fixture index; RAM only, no network.
  • Tested; make check green.

Grounding

src/library/. Depends on the ML-data/index unit.

Dispatch notes: TDD; no scripted edits; no interactive questions; plain language.
Squash subject ends (closes #<this issue>). End every message with
Model: opus-4-8.

Model: opus-4-8

Cache/API implementation (https://git.eeqj.de/sneak/quak/issues/36), phase 4. Depends on the ML-data/index unit. ## Goal The content-similarity surface over the CLIP index, in RAM. ## Scope - `lib.mldata.forFile({ fileID })` → the stored `MLData` payload or undefined. - `lib.mldata.similar({ fileID, limit? })` → nearest fileIDs by CLIP embedding (cosine), RAM only. - `lib.mldata.searchByEmbedding({ embedding, limit? })` → cosine similarity over the index, RAM only. quak has no text encoder; the caller (the app, or a later unit) supplies the query vector — do not bundle a model here (owner-deferred; a bundled CLIP text model + ONNX runtime is its own future issue). - Cosine over a `Float32Array` index of ~50k×512 must be a plain, fast loop; no new dependency. ## Definition of done - `forFile`/`similar`/`searchByEmbedding` return correct rankings on a small fixture index; RAM only, no network. - Tested; `make check` green. ## Grounding `src/library/`. Depends on the ML-data/index unit. Dispatch notes: TDD; no scripted edits; no interactive questions; plain language. Squash subject ends ` (closes #<this issue>)`. End every message with `Model: opus-4-8`. Model: opus-4-8
Author
Collaborator

Implemented in PR #67 (branch issue-50-ml-search, base next).

Added lib.mldata over the #49 CLIP index:

  • 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. similar/searchByEmbedding return { fileID, score }[] descending, ties broken by ascending fileID — a judgement call to include the score alongside the id. make check is green.

Model: opus-4-8

Implemented in [PR #67](https://git.eeqj.de/sneak/quak/pulls/67) (branch `issue-50-ml-search`, base `next`). Added `lib.mldata` over the [#49](https://git.eeqj.de/sneak/quak/issues/49) CLIP index: - `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. `similar`/`searchByEmbedding` return `{ fileID, score }[]` descending, ties broken by ascending fileID — a judgement call to include the score alongside the id. `make check` is green. Model: opus-4-8
clawbot reopened this issue 2026-09-22 18:22:15 +02:00
Author
Collaborator

Reopened. The first implementation (PR #67) was reverted from next (#69): src/library/mlsearch.ts failed tsc under noUncheckedIndexedAccess, which make check does not catch (make check runs test/lint/fmt-check, not make build) — so it passed review but reddened next in the CI docker build. Redo: same scope, but the implementer AND reviewer must run make build (or script/cibuild) in addition to make check, and fix the index-access typing in the cosine loops (query[k]/embeddings[base+k]/fileIDs[i] are number|undefined under the strict config).

Model: opus-4-8

Reopened. The first implementation (PR #67) was reverted from next (https://git.eeqj.de/sneak/quak/pulls/69): src/library/mlsearch.ts failed tsc under noUncheckedIndexedAccess, which make check does not catch (make check runs test/lint/fmt-check, not make build) — so it passed review but reddened next in the CI docker build. Redo: same scope, but the implementer AND reviewer must run make build (or script/cibuild) in addition to make check, and fix the index-access typing in the cosine loops (query[k]/embeddings[base+k]/fileIDs[i] are number|undefined under the strict config). Model: opus-4-8
Author
Collaborator

Redone in PR #72 (branch issue-50-ml-search-redo, base next), same scope as the reverted first cut.

The revert cause is fixed: under noUncheckedIndexedAccess the cosine loops read query[k]/embeddings[base+k]/fileIDs[i] as number | undefined, which tsc rejects. Each read is in range by the surrounding bounds (query length equals the embedding length, the buffer is packed), so they are asserted non-null in the hot loop. Verified green on both make check and make build (the tsc build that make check does not run).

Model: opus-4-8

Redone in [PR #72](https://git.eeqj.de/sneak/quak/pulls/72) (branch `issue-50-ml-search-redo`, base `next`), same scope as the reverted first cut. The revert cause is fixed: under `noUncheckedIndexedAccess` the cosine loops read `query[k]`/`embeddings[base+k]`/`fileIDs[i]` as `number | undefined`, which `tsc` rejects. Each read is in range by the surrounding bounds (query length equals the embedding length, the buffer is packed), so they are asserted non-null in the hot loop. Verified green on **both** `make check` and `make build` (the tsc build that `make check` does not run). Model: opus-4-8
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/quak#50