{"slug": "turbovec-google-s-turboquant-for-vector-search-in-rust", "title": "Turbovec – Google's TurboQuant for vector search in Rust", "summary": "Turbovec, a Rust vector index with Python bindings built on Google Research's TurboQuant algorithm, fits a 10 million document corpus into 4 GB of RAM versus 31 GB as float32 and searches faster than FAISS, averaging 3.4× speedup at 4-bit and 23% at 2-bit across tested configurations. The library offers online ingest, SIMD-optimized search, incremental saves, and filter-at-search-time, targeting privacy-sensitive RAG applications.", "body_md": "**A 10 million document corpus takes 31 GB of RAM as float32. turbovec fits it in 4 GB - and searches it faster than FAISS.**\n\nturbovec is a Rust vector index with Python bindings, built on Google Research's [ TurboQuant](https://arxiv.org/abs/2504.19874) algorithm — a data-oblivious quantizer with near-optimal distortion and no separate training phase.\n\n**Online ingest.** Add vectors, they're indexed — no train step, no parameter tuning, no rebuilds as the corpus grows.**Fast SIMD search.** Hand-written kernels — NEON SDOT/SMMLA on ARM, AVX-512 VNNI and`vpermb`\n\non x86, with AVX2 and scalar fallbacks — beat FAISS IndexPQFastScan in every measured config, averaging 3.4× at 4-bit and 23% at 2-bit across the eight cells of each width, on both architectures.**Incremental saves.**`sync(path)`\n\npersists just what changed since the last sync — one fsync per call, crash-safe at any byte, and a removal or a small append costs milliseconds however large the index.`write`\n\n/`load`\n\nstay for whole-file snapshots.**Filter at search time.** Pass an id allowlist (or a slot bitmask) to`search()`\n\nand the kernel honours it directly. You always get up to`k`\n\nresults from the allowed set — no over-fetching, no recall hit on selective filters.**Pure local.** No managed service, no data leaving your machine or VPC. Pair with any open-source embedding model for a fully air-gapped RAG stack.\n\nBuilding RAG where privacy, memory, or latency matters? **You're in the right place.**\n\n```\npip install turbovec\npython\nfrom turbovec import TurboQuantIndex\n\nindex = TurboQuantIndex(dim=1536, bit_width=4)\nindex.add(vectors)\nindex.add(more_vectors)\n\nscores, indices = index.search(query, k=10)\n\nindex.write(\"my_index.tv\")\nloaded = TurboQuantIndex.load(\"my_index.tv\")\n\nindex.sync(\"my_index.tv\")   # after more changes: durable incremental save\n```\n\n`vectors`\n\nand `query`\n\nare 2-D `float32`\n\narrays of shape `(n, dim)`\n\n— other dtypes are rejected rather than silently converted, so cast with `np.asarray(x, dtype=np.float32)`\n\nfirst if needed.\n\nNeed stable ids that survive deletes? Use `IdMapIndex`\n\n:\n\n``` python\nimport numpy as np\nfrom turbovec import IdMapIndex\n\nindex = IdMapIndex(dim=1536, bit_width=4)\nindex.add_with_ids(vectors, np.array([1001, 1002, 1003], dtype=np.uint64))\n\nscores, ids = index.search(query, k=10)   # ids are your uint64 external ids\nindex.remove(1002)                         # O(1) by id\n\nindex.write(\"my_index.tvim\")\nloaded = IdMapIndex.load(\"my_index.tvim\")\n\nindex.sync(\"my_index.tvim\")   # durable incremental save, ids included\n```\n\nRestrict results to a candidate set produced by another system (SQL, BM25, ACL, time window, …):\n\n``` python\nimport numpy as np\nfrom turbovec import IdMapIndex\n\nidx = IdMapIndex(dim=1536, bit_width=4)\nidx.add_with_ids(vectors, ids)\n\n# Stage 1: external system narrows to candidate ids.\nallowed = np.array(db.execute(\"SELECT id FROM docs WHERE tenant=?\", (t,)).fetchall(),\n                   dtype=np.uint64)\n\n# Stage 2: dense rerank within the candidate set.\nscores, ids = idx.search(query, k=10, allowlist=allowed)\n```\n\nFiltering happens inside the SIMD kernel at 32-vector block granularity: blocks with no allowed slots are short-circuited before any LUT lookup or scoring work, and individual non-allowed slots inside scored blocks are dropped at heap-insert. Selective allowlists (small fraction of the index allowed) therefore avoid most of the SIMD cost rather than paying it and discarding the result afterwards.\n\nThe output length is `min(k, n_allowed)`\n\n, where `n_allowed`\n\ncounts *distinct* allowed vectors — when fewer vectors are allowed than `k`\n\nyou get exactly that many results rather than padded fallbacks.\n\nSee [ docs/api.md](https://github.com/RyanCodrai/turbovec/blob/main/docs/api.md) for the full reference.\n\nDrop-in replacements for the in-tree reference vector / document stores in each framework. Same public surface, same persistence semantics, same retriever and pipeline wiring — swap the import and keep your pipeline.\n\n[LangChain](https://github.com/RyanCodrai/turbovec/blob/main/docs/integrations/langchain.md)—`pip install turbovec[langchain]`\n\n· replaces`langchain_core.vectorstores.InMemoryVectorStore`\n\n[LlamaIndex](https://github.com/RyanCodrai/turbovec/blob/main/docs/integrations/llama_index.md)—`pip install turbovec[llama-index]`\n\n· replaces`llama_index.core.vector_stores.SimpleVectorStore`\n\n[Haystack](https://github.com/RyanCodrai/turbovec/blob/main/docs/integrations/haystack.md)—`pip install turbovec[haystack]`\n\n· replaces`haystack.document_stores.in_memory.InMemoryDocumentStore`\n\n[Agno](https://github.com/RyanCodrai/turbovec/blob/main/docs/integrations/agno.md)—`pip install turbovec[agno]`\n\n· replaces`agno.vectordb.lancedb.LanceDb`\n\n```\ncargo add turbovec\njs\nuse turbovec::TurboQuantIndex;\n\nlet mut index = TurboQuantIndex::new(1536, 4).unwrap();\nindex.add(&vectors);\nlet results = index.search(&queries, 10);\nindex.write(\"index.tv\").unwrap();\nlet loaded = TurboQuantIndex::load(\"index.tv\").unwrap();\n```\n\nFor stable external ids that survive deletes:\n\n``` js\nuse turbovec::IdMapIndex;\n\nlet mut index = IdMapIndex::new(1536, 4).unwrap();\nindex.add_with_ids(&vectors, &[1001, 1002, 1003]).unwrap();\nlet (scores, ids) = index.search(&queries, 10);\nindex.remove(1002);\nindex.write(\"index.tvim\").unwrap();\nlet loaded = IdMapIndex::load(\"index.tvim\").unwrap();\n```\n\nTurboQuant vs FAISS `IndexPQ`\n\n(LUT256, nbits=8) — the paper's Section 4.4 baseline. 100K vectors, k=64. FAISS PQ sub-quantizer counts sized to match TurboQuant's bit rate (m=d/4 at 2-bit, m=d/2 at 4-bit).\n\nThe charts plot calibrated TurboQuant (TQ+). Across OpenAI d=1536 and d=3072, TQ+ beats FAISS at R@1 on three of four cells (by 0.9–2.9 points; d=1536 4-bit trails by 0.7), and both reach 1.0 by k=8 (≥0.997 already at k≤4). GloVe d=200 is the harder regime — at low dim the asymptotic Beta assumption is looser. TQ+ lands ahead of FAISS at R@1 at both bit widths (+1.9 at 4-bit, +0.8 at 2-bit), with FAISS keeping a slim edge at 2-bit from k≈8. Uncalibrated numbers are in the JSONs (`tq_recalls`\n\n).\n\n**A note on baselines.** We compare against FAISS `IndexPQ`\n\n(LUT256, nbits=8, float32 LUT) because it's the default production-grade PQ most users would reach for. This is a stronger baseline than the custom u8-LUT PQ in the [TurboQuant paper](https://arxiv.org/abs/2504.19874) — FAISS uses a higher-precision LUT at scoring time and k-means++ for codebook training. We reproduce the paper's TurboQuant numbers on OpenAI d=1536 / d=3072 and hit similar numbers to other community reference implementations on low-dim embeddings (see [ turboquant-py](https://pypi.org/project/turboquant-py/) at d=384). On GloVe (d=200) — the low-dim regime where the asymptotic Beta assumption is loosest — TurboQuant lands ahead of FAISS at 4-bit but trails it at 2-bit; TQ+ calibration recovers the 2-bit deficit at R@1 (0.572 vs FAISS's 0.564), with FAISS keeping a slim edge at deeper k.\n\nFull results: [d=1536 2-bit](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/recall_d1536_2bit.json), [d=1536 4-bit](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/recall_d1536_4bit.json), [d=3072 2-bit](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/recall_d3072_2bit.json), [d=3072 4-bit](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/recall_d3072_4bit.json), [GloVe 2-bit](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/recall_glove_2bit.json), [GloVe 4-bit](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/recall_glove_4bit.json).\n\nAll benchmarks: 100K vectors, 1K queries, k=64, median of 5 runs.\n\nOn ARM, TurboQuant beats FAISS FastScan in every config, averaging 3.5× at 4-bit (3.4–3.7× across cells — the SDOT/SMMLA dot-product kernels score the vector-major layout directly) and 26% at 2-bit (22–29%).\n\nOn x86, TurboQuant wins every config, averaging 3.4× at 4-bit (3.2–3.5× across cells — the AVX-512 VNNI dot-product kernel on the vector-major layout) and 20% at 2-bit (5–32%), where the `vpermb`\n\nLUT scan carries the short 2-bit accumulate loop.\n\nSame corpus as the search cells: 100K OpenAI vectors, median of 5 runs, timed loops including the Python-call overhead a caller actually pays per op. Insertion measures per-vector `add()`\n\nlatency on a warm, populated index (built untimed) at n=1 — a single-vector `add()`\n\n— and n=100 — a 100-vector batch, showing how far batching amortizes the per-call overhead — against `add()`\n\ninto the trained, populated FAISS `IndexPQFastScan`\n\n(training untimed). A single `add()`\n\nlands in 6.3–19.7 µs depending on the cell (7.6–13.9× faster than a FAISS single add), and a 100-vector batch amortizes TurboQuant to 4.6–16.3 µs/vector (4.6–15.1× faster than the same batch into FAISS). Removal measures per-op remove-by-id latency at n=1 (the steady per-op rate over 1000 removes) and n=100 (the first 100 removes on a fresh index): `IdMapIndex.remove(id)`\n\n— O(1) swap-and-pop plus the id-map bookkeeping — lands at 0.44–1.22 µs and 0.59–1.37 µs per op across the cells. The FAISS column is the same user-visible operation, `remove_ids`\n\non an `IndexIDMap`\n\nover `IndexPQFastScan`\n\n, which repacks the stored codes on every call: 0.19–1.02 s per single remove at 100K, with cost doubling alongside code size — which is why the removal charts use a log-scale axis. Charts show the single-threaded cells (`RAYON_NUM_THREADS=1`\n\n); the `_mt`\n\ncells are measured too and match at n=1, since a single add is serial. Scripts: [ benchmarks/suite/](https://github.com/RyanCodrai/turbovec/tree/main/benchmarks/suite/).\n\nFull results: [d=1536 2-bit insert](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_insert_d1536_2bit_arm_st.json), [d=1536 4-bit insert](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_insert_d1536_4bit_arm_st.json), [d=3072 2-bit insert](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_insert_d3072_2bit_arm_st.json), [d=3072 4-bit insert](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_insert_d3072_4bit_arm_st.json), and the matching [ speed_remove_*](https://github.com/RyanCodrai/turbovec/tree/main/benchmarks/results/) and\n\n`_mt`\n\nfiles.Full results: [d=1536 2-bit insert](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_insert_d1536_2bit_x86_st.json), [d=1536 4-bit insert](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_insert_d1536_4bit_x86_st.json), [d=3072 2-bit insert](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_insert_d3072_2bit_x86_st.json), [d=3072 4-bit insert](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_insert_d3072_4bit_x86_st.json), and the matching [ speed_remove_*](https://github.com/RyanCodrai/turbovec/tree/main/benchmarks/results/) and\n\n`_mt`\n\nfiles.Same corpus as the search cells: 100K OpenAI vectors, median of 5 runs. TurboQuant serializes to a single `.tv`\n\nfile with an fsync + atomic rename; FAISS is `write_index`\n\n/ `read_index`\n\non the precision-matched `IndexPQFastScan`\n\n(sub-quantizer count matched to TurboQuant's bit rate, as in the search cells). **Save (warm)** is a write after a search has run, so the blocked layout cache is populated. **Load → first search** opens a fresh index and times the first query — separating bare deserialization (the page cache is warm throughout, so this is layout work, not cold-storage I/O) from the first-query cost. **Round-trip** chains the checkpoint/resume cycle an embedding store actually pays — mutate 1K vectors → save → reopen → serve the first query; FAISS has no measured equivalent for this path, so it is shown for TurboQuant only. On the smaller payloads the round-trip can come in *below* the isolated post-mutation (\"dirty\") write: the two are timed in separate suite steps, and at small file sizes the standalone `fsync`\n\nin the dirty-write step dominates and inflates it — a measurement artifact of the harness, not a repack win in the combined path. Single-threaded cells pin `RAYON_NUM_THREADS=1`\n\n. Scripts: [ benchmarks/suite/](https://github.com/RyanCodrai/turbovec/tree/main/benchmarks/suite/).\n\nFull results: [d=1536 2-bit persist ST](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d1536_2bit_arm_st.json), [MT](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d1536_2bit_arm_mt.json), [d=1536 4-bit persist ST](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d1536_4bit_arm_st.json), [MT](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d1536_4bit_arm_mt.json), [d=3072 2-bit persist ST](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d3072_2bit_arm_st.json), [MT](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d3072_2bit_arm_mt.json), [d=3072 4-bit persist ST](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d3072_4bit_arm_st.json), [MT](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d3072_4bit_arm_mt.json).\n\nFull results: [d=1536 2-bit persist ST](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d1536_2bit_x86_st.json), [MT](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d1536_2bit_x86_mt.json), [d=1536 4-bit persist ST](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d1536_4bit_x86_st.json), [MT](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d1536_4bit_x86_mt.json), [d=3072 2-bit persist ST](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d3072_2bit_x86_st.json), [MT](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d3072_2bit_x86_mt.json), [d=3072 4-bit persist ST](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d3072_4bit_x86_st.json), [MT](https://github.com/RyanCodrai/turbovec/blob/main/benchmarks/results/speed_persist_d3072_4bit_x86_mt.json).\n\nEach vector is a direction on a high-dimensional hypersphere. TurboQuant compresses these directions using a simple insight: after applying a random rotation, every coordinate follows a known distribution -- regardless of the input data.\n\n**1. Normalize.** Strip the length (norm) from each vector and store it as a single float. Now every vector is a unit direction on the hypersphere.\n\n**2. Random rotation.** Multiply all vectors by the same random orthogonal matrix. After rotation, each coordinate independently follows a Beta distribution that converges to Gaussian N(0, 1/d) in high dimensions. This holds for any input data -- the rotation makes the coordinate distribution predictable.\n\n**3. Per-coordinate calibration (TQ+).** The Beta distribution from step 2 is asymptotic — at finite dimensions, individual coordinates drift from the canonical shape (especially low-bit and word-vector-style embeddings). TQ+ fits two scalars per coordinate — a shift and a scale — mapping each coordinate's empirical quantiles onto the codebook's outermost centroids. The probability level comes from the codebook, so it tracks the bit width (~0.933 at 2-bit, ~0.996 at 4-bit) rather than being fixed. The Lloyd-Max codebook then quantizes against the *target* distribution it was designed for. The fit is explicit: call `index.calibrate(sample)`\n\nonce with a random, representative sample of your vectors (~1024 rows is enough — a draw of that size matches fitting on the whole corpus) before adding; afterwards the calibration is committed and reused by every add — no retraining, no rebuilds, no separate train phase. An index you never calibrate is plain TurboQuant. `index.calibration_state`\n\nreports `\"uncalibrated\"`\n\nor `\"calibrated\"`\n\n. Recall gain: up to +2.2pp at @1 on the cells that drift most (e.g. GloVe at 2-bit).\n\n**4. Lloyd-Max scalar quantization.** Since the distribution is known, we can precompute the optimal way to bucket each coordinate. For 2-bit, that's 4 buckets; for 4-bit, 16 buckets. The [Lloyd-Max algorithm](https://en.wikipedia.org/wiki/Lloyd%27s_algorithm) finds bucket boundaries and centroids that minimize mean squared error. These are computed once from the math, not from the data.\n\n**5. Bit-pack.** Each coordinate is now a small integer (0-3 for 2-bit, 0-15 for 4-bit). Pack these tightly into bytes. A 1536-dim vector goes from 6,144 bytes (FP32) to 384 bytes (2-bit). That's 16x compression.\n\n**6. Length-renormalized scoring.** Scalar quantization systematically underestimates inner products — the reconstructed unit direction is a little shorter than the original. We compute one scalar per vector at encode time — the inner product of the rotated unit vector with its own centroid reconstruction — and store `||v|| / ⟨u, x̂⟩`\n\nalongside each compressed vector. The search kernel multiplies the per-candidate score by this scalar before heap insertion, turning the inner-product estimator from downward-biased into unbiased at zero search-time cost and zero extra storage. The recall gain shows up most at low bit widths, where the quantization shrinkage is largest.\n\nEncoding cost: one extra `d`\n\n-dimensional dot product per vector to compute `⟨u, x̂⟩`\n\n. On 1M vectors at d=1536 this is sub-second of additional encode time — a one-shot price paid at ingest, not at query.\n\n**Search.** Instead of decompressing every database vector, we rotate the query once into the same domain and score directly against the codebook values. The scoring kernel uses SIMD intrinsics (NEON on ARM; AVX-512BW on modern x86, falling back to AVX2, then to a scalar path on pre-AVX2 CPUs) with nibble-split lookup tables for maximum throughput.\n\nThe Lloyd-Max codebook achieves distortion within a factor of 2.7x of the information-theoretic lower bound (Shannon's distortion-rate limit); the length-renormalization step removes the residual bias the Lloyd-Max codebook introduces on the inner-product estimator itself.\n\n```\npip install maturin\ncd turbovec-python\nmaturin build --release\npip install target/wheels/*.whl\ncargo build --release\n```\n\nAll x86_64 builds target `x86-64-v2`\n\n(SSE4.2 baseline, Nehalem 2008+) via `.cargo/config.toml`\n\n, so any x86-64-v2 CPU can run the whole crate. The AVX-512 and AVX2 kernels are `#[target_feature]`\n\n-gated and selected at runtime via `is_x86_feature_detected!`\n\n, so they kick in on hardware that supports them regardless of the compile baseline; CPUs with neither run the scalar fallback.\n\nDownload datasets:\n\n```\npython3 benchmarks/download_data.py all            # all datasets\npython3 benchmarks/download_data.py glove          # GloVe d=200\npython3 benchmarks/download_data.py openai-1536    # OpenAI DBpedia d=1536\npython3 benchmarks/download_data.py openai-3072    # OpenAI DBpedia d=3072\n```\n\nEach benchmark is a self-contained script in `benchmarks/suite/`\n\n. Run any one individually:\n\n```\npython3 benchmarks/suite/speed_d1536_2bit_arm_mt.py\npython3 benchmarks/suite/recall_d1536_2bit.py\npython3 benchmarks/suite/compression.py\n```\n\nRun all benchmarks for a category:\n\n```\nfor f in benchmarks/suite/speed_*arm*.py; do python3 \"$f\"; done    # all ARM speed\nfor f in benchmarks/suite/speed_*x86*.py; do python3 \"$f\"; done    # all x86 speed\nfor f in benchmarks/suite/recall_*.py; do python3 \"$f\"; done       # all recall\npython3 benchmarks/suite/compression.py                            # compression\n```\n\nResults are saved as JSON to `benchmarks/results/`\n\n. Regenerate charts:\n\n```\npython3 benchmarks/create_diagrams.py\n```\n\nThe suite above is the source of every published number — real embeddings, FAISS comparator, fixed shapes, run on the two official environments. For the inner loop of an optimization pass there's also a Rust harness that reproduces the four mutation metrics (cold bulk add, warm append, single add, remove) on deterministic synthetic vectors, so a hypothesis can be measured in seconds on any machine with no dataset and no FAISS:\n\n```\ncargo run --release --example insert_bench -- --dim 1536 --bits 2\nRAYON_NUM_THREADS=1 cargo run --release --example insert_bench\n```\n\nIt is a screening tool, not a source of published numbers.\n\n`examples/encode_hash`\n\nprints a per-stage hash of the encode pipeline for a\nfixed input; CI runs it on every OS in the matrix and fails if they disagree,\nwhich is how cross-platform byte identity of the encode is checked.\n\n[TurboQuant: Online Vector Quantization with Near-optimal Distortion Rate](https://arxiv.org/abs/2504.19874)(ICLR 2026) -- the paper this implements[RaBitQ: Quantizing High-Dimensional Vectors with a Theoretical Error Bound for Approximate Nearest Neighbor Search](https://arxiv.org/abs/2405.12497)(SIGMOD 2024) -- the source of the per-vector length-renormalization correction adapted in step 5[FAISS Fast accumulation of PQ and AQ codes](https://github.com/facebookresearch/faiss/wiki/Fast-accumulation-of-PQ-and-AQ-codes-(FastScan))-- turbovec's x86 SIMD kernel adapts FastScan's pack layout, nibble-LUT scoring, and u16 accumulator strategy", "url": "https://wpnews.pro/news/turbovec-google-s-turboquant-for-vector-search-in-rust", "canonical_source": "https://github.com/RyanCodrai/turbovec", "published_at": "2026-08-18 18:07:21+00:00", "updated_at": "2026-08-18 18:41:18.733196+00:00", "lang": "en", "topics": ["artificial-intelligence", "machine-learning", "ai-infrastructure", "ai-tools", "ai-products"], "entities": ["Turbovec", "Google Research", "TurboQuant", "FAISS", "RyanCodrai"], "alternates": {"html": "https://wpnews.pro/news/turbovec-google-s-turboquant-for-vector-search-in-rust", "markdown": "https://wpnews.pro/news/turbovec-google-s-turboquant-for-vector-search-in-rust.md", "text": "https://wpnews.pro/news/turbovec-google-s-turboquant-for-vector-search-in-rust.txt", "jsonld": "https://wpnews.pro/news/turbovec-google-s-turboquant-for-vector-search-in-rust.jsonld"}}