sweet-search 2.5.2 → 2.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/core/cli.js +24 -3
  2. package/core/graph/graph-expansion.js +215 -36
  3. package/core/graph/graph-extractor.js +196 -11
  4. package/core/graph/graph-search.js +395 -92
  5. package/core/graph/hcgs-generator.js +2 -1
  6. package/core/graph/index.js +2 -0
  7. package/core/graph/repo-map.js +28 -6
  8. package/core/graph/structural-answer-cues.js +168 -0
  9. package/core/graph/structural-callsite-hints.js +40 -0
  10. package/core/graph/structural-context-format.js +40 -0
  11. package/core/graph/structural-context.js +450 -0
  12. package/core/graph/structural-forward-push.js +156 -0
  13. package/core/graph/structural-header-context.js +19 -0
  14. package/core/graph/structural-importance.js +148 -0
  15. package/core/graph/structural-pagerank.js +197 -0
  16. package/core/graph/summary-manager.js +13 -9
  17. package/core/incremental-indexing/application/dirty-scan.mjs +236 -0
  18. package/core/incremental-indexing/application/file-watcher.mjs +197 -0
  19. package/core/incremental-indexing/application/maintenance-handlers.mjs +519 -0
  20. package/core/incremental-indexing/application/maintenance-worker.mjs +380 -0
  21. package/core/incremental-indexing/application/operator-cli.mjs +554 -0
  22. package/core/incremental-indexing/application/production-li-delta.mjs +192 -0
  23. package/core/incremental-indexing/application/production-reconciler-helpers.mjs +107 -0
  24. package/core/incremental-indexing/application/production-reconciler.mjs +583 -0
  25. package/core/incremental-indexing/application/reconciler.mjs +477 -0
  26. package/core/incremental-indexing/application/tombstone-injector.mjs +148 -0
  27. package/core/incremental-indexing/domain/chunk-identity.mjs +260 -0
  28. package/core/incremental-indexing/domain/encoder-deps.mjs +193 -0
  29. package/core/incremental-indexing/domain/encoder-input.mjs +225 -0
  30. package/core/incremental-indexing/domain/interval-autotune.mjs +255 -0
  31. package/core/incremental-indexing/domain/reconcile-counters.mjs +149 -0
  32. package/core/incremental-indexing/domain/watermark-scheduler.mjs +239 -0
  33. package/core/incremental-indexing/infrastructure/artifact-temp-sweep.mjs +163 -0
  34. package/core/incremental-indexing/infrastructure/baseline-readiness.mjs +121 -0
  35. package/core/incremental-indexing/infrastructure/dirty-set.mjs +233 -0
  36. package/core/incremental-indexing/infrastructure/graph-gc.mjs +314 -0
  37. package/core/incremental-indexing/infrastructure/hashing.mjs +298 -0
  38. package/core/incremental-indexing/infrastructure/hcgs-invalidation.mjs +182 -0
  39. package/core/incremental-indexing/infrastructure/li-segment-merge.mjs +278 -0
  40. package/core/incremental-indexing/infrastructure/li-segment-state.mjs +173 -0
  41. package/core/incremental-indexing/infrastructure/lockfile.mjs +119 -0
  42. package/core/incremental-indexing/infrastructure/maintenance-state-reader.mjs +283 -0
  43. package/core/incremental-indexing/infrastructure/manifest.mjs +194 -0
  44. package/core/incremental-indexing/infrastructure/path-filter.mjs +190 -0
  45. package/core/incremental-indexing/infrastructure/reader-heartbeat.mjs +201 -0
  46. package/core/incremental-indexing/infrastructure/schema-migrations.mjs +257 -0
  47. package/core/incremental-indexing/infrastructure/sparse-gram-delta.mjs +335 -0
  48. package/core/incremental-indexing/infrastructure/sqlite-fts5.mjs +176 -0
  49. package/core/incremental-indexing/infrastructure/staleness-display.mjs +105 -0
  50. package/core/incremental-indexing/infrastructure/tombstone-bitmap.mjs +234 -0
  51. package/core/incremental-indexing/infrastructure/vector-delta-writer.mjs +359 -0
  52. package/core/incremental-indexing/infrastructure/vector-gc.mjs +133 -0
  53. package/core/incremental-indexing/infrastructure/worktree-stamp.mjs +155 -0
  54. package/core/incremental-indexing/infrastructure/wsl2-detect.mjs +115 -0
  55. package/core/indexing/admission-policy.js +139 -0
  56. package/core/indexing/artifact-builder.js +29 -12
  57. package/core/indexing/ast-chunker.js +107 -30
  58. package/core/indexing/dedup/exemplar-selector.js +19 -1
  59. package/core/indexing/gitignore-filter.js +223 -0
  60. package/core/indexing/incremental-tracker.js +99 -30
  61. package/core/indexing/index-codebase-v21.js +6 -5
  62. package/core/indexing/index-maintainer.mjs +698 -6
  63. package/core/indexing/indexer-ann.js +99 -15
  64. package/core/indexing/indexer-build.js +158 -45
  65. package/core/indexing/indexer-empty-baseline.js +80 -0
  66. package/core/indexing/indexer-manifest.js +66 -0
  67. package/core/indexing/indexer-phases.js +56 -23
  68. package/core/indexing/indexer-sparse-gram.js +54 -13
  69. package/core/indexing/indexer-utils.js +26 -208
  70. package/core/indexing/indexing-file-policy.js +32 -7
  71. package/core/indexing/maintainer-launcher.mjs +137 -0
  72. package/core/indexing/merkle-tracker.js +251 -244
  73. package/core/indexing/model-pool.js +46 -5
  74. package/core/infrastructure/code-graph-repository.js +758 -6
  75. package/core/infrastructure/code-graph-visibility.js +157 -0
  76. package/core/infrastructure/codebase-repository.js +100 -13
  77. package/core/infrastructure/config/search.js +1 -1
  78. package/core/infrastructure/db-utils.js +118 -0
  79. package/core/infrastructure/dedup-hashing.js +10 -13
  80. package/core/infrastructure/hardware-capability.js +17 -7
  81. package/core/infrastructure/index.js +8 -2
  82. package/core/infrastructure/language-patterns/maps.js +4 -1
  83. package/core/infrastructure/language-patterns/registry-core.js +56 -17
  84. package/core/infrastructure/language-patterns/registry-object-oriented.js +12 -5
  85. package/core/infrastructure/language-patterns.js +69 -0
  86. package/core/infrastructure/model-registry.js +20 -0
  87. package/core/infrastructure/native-inference.js +7 -12
  88. package/core/infrastructure/native-resolver.js +52 -37
  89. package/core/infrastructure/native-sparse-gram.js +261 -20
  90. package/core/infrastructure/native-tokenizer.js +6 -15
  91. package/core/infrastructure/simd-distance.js +10 -16
  92. package/core/infrastructure/sparse-gram-delta-reader.js +76 -0
  93. package/core/infrastructure/structural-alias-resolver.js +122 -0
  94. package/core/infrastructure/structural-candidate-ranker.js +34 -0
  95. package/core/infrastructure/structural-context-repository.js +472 -0
  96. package/core/infrastructure/structural-context-utils.js +51 -0
  97. package/core/infrastructure/structural-graph-signals.js +121 -0
  98. package/core/infrastructure/structural-qualified-resolution.js +15 -0
  99. package/core/infrastructure/structural-source-definitions.js +100 -0
  100. package/core/infrastructure/tombstone-bitmap-reader.js +139 -0
  101. package/core/infrastructure/tree-sitter-provider.js +811 -37
  102. package/core/prompt-optimization/data/p7-final/sweet-search-system-prompt.md +50 -0
  103. package/core/query/query-router.js +55 -5
  104. package/core/ranking/file-kind-ranking.js +2192 -15
  105. package/core/ranking/late-interaction-index.js +87 -12
  106. package/core/search/cli-decoration.js +290 -0
  107. package/core/search/context-expander.js +988 -78
  108. package/core/search/index.js +1 -0
  109. package/core/search/output-policy.js +275 -0
  110. package/core/search/search-anchor.js +499 -0
  111. package/core/search/search-boost.js +93 -1
  112. package/core/search/search-cli.js +61 -204
  113. package/core/search/search-hybrid.js +250 -10
  114. package/core/search/search-pattern-chunks.js +57 -8
  115. package/core/search/search-pattern-planner.js +68 -9
  116. package/core/search/search-pattern-prefilter.js +30 -10
  117. package/core/search/search-pattern-ripgrep.js +40 -4
  118. package/core/search/search-pattern-sparse-overlay.js +256 -0
  119. package/core/search/search-pattern.js +117 -29
  120. package/core/search/search-postprocess.js +479 -5
  121. package/core/search/search-read-semantic.js +260 -23
  122. package/core/search/search-read.js +82 -64
  123. package/core/search/search-reader-pin.js +71 -0
  124. package/core/search/search-rrf.js +279 -0
  125. package/core/search/search-semantic.js +110 -5
  126. package/core/search/search-server.js +130 -57
  127. package/core/search/search-trace.js +107 -0
  128. package/core/search/server-identity.js +93 -0
  129. package/core/search/session-daemon-prewarm.mjs +33 -10
  130. package/core/search/sweet-search.js +399 -7
  131. package/core/skills/sweet-index/SKILL.md +8 -6
  132. package/core/vector-store/binary-hnsw-index.js +194 -30
  133. package/core/vector-store/float-vector-store.js +96 -6
  134. package/core/vector-store/hnsw-index.js +220 -49
  135. package/eval/agent-read-workflows/bin/_ss-helpers.mjs +471 -0
  136. package/eval/agent-read-workflows/bin/ss-find +15 -0
  137. package/eval/agent-read-workflows/bin/ss-grep +12 -0
  138. package/eval/agent-read-workflows/bin/ss-read +14 -0
  139. package/eval/agent-read-workflows/bin/ss-search +18 -0
  140. package/eval/agent-read-workflows/bin/ss-semantic +12 -0
  141. package/eval/agent-read-workflows/bin/ss-trace +11 -0
  142. package/mcp/read-tool.js +109 -0
  143. package/mcp/server.js +55 -15
  144. package/mcp/tool-handlers.js +14 -124
  145. package/mcp/trace-tool.js +81 -0
  146. package/package.json +25 -10
  147. package/scripts/hooks/intercept-read.mjs +55 -0
  148. package/scripts/hooks/remind-tools.mjs +40 -0
  149. package/scripts/init.js +698 -54
  150. package/scripts/inject-agent-instructions.js +431 -0
  151. package/scripts/install-prompt-reminders.js +188 -0
  152. package/scripts/install-tool-enforcement.js +220 -0
  153. package/scripts/smoke-test.js +12 -9
  154. package/scripts/uninstall.js +276 -18
  155. package/scripts/write-claude-rules.js +110 -0
