Keep the vectors beside the atoms instead of rebuilding them per query
The dense leg used to be a pair of files next to the database: a full .npy re-encoded from every atom, re-normalized on every query, and stale the moment anything was ingested. It is now a vec0 table inside the same SQLite file, written in the same transaction as the atom it describes, so a memory is queryable the instant it is remembered. - vector_index.py maintains the table; the writer inserts, the reader never rebuilds. A repair scan (ely-eye build-dense-index) catches what a crash hid, and it is not on the search path. - Ranking stays in the engine; every boundary (closed, retired, not yet true, another holder's private memory, the mounted cartridge) is checked once against the atoms table afterwards. A visibility subquery inside the KNN cost 48.9s on 153,857 vectors; the same check over 200 candidates costs 0.2ms. When the boundaries reject enough candidates the ask widens once, never twice. - The Go mirror and the web panel carry the new status fields. - bench/dense_scale.py and bench/quant_probe.py measure the leg at corpus size; bench/scale_check.py now works on a copy so the frozen baseline stays frozen. Measured on the frozen 165,973-atom corpus. bench/reports/DENSE.md, QUANT.md and SCALE.md carry the numbers and the machine they were taken on. Gates: 285 pytest, ruff clean, pyright 0 on the touched files, go build/vet/test and gofmt clean, frontend build and eslint clean. mypy is 25 findings against the parent's 24: the new one is "Skipping analyzing sqlite_vec: missing library stubs", the same class as the two existing qwen_vl_utils lines, and the repo keeps no type: ignore anywhere.
This commit is contained in:
1 parent
be08e7bf9c
commit
cd9851a12c
26 files changed
+1602
-118
No files matched your search
@@ -103,7 +103,8 @@ export function MemoryPanel({ onChanged }: { onChanged: () => void }) {
|
||||
setNotice(
|
||||
`forgotten: ${report.atoms_deleted} atom${report.atoms_deleted === 1 ? '' : 's'}, ` +
|
||||
`${report.edges_deleted} links, ${report.cache_entries_cleared} cache entries` +
|
||||
(report.vector_index_dropped ? ', dense index dropped' : ''),
|
||||
(report.vectors_dropped ? `, ${report.vectors_dropped} dense vectors dropped` : '') +
|
||||
(report.visual_index_dropped ? ', picture index dropped' : ''),
|
||||
);
|
||||
setResult(null);
|
||||
await loadBrowse();
|
||||
|
||||
@@ -13,7 +13,7 @@ export function SensesPanel({ senses }: { senses: SenseStatus | null }) {
|
||||
<SenseRow
|
||||
label="dense vectors"
|
||||
state={senses.dense}
|
||||
detail={`${formatNumber(senses.dense_covered)} of ${formatNumber(senses.store_atoms)} atoms indexed`}
|
||||
detail={`${formatNumber(senses.dense_covered)} of ${formatNumber(senses.dense_expected)} memories with words indexed`}
|
||||
/>
|
||||
<SenseRow
|
||||
label="picture embeddings"
|
||||
|
||||
@@ -35,7 +35,7 @@ export interface MemoryStatus {
|
||||
export interface SenseStatus {
|
||||
dense: 'off' | 'missing' | 'behind' | 'current';
|
||||
dense_covered: number;
|
||||
store_atoms: number;
|
||||
dense_expected: number;
|
||||
picture_atoms: number;
|
||||
picture_bytes_missing: number;
|
||||
visual: 'absent' | 'stale' | 'ready';
|
||||
@@ -309,7 +309,8 @@ export interface ForgetReport {
|
||||
atoms_deleted: number;
|
||||
edges_deleted: number;
|
||||
cache_entries_cleared: number;
|
||||
vector_index_dropped: boolean;
|
||||
vectors_dropped: number;
|
||||
visual_index_dropped: boolean;
|
||||
affected_cartridges: Array<Record<string, unknown>>;
|
||||
reason: string;
|
||||
}
|
||||
Reference in new issue
Block a user