• Blog
  • Docs
  • Pricing
  • We’re hiring!
Log inSign up
kamenxrider

kamenxrider

slimarmor

Semantic vector DB on Val Town SQLite — DiskANN, hybrid search
Public
Like
slimarmor
Home
Code
5
H
api.ts
plandan.md
review.md
turso-doc.md
vectordb.ts
Environment variables
4
Branches
1
Pull requests
Remixes
History
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Sign up now
Code
/
review.md
Code
/
review.md
Search
2/2/2026
Viewing readonly version of main branch: v23
View latest version
review.md

No-holds-barred take (based on the portion you pasted — it cuts off mid-function, so I’m reviewing what’s visible + the implied next steps).


1) The good parts (you’re on the right track)

✅ You’re testing the right primitives

You’re explicitly validating the exact libSQL/Turso “native vectors” surface area Val Town would expose:

  • F32_BLOB(D) vector column types
  • vector(...) constructor
  • libsql_vector_idx(...) index marker
  • vector_top_k(...) query function

That’s exactly the feature set Turso documents for vector search. (docs.turso.tech)

✅ You’re checking embedding dimensionality (at least printing it)

Printing embedding.length is the first sanity check that prevents 90% of “why won’t inserts / index queries work?” issues.

✅ Your SQL literal conversion is conceptually correct

In libSQL, vector is an alias conversion function (effectively vector32) and it accepts a JSON array string. (docs.turso.tech)


2) The big problems / likely breakpoints

🚨 Problem A: You hardcode EMBEDDING_DIM = 4096 but you never enforce it

Right now you print length, but you don’t fail fast if the returned length differs.

Why this matters:

  • libSQL requires the query vector to have the same type and dimensionality as the column/index. If your table is F32_BLOB(4096) and the API returns 3072 or 1024, you’re dead. (docs.turso.tech)

Fix: assert the dimension before any DB work.

Create val
function assertDim(vec: number[], expected: number) { if (vec.length !== expected) { throw new Error( `Embedding dim mismatch: got ${vec.length}, expected ${expected}`, ); } }

Then in testEmbeddingAPI():

Create val
assertDim(embedding, EMBEDDING_DIM);

Also: Qwen3-Embedding supports up to 4096 dims (and sometimes providers always return 4096). (Hugging Face)


🚨 Problem B: toFixed(8) is not helping you, and it can hurt you

You’re rounding every component to 8 decimals. That is:

  • wasted CPU
  • makes the SQL string bigger than it needs to be (because you force a fixed representation)
  • introduces quantization noise you didn’t ask for

libSQL will store as float32 anyway; you don’t need to pre-round.

Fix: remove rounding completely (or keep minimal rounding only for debugging prints).


🚨 Problem C: Building a 4096-float SQL literal is a “string bazooka”

A 4096-dim embedding as JSON text is huge. You’re generating a SQL statement that contains a massive vector('[...]') blob.

This can bite you in multiple ways:

  • request payload size to the DB endpoint
  • SQL parsing overhead
  • log output bloat if anything errors and prints the query
  • harder to parameterize safely

Turso’s docs explicitly support converting a text JSON array to the target vector type via conversion functions like vector32(...). (docs.turso.tech)

Much better fix: parameterize the JSON and wrap it in vector32(?):

Create val
const vecJson = JSON.stringify(embedding); await sqlite.execute({ sql: `INSERT INTO t(embedding) VALUES (vector32(?))`, args: [vecJson], });

This keeps SQL smaller, avoids injection-y patterns, and is closer to the documented usage model. (docs.turso.tech)


🚨 Problem D: You’re importing std/sqlite in a nonstandard way

You used:

Create val
import sqlite from "https://esm.town/v/std/sqlite@14-main/main.ts";

Val Town’s own std/sqlite README shows:

Create val
import sqlite from "https://esm.town/v/std/sqlite/main.ts";

So unless you need that exact tag/branch, you’re taking on avoidable breakage risk. (Val Town)

Fix (simple):

Create val
import sqlite from "https://esm.town/v/std/sqlite/main.ts";

If you want pinning, pin a commit/version you control—but don’t invent tag shapes unless you know they exist.


🚨 Problem E: You’re not testing the actual vector mechanics libSQL cares about

The real “gotchas” are:

  1. Conversion function/type match Your table will likely be F32_BLOB(D). Your inserted values must be float32 vectors. vector(...) is alias for float32 conversion, which is good. (docs.turso.tech)

  2. Index query requires same type + same dimensionality vector_top_k will fail if the query vector differs in either. (docs.turso.tech)

Your script should include a test that:

  • creates the table with F32_BLOB(4096)
  • creates the index with libsql_vector_idx(embedding, 'metric=cosine')
  • inserts at least 2 rows
  • runs vector_top_k(...) and joins back to the table

