perf: v0.2.5 spike follow-ups — hydrate verify, streaming hash, JSONL policy, embed batching, vector polish - #93
Merged
Conversation
…d unpacker (#92) - db.search_by_ids binds ids in chunks of 900 (under SQLite's legacy 999-variable cap for any oversample); merged results carry the same canonical (cosine DESC, id ASC, NULL-last) order. - search_memories fetches FTS rows once; _fetch_candidates unions them and _normalize_bm25 reuses them (one FTS pass on both paths). - vector_index drops _unpack_blob for embed.unpack_embedding and logs swallowed Chroma delete/clear failures at warn level. - Tests: >999-id chunking parity vs full scan; single-FTS-pass assertion on both accelerated and fallback paths.
valiantone
marked this pull request as ready for review
August 28, 2026 12:39
Trigram -> (bucket, sign) results are memoized in an lru_cache (65 536 entries), and md5 digests are converted via int.from_bytes instead of hexdigest parsing. Iteration order and float accumulation are unchanged, so hotmem-hash-v1 vectors are bit-identical — golden test compares against the verbatim pre-#90 algorithm over ASCII, unicode, and randomized corpora. Measured: 6.26 ms -> 1.07 ms per 9.7 KB embedding (5.9x, cold cache). Targets the spike B3 finding that embed_text is 37-78% of parse_bundle.
hydrate_memory_detailed hashes the bytes it already read via the new provenance.verify_bytes instead of calling verify_range, which re-read the range through the adapter. The digest and ProvenanceError surface are byte-for-byte identical; verify_range remains for standalone verification. Test: SpyAdapter asserts exactly one read_range per verified hydrate (proven to fail against the double-read implementation). Targets spike B1: ~+16% at 100 MB (850 -> 736 ms median).
verify_range hashes ranges > STREAM_VERIFY_THRESHOLD (8 MiB) through the new optional LocalFilesystemAdapter.read_range_chunked generator — O(chunk) memory instead of O(range). Digest, mismatch, and truncation semantics are identical to the single-read path; adapters without the capability and ranges at/below the threshold are unchanged (duck-typed capability probe, no StorageAdapter protocol change). Test: parity + path engagement across the threshold boundary (proven boundary: exactly 8 MiB stays single-read) via SpyAdapter.
… assurance (#89) Inspection is advisory: it never authorizes import, hydration, snapshot, or provenance decisions. Default validation now parses only the declared sample window and declares the assurance level via FileInspection.metadata['validation'] ('sampled' | 'full'); validation='full' (inspect_file param / hotmem inspect --full-validation) keeps the pre-#89 parse-every-line behavior. row_count semantics unchanged (validation- independent). The validation kwarg is accepted uniformly across inspectors for dispatch; CSV/Parquet ignore it (no JSON validation applies). Tests: window-line reporting in both modes, beyond-window behavior (sampled None vs full reported), assurance declaration, mode validation. Measured: 34 ms vs 115 ms per 11.6 MB JSONL (spike B2: ~5.5x at 100 MB).
alphakenz
approved these changes
Aug 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Performance and correctness follow-ups from the native helper spike (#48, benched in #84), batched as the v0.2.5 release. This PR is opened as a draft and filled in ticket by ticket; each ticket's scope is implemented, tested, and checked against the unified acceptance criteria below.
Scope — tickets closed by this PR
memory.hydrate_memory_detailedreads the range, thenprovenance.verify_rangere-reads it to hash it. Hash the already-read bytes (or add a streaming verify API accepting in-memory bytes); expected ~+16% on verified hydration of large ranges, plus one fewer range copy.json.loadsis 5.5x of scan cost at 100 MB.docs/api.mdand covered by tests.embed_texttrigram hashing. 37–78% ofparse_bundletime is per-trigramhashlib.md5calls (embed.py:44). Batch into one hash update over a packed buffer or a cheaper rolling hash. Constraint:hotmem-hash-v1vectors stay bit-identical, or the embedding model string is versioned with a migration note.search_by_idsbinds (safe on SQLite builds with the 999-variable limit, whateveroversampleis configured); single FTS pass per accelerated search (candidate union + BM25 share onefts_search); reuseembed.unpack_embeddinginstead of_unpack_blob; log swallowed Chroma delete/clear errors at warn level instead of blanket-silencing.Unified acceptance criteria (holistic, across all tickets)
Correctness — the contracts that must not move:
/v1/searchresponses remain byte-identical with and without the vector index (golden additive test stays green); verified hydration produces digests identical to the current double-read path on every range size; hybrid ranking order is unchanged for stores within the exactness window.FileInspectionguarantees is deliberate, documented indocs/api.md, and versioned in the CHANGELOG.include_archived/TTL/archived predicates behave identically on every new code path.chromadbstays optional ([vector]extra); zero new required dependencies or wheels.Performance — measured against the committed spike baselines (
bench/native_spike/results.json, B1/B2/B3):embed_textbatching yields a measurableparse_bundlereduction on the spike bundle trees (fixtures reusable), withhotmem-hash-v1vectors bit-identical (or model string versioned + migration note).Robustness:
search_by_idsis parameter-limit safe for any configuredoversample(chunked binds), with a regression test covering >999 ids.Quality gates (every commit):
vectorCI job;ruff check+ruff format --checkclean onsrc/ tests/ examples/.[Unreleased]per ticket; docs (api.md,architecture.md) updated wherever user-visible behavior or policy changes.Release
v0.2.5on merge to main), not incidentally in feature commits.References
Implementation status — all five tickets landed
864d655unpack_embedding; warn-logged Chroma failuresfe7602elru_cache(65 536) +int.from_bytesdigest conversion;hotmem-hash-v1bit-identicalc053082provenance.verify_bytes; hydrate hashes already-read bytes — one read per rangeebd8ce2read_range_chunked(optional local-adapter capability, no protocol change); streaming verify > 8 MiB threshold9b2ded7metadata["validation"], opt-invalidation="full"(+ CLI flag); row_count untouchedMeasured (this machine, min-of-N):
embed_text9.7 KB: 6.26 ms → 1.07 ms (5.9x, cold cache) — attacks the spike's 37–78% ofparse_bundle.read_rangeper range (was two) — spike B1: ~+16% at 100 MB.Decision record (#89, ratified by @valiantone 2026-08-28): inspection is advisory and never authorizes import/hydration/snapshot/provenance decisions; default
validation="sampled"with declared assurance inmetadata["validation"]; full validation preserved as opt-in. Documented indocs/architecture.md.Atlas alignment: #87/#88 preserve digest +
ProvenanceErrorsemantics exactly (no new public semantics); #90 keepshotmem-hash-v1bit-identical (no model-version bump needed — the golden test is the compatibility proof); #92 keeps the index optional/rebuildable with zero result-semantics change; #89 implements the advisory-vs-authoritative boundary verbatim.