ultracode 5.4.0 → 5.6.0
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/dist/chunks/analysis-tool-handlers-GH5FDEWW.js +817 -0
- package/dist/chunks/analysis-tool-handlers-IXP4MWZX.js +817 -0
- package/dist/chunks/analysis-tool-handlers-LC2BTQYK.js +13 -0
- package/dist/chunks/analysis-tool-handlers-QYFKQPFL.js +817 -0
- package/dist/chunks/autodoc-tool-handlers-2HF6ERYN.js +1112 -0
- package/dist/chunks/autodoc-tool-handlers-4OGQJ7C3.js +1112 -0
- package/dist/chunks/autodoc-tool-handlers-N736CB56.js +138 -0
- package/dist/chunks/autodoc-tool-handlers-NQYBY6U4.js +1112 -0
- package/dist/chunks/branch-tool-handlers-KW3H4FJK.js +276 -0
- package/dist/chunks/branch-tool-handlers-QOUDZKJ2.js +276 -0
- package/dist/chunks/branch-tool-handlers-RB2U36KI.js +2 -0
- package/dist/chunks/branch-tool-handlers-ZHJM6PDK.js +276 -0
- package/dist/chunks/chunk-2Z6OQPYC.js +656 -0
- package/dist/chunks/chunk-3MQ7LRPN.js +322 -0
- package/dist/chunks/chunk-4W6QYGXZ.js +10 -0
- package/dist/chunks/chunk-533NFGUG.js +1 -0
- package/dist/chunks/chunk-5NUPOPWM.js +1 -0
- package/dist/chunks/chunk-AK4HIPA2.js +322 -0
- package/dist/chunks/chunk-B3H5NS3I.js +656 -0
- package/dist/chunks/chunk-DPTZHDST.js +4 -0
- package/dist/chunks/chunk-E5HQWLU5.js +322 -0
- package/dist/chunks/chunk-EOH25B5P.js +572 -0
- package/dist/chunks/chunk-ESV6F6E3.js +3179 -0
- package/dist/chunks/chunk-FPELPFER.js +924 -0
- package/dist/chunks/chunk-G6J42I55.js +161 -0
- package/dist/chunks/chunk-GCQE7ZYW.js +1 -0
- package/dist/chunks/chunk-GTIF6MOX.js +1 -0
- package/dist/chunks/chunk-I6STSSAK.js +2 -0
- package/dist/chunks/chunk-J2WBGTK2.js +4697 -0
- package/dist/chunks/chunk-KAYOX5EB.js +4697 -0
- package/dist/chunks/chunk-KPMTACOT.js +656 -0
- package/dist/chunks/chunk-L376GZ44.js +3179 -0
- package/dist/chunks/chunk-LCTOTHDA.js +15 -0
- package/dist/chunks/chunk-LEDJ7GVQ.js +316 -0
- package/dist/chunks/chunk-LH4OUKNZ.js +277 -0
- package/dist/chunks/chunk-O6IE2MEZ.js +161 -0
- package/dist/chunks/chunk-OEXPCY3F.js +316 -0
- package/dist/chunks/chunk-OMXOLFDN.js +924 -0
- package/dist/chunks/chunk-PLPBXWOU.js +3179 -0
- package/dist/chunks/chunk-PWLE5DN2.js +572 -0
- package/dist/chunks/chunk-PY43JPWL.js +447 -0
- package/dist/chunks/chunk-Q3B4EB7A.js +15 -0
- package/dist/chunks/chunk-Q5LPVLXA.js +337 -0
- package/dist/chunks/chunk-QIRZHZK2.js +5 -0
- package/dist/chunks/chunk-ROQ27LSL.js +924 -0
- package/dist/chunks/chunk-S5Q7BD6J.js +572 -0
- package/dist/chunks/chunk-SAMX3HJQ.js +337 -0
- package/dist/chunks/chunk-SV3WKUNV.js +1 -0
- package/dist/chunks/chunk-TCHCDCDO.js +167 -0
- package/dist/chunks/chunk-TR3HS7U6.js +316 -0
- package/dist/chunks/chunk-TUWE6FCW.js +167 -0
- package/dist/chunks/chunk-TVOTA7EE.js +277 -0
- package/dist/chunks/chunk-VS44D772.js +337 -0
- package/dist/chunks/chunk-WIE3G5ES.js +167 -0
- package/dist/chunks/chunk-XG3ACLWR.js +5 -0
- package/dist/chunks/chunk-XJ2Z5QQO.js +1 -0
- package/dist/chunks/chunk-XK2NY7RB.js +277 -0
- package/dist/chunks/chunk-Y4F7NZFZ.js +4700 -0
- package/dist/chunks/chunk-YS75L3ZS.js +161 -0
- package/dist/chunks/chunk-ZVG5HHI3.js +15 -0
- package/dist/chunks/dev-agent-DDDIVWOF.js +1 -0
- package/dist/chunks/dev-agent-E2VCFKXN.js +1624 -0
- package/dist/chunks/dev-agent-KJNSU5KQ.js +1624 -0
- package/dist/chunks/dev-agent-NDERYIPV.js +1624 -0
- package/dist/chunks/faiss-provider-7R4BQDIV.js +12 -0
- package/dist/chunks/faiss-provider-7ZFRSDN5.js +12 -0
- package/dist/chunks/faiss-provider-SXB7FTLB.js +1 -0
- package/dist/chunks/faiss-provider-TKLBEUSH.js +12 -0
- package/dist/chunks/file-tool-handlers-5DODQXGF.js +1027 -0
- package/dist/chunks/file-tool-handlers-KGHLE4KR.js +1027 -0
- package/dist/chunks/file-tool-handlers-KTOQ4NFS.js +12 -0
- package/dist/chunks/file-tool-handlers-V4SFUDQB.js +1027 -0
- package/dist/chunks/graph-metrics-tool-handlers-3AV4X4ZY.js +65 -0
- package/dist/chunks/graph-metrics-tool-handlers-3VMDQHJ6.js +65 -0
- package/dist/chunks/graph-metrics-tool-handlers-BZ6E6YHF.js +1 -0
- package/dist/chunks/graph-metrics-tool-handlers-IYBGSXL7.js +65 -0
- package/dist/chunks/graph-storage-factory-2CQ2RPDV.js +13 -0
- package/dist/chunks/graph-storage-factory-C5SMMYL6.js +13 -0
- package/dist/chunks/graph-storage-factory-EEO2V3GJ.js +1 -0
- package/dist/chunks/graph-storage-factory-WBCTXP34.js +13 -0
- package/dist/chunks/history-tool-handlers-AS7OQFZI.js +1 -0
- package/dist/chunks/history-tool-handlers-FSNJYXV2.js +208 -0
- package/dist/chunks/history-tool-handlers-JZAH4EIQ.js +208 -0
- package/dist/chunks/history-tool-handlers-KCSCXZ7T.js +208 -0
- package/dist/chunks/incremental-updater-A2EL4QXU.js +14 -0
- package/dist/chunks/incremental-updater-EQIKBVY2.js +14 -0
- package/dist/chunks/incremental-updater-JFGRPH3B.js +14 -0
- package/dist/chunks/incremental-updater-S5BAAGHP.js +1 -0
- package/dist/chunks/indexer-agent-ASKY7JPG.js +1 -0
- package/dist/chunks/indexer-agent-NKAOF323.js +21 -0
- package/dist/chunks/indexer-agent-PJN5IOKQ.js +21 -0
- package/dist/chunks/indexer-agent-WRJFWKZX.js +21 -0
- package/dist/chunks/indexing-pipeline-D4P2O72Z.js +249 -0
- package/dist/chunks/indexing-pipeline-L7C543N4.js +1 -0
- package/dist/chunks/indexing-pipeline-NHPRN3AB.js +249 -0
- package/dist/chunks/indexing-pipeline-ZAXCZU22.js +249 -0
- package/dist/chunks/layered-faiss-provider-62CNW54X.js +1 -0
- package/dist/chunks/layered-faiss-provider-O7L77GFX.js +12 -0
- package/dist/chunks/layered-faiss-provider-RVHLHLPK.js +12 -0
- package/dist/chunks/layered-faiss-provider-YT7EDIJI.js +12 -0
- package/dist/chunks/merge-agent-3RF7VFF5.js +2481 -0
- package/dist/chunks/merge-agent-JCKTCBCE.js +2481 -0
- package/dist/chunks/merge-agent-VCL7OXPN.js +2481 -0
- package/dist/chunks/merge-agent-ZGK24WVF.js +11 -0
- package/dist/chunks/merge-tool-handlers-GV2LOIKU.js +277 -0
- package/dist/chunks/merge-tool-handlers-TYDWU5X2.js +277 -0
- package/dist/chunks/merge-tool-handlers-U7X2ZO2M.js +1 -0
- package/dist/chunks/merge-tool-handlers-YH62ZLPJ.js +277 -0
- package/dist/chunks/pattern-tool-handlers-76NF5JDS.js +13 -0
- package/dist/chunks/pattern-tool-handlers-IJAGEIVD.js +1549 -0
- package/dist/chunks/pattern-tool-handlers-VA5WYA62.js +1549 -0
- package/dist/chunks/pattern-tool-handlers-WQ6UBMJS.js +1549 -0
- package/dist/chunks/query-agent-36ADGCFZ.js +1 -0
- package/dist/chunks/query-agent-H22CR5N5.js +191 -0
- package/dist/chunks/query-agent-K2UGZS4M.js +191 -0
- package/dist/chunks/query-agent-YJCEHOXD.js +191 -0
- package/dist/chunks/semantic-agent-AC7CBEDE.js +6381 -0
- package/dist/chunks/semantic-agent-HK5X6CKU.js +6381 -0
- package/dist/chunks/semantic-agent-KONIKEGW.js +6381 -0
- package/dist/chunks/semantic-agent-LH6IZ2L7.js +137 -0
- package/dist/chunks/semantic-tool-handlers-5LMSH2U7.js +3 -0
- package/dist/chunks/semantic-tool-handlers-735UMO7Y.js +817 -0
- package/dist/chunks/semantic-tool-handlers-BNUYPP7X.js +817 -0
- package/dist/chunks/semantic-tool-handlers-MYZPEUD2.js +817 -0
- package/dist/chunks/snapshot-tool-handlers-6SIHZT2F.js +201 -0
- package/dist/chunks/snapshot-tool-handlers-DS4P3KOT.js +201 -0
- package/dist/chunks/snapshot-tool-handlers-JYHRFPC7.js +201 -0
- package/dist/chunks/snapshot-tool-handlers-YEHMAT3L.js +1 -0
- package/dist/chunks/storage-paths-A3C7WHHG.js +8 -0
- package/dist/chunks/storage-paths-HDYH7WPM.js +1 -0
- package/dist/chunks/storage-paths-IMFRHBWF.js +8 -0
- package/dist/chunks/storage-paths-P3PUSMUD.js +8 -0
- package/dist/chunks/taint-tool-handlers-CWESOOMQ.js +68 -0
- package/dist/chunks/taint-tool-handlers-OG3NVVP3.js +1 -0
- package/dist/chunks/taint-tool-handlers-ON3G3FA7.js +68 -0
- package/dist/chunks/taint-tool-handlers-P4P5J6DB.js +68 -0
- package/dist/chunks/tracing-tool-handlers-4BDCXTZZ.js +3935 -0
- package/dist/chunks/tracing-tool-handlers-6FPNM7HX.js +3935 -0
- package/dist/chunks/tracing-tool-handlers-LQTQ5SKK.js +89 -0
- package/dist/chunks/tracing-tool-handlers-XRQX2DTS.js +3935 -0
- package/dist/chunks/validation-tool-handlers-DZUG7KYY.js +2 -0
- package/dist/chunks/validation-tool-handlers-O6TGFSH5.js +555 -0
- package/dist/chunks/validation-tool-handlers-RREUYKIR.js +555 -0
- package/dist/chunks/validation-tool-handlers-XPWSMS37.js +555 -0
- package/dist/index.js +13 -13
- package/dist/roslyn-addon/.build-hash +1 -1
- package/dist/roslyn-addon/ILGPU.Algorithms.dll +0 -0
- package/dist/roslyn-addon/ILGPU.dll +0 -0
- package/dist/roslyn-addon/UltraCode.CSharp.deps.json +35 -0
- package/dist/roslyn-addon/UltraCode.CSharp.dll +0 -0
- package/package.json +1 -1
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
import { getGpuClient } from './chunk-OMXOLFDN.js';
|
|
2
|
+
import { simdL2Normalize } from './chunk-QN237S7C.js';
|
|
3
|
+
import { getBaseMetaPathByHash } from './chunk-YS75L3ZS.js';
|
|
4
|
+
import { init_storage_paths, normalizeBranchName, getFaissIndexPathByHash } from './chunk-XK2NY7RB.js';
|
|
5
|
+
import { init_logging, log } from './chunk-VCCBEJQ5.js';
|
|
6
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, unlinkSync } from 'fs';
|
|
7
|
+
import { dirname } from 'path';
|
|
8
|
+
|
|
9
|
+
// src/semantic/faiss/faiss-provider.ts
|
|
10
|
+
init_logging();
|
|
11
|
+
init_storage_paths();
|
|
12
|
+
var DEFAULT_CONFIG = {
|
|
13
|
+
dimensions: 768,
|
|
14
|
+
indexType: "hnsw",
|
|
15
|
+
hnswM: 32,
|
|
16
|
+
hnswEfConstruction: 200,
|
|
17
|
+
hnswEfSearch: 64,
|
|
18
|
+
autoSaveThreshold: 5e3,
|
|
19
|
+
// Save after 5000 new embeddings
|
|
20
|
+
persistPath: ""
|
|
21
|
+
};
|
|
22
|
+
var FaissProvider = class {
|
|
23
|
+
config;
|
|
24
|
+
client = null;
|
|
25
|
+
isInitialized = false;
|
|
26
|
+
// Track unsaved changes for auto-save
|
|
27
|
+
unsavedCount = 0;
|
|
28
|
+
lastSaveTime = 0;
|
|
29
|
+
// Background save state (avoid blocking pipeline)
|
|
30
|
+
pendingSave = null;
|
|
31
|
+
saveScheduled = false;
|
|
32
|
+
// Project context (FAISS index is per-project, per-branch)
|
|
33
|
+
projectHash = "default";
|
|
34
|
+
branchName = "main";
|
|
35
|
+
// ID set for existence checks (content stored in LibSQL, not here)
|
|
36
|
+
idSet = /* @__PURE__ */ new Set();
|
|
37
|
+
// Mutex for initialize() to prevent race condition with concurrent calls
|
|
38
|
+
initializePromise = null;
|
|
39
|
+
constructor(config = {}) {
|
|
40
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Set project context for per-project FAISS index
|
|
44
|
+
* If context changes while initialized, saves current index and loads new one
|
|
45
|
+
*/
|
|
46
|
+
async setProjectContext(projectHash, branchName = "main") {
|
|
47
|
+
const normalizedBranch = normalizeBranchName(branchName);
|
|
48
|
+
const newPersistPath = getFaissIndexPathByHash(projectHash, normalizedBranch);
|
|
49
|
+
if (this.projectHash === projectHash && this.branchName === normalizedBranch) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (this.isInitialized && this.client) {
|
|
53
|
+
log.i("FAISS", "Switching project context", {
|
|
54
|
+
from: { projectHash: this.projectHash, branchName: this.branchName },
|
|
55
|
+
to: { projectHash, branchName: normalizedBranch }
|
|
56
|
+
});
|
|
57
|
+
if (this.unsavedCount > 0) {
|
|
58
|
+
await this.save();
|
|
59
|
+
}
|
|
60
|
+
this.saveIdSet();
|
|
61
|
+
this.projectHash = projectHash;
|
|
62
|
+
this.branchName = normalizedBranch;
|
|
63
|
+
this.config.persistPath = newPersistPath;
|
|
64
|
+
this.idSet.clear();
|
|
65
|
+
this.unsavedCount = 0;
|
|
66
|
+
const loadPath = existsSync(newPersistPath) ? newPersistPath : void 0;
|
|
67
|
+
const indexConfig = {
|
|
68
|
+
dimensions: this.config.dimensions,
|
|
69
|
+
indexType: this.config.indexType,
|
|
70
|
+
metric: "l2",
|
|
71
|
+
hnswM: this.config.hnswM,
|
|
72
|
+
hnswEfConstruction: this.config.hnswEfConstruction,
|
|
73
|
+
hnswEfSearch: this.config.hnswEfSearch
|
|
74
|
+
};
|
|
75
|
+
await this.client.faissInitialize(indexConfig, loadPath);
|
|
76
|
+
if (loadPath) {
|
|
77
|
+
this.loadIdSet();
|
|
78
|
+
}
|
|
79
|
+
const stats = await this.client.faissGetStats();
|
|
80
|
+
log.i("FAISS", "Switched to new context", {
|
|
81
|
+
projectHash,
|
|
82
|
+
branchName: normalizedBranch,
|
|
83
|
+
vectors: stats.totalVectors,
|
|
84
|
+
idSetSize: this.idSet.size
|
|
85
|
+
});
|
|
86
|
+
} else {
|
|
87
|
+
this.projectHash = projectHash;
|
|
88
|
+
this.branchName = normalizedBranch;
|
|
89
|
+
this.config.persistPath = newPersistPath;
|
|
90
|
+
log.d("FAISS", "Project context set", {
|
|
91
|
+
projectHash: this.projectHash,
|
|
92
|
+
branchName: this.branchName,
|
|
93
|
+
persistPath: this.config.persistPath
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// ===========================================================================
|
|
98
|
+
// Lifecycle
|
|
99
|
+
// ===========================================================================
|
|
100
|
+
async initialize() {
|
|
101
|
+
if (this.isInitialized) return true;
|
|
102
|
+
if (this.initializePromise) {
|
|
103
|
+
return this.initializePromise;
|
|
104
|
+
}
|
|
105
|
+
this.initializePromise = this.initializeInternal();
|
|
106
|
+
return this.initializePromise;
|
|
107
|
+
}
|
|
108
|
+
async initializeInternal() {
|
|
109
|
+
try {
|
|
110
|
+
log.i("FAISS", "Initializing...");
|
|
111
|
+
this.client = getGpuClient();
|
|
112
|
+
const started = await this.client.start();
|
|
113
|
+
if (!started) {
|
|
114
|
+
log.e("FAISS", "Failed to start Faiss client");
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
const indexConfig = {
|
|
118
|
+
dimensions: this.config.dimensions,
|
|
119
|
+
indexType: this.config.indexType,
|
|
120
|
+
metric: "l2",
|
|
121
|
+
hnswM: this.config.hnswM,
|
|
122
|
+
hnswEfConstruction: this.config.hnswEfConstruction,
|
|
123
|
+
hnswEfSearch: this.config.hnswEfSearch
|
|
124
|
+
};
|
|
125
|
+
let loadPath = existsSync(this.config.persistPath) ? this.config.persistPath : void 0;
|
|
126
|
+
if (loadPath) {
|
|
127
|
+
const savedMeta = this.loadIndexMetadata();
|
|
128
|
+
if (savedMeta && savedMeta.dimensions !== this.config.dimensions) {
|
|
129
|
+
log.w("FAISS", "Dimension mismatch - dropping old index", {
|
|
130
|
+
savedDimensions: savedMeta.dimensions,
|
|
131
|
+
configDimensions: this.config.dimensions,
|
|
132
|
+
indexPath: loadPath
|
|
133
|
+
});
|
|
134
|
+
this.deleteIndexFiles();
|
|
135
|
+
loadPath = void 0;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
await this.client.faissInitialize(indexConfig, loadPath);
|
|
139
|
+
if (loadPath) {
|
|
140
|
+
this.loadIdSet();
|
|
141
|
+
}
|
|
142
|
+
this.isInitialized = true;
|
|
143
|
+
this.lastSaveTime = Date.now();
|
|
144
|
+
const stats = await this.client.faissGetStats();
|
|
145
|
+
log.i("FAISS", "Initialized", {
|
|
146
|
+
indexType: this.config.indexType,
|
|
147
|
+
dimensions: this.config.dimensions,
|
|
148
|
+
vectors: stats.totalVectors,
|
|
149
|
+
loaded: !!loadPath,
|
|
150
|
+
idSetSize: this.idSet.size
|
|
151
|
+
});
|
|
152
|
+
return true;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
log.e("FAISS", "Initialization failed", { error: error.message });
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async close() {
|
|
159
|
+
if (this.unsavedCount > 0) {
|
|
160
|
+
await this.save();
|
|
161
|
+
}
|
|
162
|
+
if (this.client && this.isInitialized) {
|
|
163
|
+
await this.client.stop();
|
|
164
|
+
}
|
|
165
|
+
this.idSet.clear();
|
|
166
|
+
this.isInitialized = false;
|
|
167
|
+
}
|
|
168
|
+
// ===========================================================================
|
|
169
|
+
// Insert Operations
|
|
170
|
+
// ===========================================================================
|
|
171
|
+
/**
|
|
172
|
+
* Add single embedding to Faiss
|
|
173
|
+
*/
|
|
174
|
+
async add(embedding) {
|
|
175
|
+
if (!this.client || !this.isInitialized) {
|
|
176
|
+
throw new Error("FaissProvider not initialized");
|
|
177
|
+
}
|
|
178
|
+
await this.client.faissAdd([embedding.id], Array.from(embedding.vector));
|
|
179
|
+
this.idSet.add(embedding.id);
|
|
180
|
+
this.unsavedCount++;
|
|
181
|
+
if (this.unsavedCount >= this.config.autoSaveThreshold) {
|
|
182
|
+
await this.save();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Batch add embeddings to Faiss
|
|
187
|
+
*/
|
|
188
|
+
async addBatch(embeddings) {
|
|
189
|
+
if (!this.client || !this.isInitialized) {
|
|
190
|
+
throw new Error("FaissProvider not initialized");
|
|
191
|
+
}
|
|
192
|
+
if (embeddings.length === 0) return;
|
|
193
|
+
const pStart = performance.now();
|
|
194
|
+
const pLog = (phase) => {
|
|
195
|
+
const elapsed = (performance.now() - pStart).toFixed(1);
|
|
196
|
+
log.i("FAISS", phase, { elapsedMs: elapsed });
|
|
197
|
+
};
|
|
198
|
+
const dim = this.config.dimensions;
|
|
199
|
+
const count = embeddings.length;
|
|
200
|
+
const ids = new Array(count);
|
|
201
|
+
const vectors = new Float32Array(count * dim);
|
|
202
|
+
for (let i = 0; i < count; i++) {
|
|
203
|
+
const emb = embeddings[i];
|
|
204
|
+
ids[i] = emb.id;
|
|
205
|
+
const normalized = simdL2Normalize(emb.vector);
|
|
206
|
+
vectors.set(normalized, i * dim);
|
|
207
|
+
this.idSet.add(emb.id);
|
|
208
|
+
}
|
|
209
|
+
pLog("F1_PREPARE_VECTORS");
|
|
210
|
+
await this.client.faissAdd(ids, vectors);
|
|
211
|
+
pLog("F2_FAISS_ADD");
|
|
212
|
+
this.unsavedCount += count;
|
|
213
|
+
log.d("FAISS", "Added embeddings", {
|
|
214
|
+
count,
|
|
215
|
+
unsaved: this.unsavedCount
|
|
216
|
+
});
|
|
217
|
+
if (this.unsavedCount >= this.config.autoSaveThreshold && !this.saveScheduled) {
|
|
218
|
+
this.saveScheduled = true;
|
|
219
|
+
this.pendingSave = this.save().catch((err) => log.w("FAISS", "Background save failed", { error: err.message })).finally(() => {
|
|
220
|
+
this.saveScheduled = false;
|
|
221
|
+
this.pendingSave = null;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Wait for any pending background save to complete
|
|
227
|
+
*/
|
|
228
|
+
async waitForPendingSave() {
|
|
229
|
+
if (this.pendingSave) {
|
|
230
|
+
await this.pendingSave;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// ===========================================================================
|
|
234
|
+
// Search Operations
|
|
235
|
+
// ===========================================================================
|
|
236
|
+
/**
|
|
237
|
+
* Search for similar vectors in Faiss
|
|
238
|
+
* Returns only id + score. Content must be fetched from LibSQL separately.
|
|
239
|
+
*/
|
|
240
|
+
async search(queryVector, limit = 10) {
|
|
241
|
+
if (!this.client || !this.isInitialized) {
|
|
242
|
+
throw new Error("FaissProvider not initialized");
|
|
243
|
+
}
|
|
244
|
+
const normalizedQuery = simdL2Normalize(queryVector);
|
|
245
|
+
const faissResults = await this.client.faissSearch(normalizedQuery, limit);
|
|
246
|
+
return faissResults.map((r) => ({
|
|
247
|
+
id: r.id,
|
|
248
|
+
similarity: r.score,
|
|
249
|
+
content: ""
|
|
250
|
+
// Content stored in LibSQL, not in Faiss
|
|
251
|
+
}));
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Batch search for multiple query vectors
|
|
255
|
+
* Returns only id + score. Content must be fetched from LibSQL separately.
|
|
256
|
+
*/
|
|
257
|
+
async batchSearch(queryVectors, limit = 10) {
|
|
258
|
+
if (!this.client || !this.isInitialized) {
|
|
259
|
+
throw new Error("FaissProvider not initialized");
|
|
260
|
+
}
|
|
261
|
+
const dim = this.config.dimensions;
|
|
262
|
+
const count = queryVectors.length;
|
|
263
|
+
const flatVectors = new Float32Array(count * dim);
|
|
264
|
+
for (let i = 0; i < count; i++) {
|
|
265
|
+
const normalized = simdL2Normalize(queryVectors[i]);
|
|
266
|
+
flatVectors.set(normalized, i * dim);
|
|
267
|
+
}
|
|
268
|
+
const faissResults = await this.client.faissBatchSearch(flatVectors, count, limit);
|
|
269
|
+
return faissResults.map(
|
|
270
|
+
(queryResults) => queryResults.map((r) => ({
|
|
271
|
+
id: r.id,
|
|
272
|
+
similarity: r.score,
|
|
273
|
+
content: ""
|
|
274
|
+
// Content stored in LibSQL, not in Faiss
|
|
275
|
+
}))
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
// ===========================================================================
|
|
279
|
+
// Persistence
|
|
280
|
+
// ===========================================================================
|
|
281
|
+
/**
|
|
282
|
+
* Save Faiss index and content cache to disk
|
|
283
|
+
*/
|
|
284
|
+
async save() {
|
|
285
|
+
if (!this.client || !this.isInitialized) {
|
|
286
|
+
throw new Error("FaissProvider not initialized");
|
|
287
|
+
}
|
|
288
|
+
if (this.unsavedCount === 0) return;
|
|
289
|
+
try {
|
|
290
|
+
const dir = dirname(this.config.persistPath);
|
|
291
|
+
if (!existsSync(dir)) {
|
|
292
|
+
mkdirSync(dir, { recursive: true });
|
|
293
|
+
}
|
|
294
|
+
await this.client.faissSave(this.config.persistPath);
|
|
295
|
+
this.saveIdSet();
|
|
296
|
+
this.saveIndexMetadata();
|
|
297
|
+
const savedCount = this.unsavedCount;
|
|
298
|
+
this.unsavedCount = 0;
|
|
299
|
+
this.lastSaveTime = Date.now();
|
|
300
|
+
log.i("FAISS", "Index saved", {
|
|
301
|
+
path: this.config.persistPath,
|
|
302
|
+
savedCount,
|
|
303
|
+
idSetSize: this.idSet.size
|
|
304
|
+
});
|
|
305
|
+
} catch (error) {
|
|
306
|
+
log.e("FAISS", "Failed to save index", { error: error.message });
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Get ID set file path
|
|
312
|
+
*/
|
|
313
|
+
getIdSetPath() {
|
|
314
|
+
return `${this.config.persistPath}.ids.json`;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Save ID set to disk (lightweight - just IDs, no content)
|
|
318
|
+
*/
|
|
319
|
+
saveIdSet() {
|
|
320
|
+
if (!this.config.persistPath) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const idSetPath = this.getIdSetPath();
|
|
324
|
+
try {
|
|
325
|
+
const ids = Array.from(this.idSet);
|
|
326
|
+
writeFileSync(idSetPath, JSON.stringify(ids), "utf-8");
|
|
327
|
+
log.d("FAISS", "ID set saved", { path: idSetPath, size: this.idSet.size });
|
|
328
|
+
} catch (error) {
|
|
329
|
+
log.w("FAISS", "Failed to save ID set", { error: error.message });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Load ID set from disk
|
|
334
|
+
*/
|
|
335
|
+
loadIdSet() {
|
|
336
|
+
const idSetPath = this.getIdSetPath();
|
|
337
|
+
if (!existsSync(idSetPath)) {
|
|
338
|
+
const legacyPath = `${this.config.persistPath}.content.json`;
|
|
339
|
+
if (existsSync(legacyPath)) {
|
|
340
|
+
try {
|
|
341
|
+
const data = readFileSync(legacyPath, "utf-8");
|
|
342
|
+
const cacheObj = JSON.parse(data);
|
|
343
|
+
this.idSet.clear();
|
|
344
|
+
for (const id of Object.keys(cacheObj)) {
|
|
345
|
+
this.idSet.add(id);
|
|
346
|
+
}
|
|
347
|
+
log.i("FAISS", "Migrated IDs from legacy content cache", { size: this.idSet.size });
|
|
348
|
+
this.saveIdSet();
|
|
349
|
+
return;
|
|
350
|
+
} catch {
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
log.d("FAISS", "No ID set file found", { path: idSetPath });
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
const data = readFileSync(idSetPath, "utf-8");
|
|
358
|
+
const ids = JSON.parse(data);
|
|
359
|
+
this.idSet.clear();
|
|
360
|
+
for (const id of ids) {
|
|
361
|
+
this.idSet.add(id);
|
|
362
|
+
}
|
|
363
|
+
log.i("FAISS", "ID set loaded", { path: idSetPath, size: this.idSet.size });
|
|
364
|
+
} catch (error) {
|
|
365
|
+
log.w("FAISS", "Failed to load ID set", { error: error.message });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Get index metadata file path
|
|
370
|
+
*/
|
|
371
|
+
getMetadataPath() {
|
|
372
|
+
return `${this.config.persistPath}.meta.json`;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Save index metadata (dimensions, etc.)
|
|
376
|
+
* Also creates faiss-base.meta.json on first save to mark base branch
|
|
377
|
+
*/
|
|
378
|
+
saveIndexMetadata() {
|
|
379
|
+
if (!this.config.persistPath) return;
|
|
380
|
+
const metaPath = this.getMetadataPath();
|
|
381
|
+
try {
|
|
382
|
+
const meta = {
|
|
383
|
+
dimensions: this.config.dimensions,
|
|
384
|
+
indexType: this.config.indexType,
|
|
385
|
+
savedAt: Date.now()
|
|
386
|
+
};
|
|
387
|
+
writeFileSync(metaPath, JSON.stringify(meta), "utf-8");
|
|
388
|
+
log.d("FAISS", "Index metadata saved", { path: metaPath, dimensions: meta.dimensions });
|
|
389
|
+
this.ensureBaseMetadata();
|
|
390
|
+
} catch (error) {
|
|
391
|
+
log.w("FAISS", "Failed to save index metadata", { error: error.message });
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Ensure faiss-base.meta.json exists (create on first indexing)
|
|
396
|
+
* This marks which branch is the base for layered index support
|
|
397
|
+
*/
|
|
398
|
+
ensureBaseMetadata() {
|
|
399
|
+
const baseMetaPath = getBaseMetaPathByHash(this.projectHash);
|
|
400
|
+
if (existsSync(baseMetaPath)) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
try {
|
|
404
|
+
const baseMeta = {
|
|
405
|
+
baseBranch: this.branchName,
|
|
406
|
+
vectorCount: this.idSet.size,
|
|
407
|
+
createdAt: Date.now(),
|
|
408
|
+
updatedAt: Date.now(),
|
|
409
|
+
dimensions: this.config.dimensions,
|
|
410
|
+
indexType: this.config.indexType
|
|
411
|
+
};
|
|
412
|
+
const dir = dirname(baseMetaPath);
|
|
413
|
+
if (!existsSync(dir)) {
|
|
414
|
+
mkdirSync(dir, { recursive: true });
|
|
415
|
+
}
|
|
416
|
+
writeFileSync(baseMetaPath, JSON.stringify(baseMeta, null, 2), "utf-8");
|
|
417
|
+
log.i("FAISS", "Base metadata created", {
|
|
418
|
+
branch: this.branchName,
|
|
419
|
+
path: baseMetaPath,
|
|
420
|
+
vectors: this.idSet.size
|
|
421
|
+
});
|
|
422
|
+
} catch (error) {
|
|
423
|
+
log.w("FAISS", "Failed to create base metadata", { error: error.message });
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Load index metadata
|
|
428
|
+
*/
|
|
429
|
+
loadIndexMetadata() {
|
|
430
|
+
const metaPath = this.getMetadataPath();
|
|
431
|
+
if (!existsSync(metaPath)) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
const data = readFileSync(metaPath, "utf-8");
|
|
436
|
+
return JSON.parse(data);
|
|
437
|
+
} catch (error) {
|
|
438
|
+
log.w("FAISS", "Failed to load index metadata", { error: error.message });
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Delete all index files (index, ID set, metadata)
|
|
444
|
+
*/
|
|
445
|
+
deleteIndexFiles() {
|
|
446
|
+
const filesToDelete = [this.config.persistPath, this.getIdSetPath(), this.getMetadataPath()];
|
|
447
|
+
for (const filePath of filesToDelete) {
|
|
448
|
+
if (filePath && existsSync(filePath)) {
|
|
449
|
+
try {
|
|
450
|
+
unlinkSync(filePath);
|
|
451
|
+
log.d("FAISS", "Deleted index file", { path: filePath });
|
|
452
|
+
} catch (error) {
|
|
453
|
+
log.w("FAISS", "Failed to delete file", { path: filePath, error: error.message });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
this.idSet.clear();
|
|
458
|
+
this.unsavedCount = 0;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Force flush and save (compatibility method)
|
|
462
|
+
*/
|
|
463
|
+
async flush() {
|
|
464
|
+
await this.waitForPendingSave();
|
|
465
|
+
const count = this.unsavedCount;
|
|
466
|
+
if (count > 0) {
|
|
467
|
+
await this.save();
|
|
468
|
+
}
|
|
469
|
+
return count;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Flush and save (alias for vector-store compatibility)
|
|
473
|
+
*/
|
|
474
|
+
async flushAndSave() {
|
|
475
|
+
await this.waitForPendingSave();
|
|
476
|
+
await this.save();
|
|
477
|
+
}
|
|
478
|
+
// ===========================================================================
|
|
479
|
+
// Index Maintenance
|
|
480
|
+
// ===========================================================================
|
|
481
|
+
/**
|
|
482
|
+
* Train IVF/PQ indexes (required before adding vectors)
|
|
483
|
+
*/
|
|
484
|
+
async train(trainingVectors) {
|
|
485
|
+
if (!this.client || !this.isInitialized) {
|
|
486
|
+
throw new Error("FaissProvider not initialized");
|
|
487
|
+
}
|
|
488
|
+
const flatVectors = [];
|
|
489
|
+
for (const v of trainingVectors) {
|
|
490
|
+
flatVectors.push(...Array.from(v));
|
|
491
|
+
}
|
|
492
|
+
await this.client.faissTrain(new Float32Array(flatVectors), trainingVectors.length);
|
|
493
|
+
log.i("FAISS", "Trained index", { vectorCount: trainingVectors.length });
|
|
494
|
+
}
|
|
495
|
+
// ===========================================================================
|
|
496
|
+
// Statistics
|
|
497
|
+
// ===========================================================================
|
|
498
|
+
async getStats() {
|
|
499
|
+
const faissStats = this.client ? await this.client.getStats() : null;
|
|
500
|
+
return {
|
|
501
|
+
totalVectors: faissStats?.totalVectors ?? 0,
|
|
502
|
+
unsavedCount: this.unsavedCount,
|
|
503
|
+
lastSaveTime: this.lastSaveTime,
|
|
504
|
+
idSetSize: this.idSet.size,
|
|
505
|
+
faissStats
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
isReady() {
|
|
509
|
+
return this.isInitialized && this.client?.isRunning() === true;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Get total vector count
|
|
513
|
+
*/
|
|
514
|
+
async getVectorCount() {
|
|
515
|
+
const stats = await this.getStats();
|
|
516
|
+
return stats.totalVectors;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Check which IDs already exist in the ID set
|
|
520
|
+
* Used for incremental indexing to skip already-indexed entities
|
|
521
|
+
*/
|
|
522
|
+
getExistingIds(ids) {
|
|
523
|
+
const existing = /* @__PURE__ */ new Set();
|
|
524
|
+
for (const id of ids) {
|
|
525
|
+
if (this.idSet.has(id)) {
|
|
526
|
+
existing.add(id);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return existing;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Check if an ID exists in the ID set
|
|
533
|
+
*/
|
|
534
|
+
hasId(id) {
|
|
535
|
+
return this.idSet.has(id);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Remove embeddings by IDs (for re-indexing changed files)
|
|
539
|
+
*/
|
|
540
|
+
async remove(ids) {
|
|
541
|
+
if (!this.client || !this.isInitialized) {
|
|
542
|
+
throw new Error("FaissProvider not initialized");
|
|
543
|
+
}
|
|
544
|
+
await this.client.faissRemove(ids);
|
|
545
|
+
for (const id of ids) {
|
|
546
|
+
this.idSet.delete(id);
|
|
547
|
+
}
|
|
548
|
+
log.d("FAISS", "Removed embeddings", { count: ids.length });
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
var faissProvider = null;
|
|
552
|
+
function getFaissProvider(config) {
|
|
553
|
+
if (!faissProvider) {
|
|
554
|
+
faissProvider = new FaissProvider(config);
|
|
555
|
+
}
|
|
556
|
+
return faissProvider;
|
|
557
|
+
}
|
|
558
|
+
async function initializeFaissProvider(config) {
|
|
559
|
+
const provider = getFaissProvider(config);
|
|
560
|
+
const success = await provider.initialize();
|
|
561
|
+
return success ? provider : null;
|
|
562
|
+
}
|
|
563
|
+
async function shutdownFaissProvider() {
|
|
564
|
+
if (faissProvider) {
|
|
565
|
+
await faissProvider.close();
|
|
566
|
+
faissProvider = null;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
export { FaissProvider, getFaissProvider, initializeFaissProvider, shutdownFaissProvider };
|
|
571
|
+
//# sourceMappingURL=chunk-S5Q7BD6J.js.map
|
|
572
|
+
//# sourceMappingURL=chunk-S5Q7BD6J.js.map
|