Turso’s docs + blog show this exact flow. (turso.tech)


3) Performance + cost reality check (the stuff that will sting later)

4096 dims is allowed, but it’s chunky

Turso docs: libSQL can operate up to 65536 dimensions. So 4096 is fine. (docs.turso.tech)

But storage math: float32 = 4 bytes.

  • 4096 × 4 = 16384 bytes (~16 KB) per row, just for the embedding, before index overhead.

If you chunk docs and store many rows, that balloons fast.

Hard truth: Unless you truly need 4096, you’ll often be better with a smaller vector (common sweet spots: 768–1536) because your recall gain from bigger dims is frequently marginal vs your cost/index size. (Not always, but often.)


4) Security / reliability issues

API call reliability

Your embedding call has:

  • no timeout
  • no retry/backoff
  • no provider error normalization
  • no batching guardrails

Nebius will occasionally spike latency or return transient errors—this will make indexing brittle.

Fix: add an AbortController timeout + small retry.

Leak risk

Don’t log the entire embedding ever (you currently only print first 5 values — good). Keep it that way.


5) Concrete code upgrades (drop-in improvements)

5.1 Replace toVectorSqlLiteral with parameterized vector32(?)

Create val
function vectorArgs(embedding: number[]) { // no rounding; keep raw floats return JSON.stringify(embedding); } // usage: await sqlite.execute({ sql: `INSERT INTO test_vectors(id, embedding) VALUES (?, vector32(?))`, args: ["row1", vectorArgs(embedding)], });

This matches Turso’s documented “text JSON array → vector32 conversion” path. (docs.turso.tech)

5.2 Enforce dimension immediately

Create val
function assertDim(vec: number[], expected: number) { if (vec.length !== expected) { throw new Error( `Embedding dim mismatch: got ${vec.length}, expected ${expected}`, ); } }

5.3 Use the canonical std/sqlite import

Create val
import sqlite from "https://esm.town/v/std/sqlite/main.ts";

As documented by Val Town’s std/sqlite README + docs. (Val Town)

5.4 Your DB test should explicitly validate vector_top_k semantics

Turso docs explain: vector_top_k returns ROWID (or PRIMARY KEY for WITHOUT ROWID tables), and you usually join back to the base table. (docs.turso.tech)

So your test should look like:

Create val
await sqlite.execute(`DROP TABLE IF EXISTS test_vectors`); await sqlite.execute(` CREATE TABLE test_vectors ( id TEXT PRIMARY KEY, embedding F32_BLOB(4096) ) `); await sqlite.execute(` CREATE INDEX test_vectors_idx ON test_vectors (libsql_vector_idx(embedding, 'metric=cosine')) `); const [e1] = await embedText("dogs bark"); const [e2] = await embedText("github api client"); assertDim(e1, 4096); assertDim(e2, 4096); await sqlite.execute({ sql: `INSERT INTO test_vectors(id, embedding) VALUES (?, vector32(?))`, args: ["a", JSON.stringify(e1)], }); await sqlite.execute({ sql: `INSERT INTO test_vectors(id, embedding) VALUES (?, vector32(?))`, args: ["b", JSON.stringify(e2)], }); const [q] = await embedText("discord bot integration"); assertDim(q, 4096); const res = await sqlite.execute({ sql: ` SELECT tv.id FROM vector_top_k('test_vectors_idx', vector32(?), 2) v JOIN test_vectors tv ON tv.rowid = v.id `, args: [JSON.stringify(q)], }); console.log(res.rows);

That test is aligned with how Turso describes indexing/querying. (docs.turso.tech)


6) Verdict (no sugar-coating)

Your direction is correct, but your current implementation style (giant SQL string literals + unused dimension constant + nonstandard sqlite import) is exactly the kind of thing that “works once” and then becomes flaky and painful to productionize.

If you implement just these 3 changes:

  1. parameterize vectors with vector32(?)
  2. assert dimensions
  3. use canonical std/sqlite/main.ts import

…your script goes from “prototype that might lie to you” to “reliable foundation test”.


If you paste the rest (especially the SQLite tests you wrote after the cutoff), I’ll do an equally ruthless line-by-line review of the actual table/index/query parts—because that’s where most people accidentally test the wrong thing and think vectors “don’t work” when it’s really a join/type mismatch.

FeaturesVersion controlCode intelligenceCLIMCP
Use cases
TeamsAI agentsSlackGTM
DocsShowcaseTemplatesNewestTrendingAPI examplesNPM packages
PricingNewsletterBlogAboutCareers
We’re hiring!
Brandhi@val.townStatus
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Open Source Pledge
Terms of usePrivacy policyAbuse contact
© 2026 Val Town, Inc.