@@ -0,0 +1,192 @@
1
+ import fs from 'node:fs';
2
+ import fsp from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { LateInteractionIndex } from '../../ranking/late-interaction-index.js';
5
+ import { encodeDocumentsCpu } from '../../ranking/late-interaction-model.js';
6
+ import {
7
+ openSegmentState,
8
+ tombstoneDoc,
9
+ persistSegmentState,
10
+ nextSegmentSeq,
11
+ } from '../infrastructure/li-segment-state.mjs';
12
+
13
+ function readJson(filePath, fallback = null) {
14
+ try {
15
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
16
+ } catch {
17
+ return fallback;
18
+ }
19
+ }
20
+
21
+ function segmentedState(indexPath) {
22
+ const stub = readJson(indexPath);
23
+ if (stub?.format !== 'segmented' || !stub.segmentDir) return null;
24
+ const segmentDir = path.resolve(path.dirname(indexPath), stub.segmentDir);
25
+ const manifestPath = path.join(segmentDir, 'manifest.json');
26
+ const manifest = readJson(manifestPath);
27
+ if (!manifest || !Array.isArray(manifest.segments)) return null;
28
+ return { segmentDir, manifestPath, manifest };
29
+ }
30
+
31
+ function manifestFromIndex(index) {
32
+ return {
33
+ version: '3.0',
34
+ format: 'sslx-v3',
35
+ modelId: index.modelId,
36
+ tokenDim: index.tokenDim,
37
+ matryoshkaDim: index.matryoshkaDim || 0,
38
+ maxTokens: index.maxTokens,
39
+ useInt8: index.useInt8,
40
+ quantBits: index.quantBits || (index.useInt8 ? 8 : 32),
41
+ poolFactor: index.poolFactor || 1,
42
+ whtSeed: index.whtSeed || 0,
43
+ whtOrdering: index.whtOrdering || 'natural',
44
+ totalDocuments: 0,
45
+ nextSeq: 0,
46
+ segments: [],
47
+ };
48
+ }
49
+
50
+ async function writeJsonAtomic(filePath, payload) {
51
+ await fsp.mkdir(path.dirname(filePath), { recursive: true });
52
+ const tmp = `${filePath}.tmp.${process.pid}`;
53
+ await fsp.writeFile(tmp, JSON.stringify(payload, null, 2));
54
+ await fsp.rename(tmp, filePath);
55
+ }
56
+
57
+ async function appendGrowingSegment(indexPath, index, docs) {
58
+ if (docs.size === 0) return 0;
59
+ const existing = segmentedState(indexPath);
60
+ const segmentDir = existing?.segmentDir || `${indexPath}.segments`;
61
+ const manifestPath = existing?.manifestPath || path.join(segmentDir, 'manifest.json');
62
+ const manifest = existing?.manifest || manifestFromIndex(index);
63
+ await fsp.mkdir(segmentDir, { recursive: true });
64
+
65
+ const seq = nextSegmentSeq(manifest);
66
+ const segName = `segment-${String(seq).padStart(4, '0')}.bin`;
67
+ const segPath = path.join(segmentDir, segName);
68
+ await index._writeSegmentFile(segPath, docs);
69
+ manifest.segments.push({ path: segName, count: docs.size });
70
+ manifest.nextSeq = seq + 1;
71
+ manifest.totalDocuments = (manifest.totalDocuments || 0) + docs.size;
72
+
73
+ await writeJsonAtomic(manifestPath, manifest);
74
+ await writeJsonAtomic(indexPath, {
75
+ version: '3.0',
76
+ format: 'segmented',
77
+ segmentDir: path.basename(segmentDir),
78
+ });
79
+ return docs.size;
80
+ }
81
+
82
+ async function rewriteLegacyIndex(index, ops, liEncoder, pickLiInput, onProgress = null) {
83
+ const progress = typeof onProgress === 'function'
84
+ ? (phase) => { onProgress(phase); }
85
+ : () => {};
86
+ let tombstone = 0;
87
+ for (const op of ops) {
88
+ if (op.retireId && index.documents.delete(op.retireId)) tombstone += 1;
89
+ }
90
+ const addOps = ops.filter((op) => op.addId && op.chunk);
91
+ const texts = addOps.map(({ chunk }) => pickLiInput(chunk));
92
+ progress('li:encode:start');
93
+ const tokens = texts.length > 0 ? await liEncoder(texts) : [];
94
+ progress('li:encode:done');
95
+ let appended = 0;
96
+ for (let i = 0; i < addOps.length; i += 1) {
97
+ if (!tokens[i] || tokens[i].length === 0) continue;
98
+ const { addId, chunk } = addOps[i];
99
+ await index.add(addId, tokens[i], {
100
+ file: chunk.file,
101
+ name: chunk.metadata?.symbol,
102
+ type: chunk.metadata?.chunk_type,
103
+ startLine: chunk.metadata?.line_start || null,
104
+ endLine: chunk.metadata?.line_end || null,
105
+ });
106
+ appended += 1;
107
+ if (appended % 100 === 0) progress('li:legacy-add');
108
+ }
109
+ await index.save();
110
+ progress('li:legacy-save');
111
+ return { appended, tombstone };
112
+ }
113
+
114
+ export async function applyLateInteractionDelta({
115
+ indexPath,
116
+ ops,
117
+ liEncoder,
118
+ pickLiInput,
119
+ onProgress = null,
120
+ }) {
121
+ const progress = typeof onProgress === 'function'
122
+ ? (phase) => { onProgress(phase); }
123
+ : () => {};
124
+ if (!Array.isArray(ops) || ops.length === 0) {
125
+ return { appended: 0, tombstone: 0 };
126
+ }
127
+
128
+ const encode = liEncoder || ((texts) => encodeDocumentsCpu(texts));
129
+ const existing = fs.existsSync(indexPath);
130
+ const segmented = existing ? segmentedState(indexPath) : null;
131
+ const index = new LateInteractionIndex({ indexPath, loadExisting: true });
132
+ await index.init();
133
+ progress('li:init');
134
+
135
+ if (existing && !segmented) {
136
+ return rewriteLegacyIndex(index, ops, encode, pickLiInput, progress);
137
+ }
138
+
139
+ let tombstone = 0;
140
+ const states = new Map();
141
+ for (const op of ops) {
142
+ if (!op.retireId) continue;
143
+ const position = index._docSegmentPositions.get(op.retireId);
144
+ if (!position) continue;
145
+ if (!states.has(position.segmentPath)) {
146
+ const docCount = index._segments.find((s) => s.path === position.segmentPath)?.count ?? position.docIndex + 1;
147
+ states.set(position.segmentPath, openSegmentState(position.segmentPath, docCount));
148
+ }
149
+ tombstoneDoc(states.get(position.segmentPath), position.docIndex);
150
+ tombstone += 1;
151
+ }
152
+ for (const state of states.values()) persistSegmentState(state);
153
+ progress('li:tombstone');
154
+
155
+ const addOps = ops.filter((op) => op.addId && op.chunk);
156
+ const texts = addOps.map(({ chunk }) => pickLiInput(chunk));
157
+ progress('li:encode:start');
158
+ const tokens = texts.length > 0 ? await encode(texts) : [];
159
+ progress('li:encode:done');
160
+ const writer = new LateInteractionIndex({
161
+ indexPath,
162
+ loadExisting: false,
163
+ tokenDim: index.tokenDim,
164
+ maxTokens: index.maxTokens,
165
+ useInt8: index.useInt8,
166
+ quantBits: index.quantBits,
167
+ modelId: index.modelId,
168
+ poolFactor: index.poolFactor,
169
+ whtSeed: index.whtSeed,
170
+ whtOrdering: index.whtOrdering,
171
+ matryoshkaDim: index.matryoshkaDim,
172
+ });
173
+ await writer.init();
174
+ progress('li:writer-init');
175
+
176
+ for (let i = 0; i < addOps.length; i += 1) {
177
+ if (!tokens[i] || tokens[i].length === 0) continue;
178
+ const { addId, chunk } = addOps[i];
179
+ await writer.add(addId, tokens[i], {
180
+ file: chunk.file,
181
+ name: chunk.metadata?.symbol,
182
+ type: chunk.metadata?.chunk_type,
183
+ startLine: chunk.metadata?.line_start || null,
184
+ endLine: chunk.metadata?.line_end || null,
185
+ });
186
+ if ((i + 1) % 100 === 0) progress('li:add');
187
+ }
188
+
189
+ const appended = await appendGrowingSegment(indexPath, writer, writer._currentSegment);
190
+ progress('li:append-segment');
191
+ return { appended, tombstone };
192
+ }
@@ -0,0 +1,107 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { normalizeIdentifier } from '../../graph/graph-extractor.js';
3
+ import { FloatVectorStore, getFloatStorePath } from '../../vector-store/float-vector-store.js';
4
+ import {
5
+ loadBitmap,
6
+ createBitmap,
7
+ resizeBitmap,
8
+ saveBitmap,
9
+ setBit,
10
+ } from '../infrastructure/tombstone-bitmap.mjs';
11
+
12
+ function entitySearchText(e) {
13
+ return [e.name, e.signature, e.doc_comment].filter(Boolean).join(' ').toLowerCase().slice(0, 1000);
14
+ }
15
+
16
+ export function insertEntity(db, e, id, epoch, hasFts) {
17
+ const nameAlias = normalizeIdentifier(e.name);
18
+ const stmt = db.prepare(`
19
+ INSERT INTO entities
20
+ (id, file_path, type, name, signature, signature_hash, doc_comment, start_line, end_line, package, parent_class, search_text, name_alias, parent_id, hierarchy_level, logical_entity_id, epoch_written, epoch_retired, stale_since)
21
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL)
22
+ `);
23
+ stmt.run(
24
+ id,
25
+ e.file_path,
26
+ e.type,
27
+ e.name,
28
+ e.signature || null,
29
+ e.signature_hash || null,
30
+ e.doc_comment || null,
31
+ e.start_line || null,
32
+ e.end_line || null,
33
+ e.package || null,
34
+ e.parent_class || null,
35
+ entitySearchText(e),
36
+ nameAlias || null,
37
+ null,
38
+ ['method', 'field', 'rpc'].includes(e.type) ? 1 : 0,
39
+ e.id,
40
+ epoch,
41
+ );
42
+ if (!hasFts) return;
43
+ const rowid = db.prepare('SELECT rowid FROM entities WHERE id = ?').get(id)?.rowid;
44
+ if (!rowid) return;
45
+ try { db.prepare('INSERT INTO entities_fts(rowid, name, name_alias, signature, doc_comment) VALUES (?, ?, ?, ?, ?)').run(rowid, e.name, nameAlias || null, e.signature || null, e.doc_comment || null); } catch {}
46
+ try { db.prepare('INSERT INTO entities_trigram(rowid, name, signature) VALUES (?, ?, ?)').run(rowid, e.name, e.signature || null); } catch {}
47
+ }
48
+
49
+ export function insertRelationships(db, relationships, liveIdFor, epoch) {
50
+ const stmt = db.prepare(`
51
+ INSERT INTO relationships
52
+ (source_id, target_id, target_name, type, weight, context_line, full_import_path, is_static, is_wildcard, logical_relationship_id, epoch_written, epoch_retired)
53
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)
54
+ `);
55
+ for (const r of relationships) {
56
+ if (!r.target_name) continue;
57
+ const source = liveIdFor.get(r.source_id) || r.source_id || null;
58
+ const logical = `${source || ''}:${r.type}:${r.target_name}:${r.context_line || ''}`;
59
+ try { stmt.run(source, r.target_id || null, r.target_name, r.type, r.weight || 1, r.context_line || null, r.full_import_path || null, r.is_static ? 1 : 0, r.is_wildcard ? 1 : 0, logical, epoch); } catch {}
60
+ }
61
+ }
62
+
63
+ export function markBinaryStale(index, id) {
64
+ const idx = index.idToIndex.get(id);
65
+ if (idx == null) return false;
66
+ const stalePath = index.stalePath || `${index.indexPath}.stale.bin`;
67
+ let bitmap = null;
68
+ try { bitmap = loadBitmap(stalePath); } catch {}
69
+ bitmap = bitmap ? resizeBitmap(bitmap, Math.max(idx + 1, index.vectors.length, 1)) : createBitmap(Math.max(idx + 1, index.vectors.length, 1));
70
+ setBit(bitmap, idx);
71
+ saveBitmap(stalePath, bitmap);
72
+ index.idToIndex.delete(id);
73
+ index.int8Vectors.delete(id);
74
+ index._staleBitmapCache = null;
75
+ return true;
76
+ }
77
+
78
+ /**
79
+ * Maintain the Stage 2.5 float vector store (`codebase-float-vectors.bin`) as a
80
+ * sidecar of the binary HNSW index. The search runtime derives its path from
81
+ * the binary HNSW path, loads/reloads the two together, and full indexing
82
+ * builds them together — so the float store is conceptually part of the binary
83
+ * HNSW tier, exactly like the int8 sidecar. Keeping it in lockstep here lets
84
+ * reconcile-created docs get true float rescoring in Stage 2.5 instead of
85
+ * falling back to SQLite.
86
+ *
87
+ * Skips when the store is absent but the binary HNSW already held vectors: a
88
+ * store built from the delta alone would be missing every baseline doc and
89
+ * would mis-score them. That abnormal state keeps the existing SQLite fallback
90
+ * until a full rebuild restores the store.
91
+ *
92
+ * @param {string} binaryHnswPath
93
+ * @param {object} delta
94
+ * @param {Array<{id: string, vector: Float32Array}>} delta.upserts
95
+ * @param {string[]} delta.removeIds
96
+ * @param {number} delta.binaryVectorsBefore Live binary-HNSW vectors before this delta.
97
+ * @param {number} delta.dimension hnswDimension to seed a fresh empty store.
98
+ */
99
+ export async function maintainFloatStore(binaryHnswPath, { upserts, removeIds, binaryVectorsBefore, dimension }) {
100
+ if (upserts.length === 0 && removeIds.length === 0) return;
101
+ const floatStorePath = getFloatStorePath(binaryHnswPath);
102
+ if (!existsSync(floatStorePath) && binaryVectorsBefore > 0) return;
103
+ const store = new FloatVectorStore();
104
+ await store.loadOrInit(floatStorePath, dimension);
105
+ store.applyDelta({ upserts, removeIds });
106
+ await store.save(floatStorePath);
107
+ }