Skip to content

TurboQuant's packed codec has no IVectorStorage path — no backend can accept a TurboQuantizeResult #798

Description

@sroussey

What is missing

packages/util/src/vector/TurboQuantize.ts ships two independent encoders:

encoder output storage path
turboQuantizeToTypedArray plain Int8Array / Int16Array ✅ any IVectorStorage fixed-width column
turboQuantize / turboDequantize / turboQuantizedInnerProduct / turboQuantizedCosineSimilarity (+ turboQuantizeStorageBytes / turboQuantizeCompressionRatio) packed TurboQuantizeResult ❌ none

The packed codec is the interesting half — sub-byte widths (1–8 bits/dim), the exact 1-bit angle correction, and pairwise comparability enforcement — and it is currently unreachable from any persistence path. git grep for those six names and TurboQuantizeResult across the branch, excluding the module and its own test, returns zero hits.

Why no backend can accept it today

  • assertVectorShape (packages/storage/src/vector/assertVectorShape.ts:22-52), called by every backend on both write and query, requires an array-like whose length equals the store's declared dimensionality and whose every entry is a finite number. A TurboQuantizeResult is a record (codes, bits, seed, norm, version, dimensions, paddedDimensions) and fails on the first check; its packed codes buffer fails on the second, since at 4 bits it holds two coordinates per byte and its length is ceil(paddedDimensions * bits / 8), not dimensions.
  • Client-side-scoring backendsInMemoryVectorStorage (:161), SqliteVectorStorage (:151), SqliteAiVectorStorage (:660) — all call cosineSimilarity(query, vector) on raw numbers. There is no hook to substitute turboQuantizedCosineSimilarity, the only function that can read these codes.
  • Server-side-distance backendsPostgresVectorStorage (pgvector operators <=> / <-> / <#>) and SupabaseVectorStorage (a match_<table> RPC) — compute the distance in the database. A client-side scorer cannot be injected into either at all.

What a real integration needs

  1. A column type (or an encoding discriminator on the existing one) that can carry a packed record's metadata alongside its bytes — bits, seed, version, dimensions, paddedDimensions, norm — since two records are only comparable when (version, bits, seed, dimensions) match.
  2. A client-side scoring path in IVectorStorage that can dispatch to turboQuantizedCosineSimilarity / turboQuantizedInnerProduct instead of cosineSimilarity.
  3. Per-backend fallbacks for the server-side-distance engines: pgvector cannot evaluate this codec, so those backends need either a fetch-then-score-client-side path or an explicit "unsupported encoding" rejection at store-construction time.
  4. A decision on whether the bias documented in the module header (similarity is biased low at 2–8 bits; a cos > 0.8 cut at 2 bits drops pairs whose real cosine is 0.87) should be surfaced through the storage API — a stored scoreThreshold does not mean the same thing under this codec as under float storage.

Interim state

Nothing is being un-exported and nothing is being deleted. The functions are verified correct and their ~1200-line test suite imports them from @workglow/util/schema. The module header now documents the split explicitly — the packed codec is usable in-process (an in-memory candidate cache, a client-side rerank over a shortlist retrieved by other means) with a "do not persist it expecting a retrieval path to exist" warning — so the gap is stated at the point of use rather than discovered at integration time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions