sweet-search 2.5.2 → 2.5.3
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.
- package/core/cli.js +24 -3
- package/core/graph/graph-expansion.js +215 -36
- package/core/graph/graph-extractor.js +196 -11
- package/core/graph/graph-search.js +395 -92
- package/core/graph/hcgs-generator.js +2 -1
- package/core/graph/index.js +2 -0
- package/core/graph/repo-map.js +28 -6
- package/core/graph/structural-answer-cues.js +168 -0
- package/core/graph/structural-callsite-hints.js +40 -0
- package/core/graph/structural-context-format.js +40 -0
- package/core/graph/structural-context.js +450 -0
- package/core/graph/structural-forward-push.js +156 -0
- package/core/graph/structural-header-context.js +19 -0
- package/core/graph/structural-importance.js +148 -0
- package/core/graph/structural-pagerank.js +197 -0
- package/core/graph/summary-manager.js +13 -9
- package/core/incremental-indexing/application/dirty-scan.mjs +236 -0
- package/core/incremental-indexing/application/file-watcher.mjs +197 -0
- package/core/incremental-indexing/application/maintenance-handlers.mjs +519 -0
- package/core/incremental-indexing/application/maintenance-worker.mjs +380 -0
- package/core/incremental-indexing/application/operator-cli.mjs +554 -0
- package/core/incremental-indexing/application/production-li-delta.mjs +192 -0
- package/core/incremental-indexing/application/production-reconciler-helpers.mjs +107 -0
- package/core/incremental-indexing/application/production-reconciler.mjs +583 -0
- package/core/incremental-indexing/application/reconciler.mjs +477 -0
- package/core/incremental-indexing/application/tombstone-injector.mjs +148 -0
- package/core/incremental-indexing/domain/chunk-identity.mjs +260 -0
- package/core/incremental-indexing/domain/encoder-deps.mjs +193 -0
- package/core/incremental-indexing/domain/encoder-input.mjs +225 -0
- package/core/incremental-indexing/domain/interval-autotune.mjs +255 -0
- package/core/incremental-indexing/domain/reconcile-counters.mjs +149 -0
- package/core/incremental-indexing/domain/watermark-scheduler.mjs +239 -0
- package/core/incremental-indexing/infrastructure/artifact-temp-sweep.mjs +163 -0
- package/core/incremental-indexing/infrastructure/baseline-readiness.mjs +121 -0
- package/core/incremental-indexing/infrastructure/dirty-set.mjs +233 -0
- package/core/incremental-indexing/infrastructure/graph-gc.mjs +314 -0
- package/core/incremental-indexing/infrastructure/hashing.mjs +298 -0
- package/core/incremental-indexing/infrastructure/hcgs-invalidation.mjs +182 -0
- package/core/incremental-indexing/infrastructure/li-segment-merge.mjs +278 -0
- package/core/incremental-indexing/infrastructure/li-segment-state.mjs +173 -0
- package/core/incremental-indexing/infrastructure/lockfile.mjs +119 -0
- package/core/incremental-indexing/infrastructure/maintenance-state-reader.mjs +283 -0
- package/core/incremental-indexing/infrastructure/manifest.mjs +194 -0
- package/core/incremental-indexing/infrastructure/path-filter.mjs +190 -0
- package/core/incremental-indexing/infrastructure/reader-heartbeat.mjs +201 -0
- package/core/incremental-indexing/infrastructure/schema-migrations.mjs +257 -0
- package/core/incremental-indexing/infrastructure/sparse-gram-delta.mjs +335 -0
- package/core/incremental-indexing/infrastructure/sqlite-fts5.mjs +176 -0
- package/core/incremental-indexing/infrastructure/staleness-display.mjs +105 -0
- package/core/incremental-indexing/infrastructure/tombstone-bitmap.mjs +234 -0
- package/core/incremental-indexing/infrastructure/vector-delta-writer.mjs +359 -0
- package/core/incremental-indexing/infrastructure/vector-gc.mjs +133 -0
- package/core/incremental-indexing/infrastructure/worktree-stamp.mjs +155 -0
- package/core/incremental-indexing/infrastructure/wsl2-detect.mjs +115 -0
- package/core/indexing/admission-policy.js +139 -0
- package/core/indexing/artifact-builder.js +29 -12
- package/core/indexing/ast-chunker.js +107 -30
- package/core/indexing/dedup/exemplar-selector.js +19 -1
- package/core/indexing/gitignore-filter.js +223 -0
- package/core/indexing/incremental-tracker.js +99 -30
- package/core/indexing/index-codebase-v21.js +6 -5
- package/core/indexing/index-maintainer.mjs +698 -6
- package/core/indexing/indexer-ann.js +99 -15
- package/core/indexing/indexer-build.js +158 -45
- package/core/indexing/indexer-empty-baseline.js +80 -0
- package/core/indexing/indexer-manifest.js +66 -0
- package/core/indexing/indexer-phases.js +56 -23
- package/core/indexing/indexer-sparse-gram.js +54 -13
- package/core/indexing/indexer-utils.js +26 -208
- package/core/indexing/indexing-file-policy.js +32 -7
- package/core/indexing/maintainer-launcher.mjs +137 -0
- package/core/indexing/merkle-tracker.js +251 -244
- package/core/indexing/model-pool.js +46 -5
- package/core/infrastructure/code-graph-repository.js +758 -6
- package/core/infrastructure/code-graph-visibility.js +157 -0
- package/core/infrastructure/codebase-repository.js +100 -13
- package/core/infrastructure/config/search.js +1 -1
- package/core/infrastructure/db-utils.js +118 -0
- package/core/infrastructure/dedup-hashing.js +10 -13
- package/core/infrastructure/hardware-capability.js +17 -7
- package/core/infrastructure/index.js +8 -2
- package/core/infrastructure/language-patterns/maps.js +4 -1
- package/core/infrastructure/language-patterns/registry-core.js +56 -17
- package/core/infrastructure/language-patterns/registry-object-oriented.js +12 -5
- package/core/infrastructure/language-patterns.js +69 -0
- package/core/infrastructure/model-registry.js +20 -0
- package/core/infrastructure/native-inference.js +7 -12
- package/core/infrastructure/native-resolver.js +52 -37
- package/core/infrastructure/native-sparse-gram.js +261 -20
- package/core/infrastructure/native-tokenizer.js +6 -15
- package/core/infrastructure/simd-distance.js +10 -16
- package/core/infrastructure/sparse-gram-delta-reader.js +76 -0
- package/core/infrastructure/structural-alias-resolver.js +122 -0
- package/core/infrastructure/structural-candidate-ranker.js +34 -0
- package/core/infrastructure/structural-context-repository.js +472 -0
- package/core/infrastructure/structural-context-utils.js +51 -0
- package/core/infrastructure/structural-graph-signals.js +121 -0
- package/core/infrastructure/structural-qualified-resolution.js +15 -0
- package/core/infrastructure/structural-source-definitions.js +100 -0
- package/core/infrastructure/tombstone-bitmap-reader.js +139 -0
- package/core/infrastructure/tree-sitter-provider.js +811 -37
- package/core/prompt-optimization/data/p7-final/sweet-search-system-prompt.md +50 -0
- package/core/query/query-router.js +55 -5
- package/core/ranking/file-kind-ranking.js +2192 -15
- package/core/ranking/late-interaction-index.js +87 -12
- package/core/search/cli-decoration.js +290 -0
- package/core/search/context-expander.js +988 -78
- package/core/search/index.js +1 -0
- package/core/search/output-policy.js +275 -0
- package/core/search/search-anchor.js +499 -0
- package/core/search/search-boost.js +93 -1
- package/core/search/search-cli.js +61 -204
- package/core/search/search-hybrid.js +250 -10
- package/core/search/search-pattern-chunks.js +57 -8
- package/core/search/search-pattern-planner.js +68 -9
- package/core/search/search-pattern-prefilter.js +30 -10
- package/core/search/search-pattern-ripgrep.js +40 -4
- package/core/search/search-pattern-sparse-overlay.js +256 -0
- package/core/search/search-pattern.js +117 -29
- package/core/search/search-postprocess.js +479 -5
- package/core/search/search-read-semantic.js +260 -23
- package/core/search/search-read.js +82 -64
- package/core/search/search-reader-pin.js +71 -0
- package/core/search/search-rrf.js +279 -0
- package/core/search/search-semantic.js +110 -5
- package/core/search/search-server.js +130 -57
- package/core/search/search-trace.js +107 -0
- package/core/search/server-identity.js +93 -0
- package/core/search/session-daemon-prewarm.mjs +33 -10
- package/core/search/sweet-search.js +399 -7
- package/core/skills/sweet-index/SKILL.md +8 -6
- package/core/vector-store/binary-hnsw-index.js +194 -30
- package/core/vector-store/float-vector-store.js +96 -6
- package/core/vector-store/hnsw-index.js +220 -49
- package/eval/agent-read-workflows/bin/_ss-helpers.mjs +471 -0
- package/eval/agent-read-workflows/bin/ss-find +15 -0
- package/eval/agent-read-workflows/bin/ss-grep +12 -0
- package/eval/agent-read-workflows/bin/ss-read +14 -0
- package/eval/agent-read-workflows/bin/ss-search +18 -0
- package/eval/agent-read-workflows/bin/ss-semantic +12 -0
- package/eval/agent-read-workflows/bin/ss-trace +11 -0
- package/mcp/read-tool.js +109 -0
- package/mcp/server.js +55 -15
- package/mcp/tool-handlers.js +14 -124
- package/mcp/trace-tool.js +81 -0
- package/package.json +25 -10
- package/scripts/hooks/intercept-read.mjs +55 -0
- package/scripts/hooks/remind-tools.mjs +40 -0
- package/scripts/init.js +698 -54
- package/scripts/inject-agent-instructions.js +431 -0
- package/scripts/install-prompt-reminders.js +188 -0
- package/scripts/install-tool-enforcement.js +220 -0
- package/scripts/smoke-test.js +12 -9
- package/scripts/uninstall.js +276 -18
- 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
|
+
}
|