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,656 @@
|
|
|
1
|
+
import { getGpuClient } from './chunk-OMXOLFDN.js';
|
|
2
|
+
import { simdL2Normalize } from './chunk-QN237S7C.js';
|
|
3
|
+
import { detectBaseBranch, loadBaseMetadata, createInitialBaseMetadata, updateBaseMetadata } from './chunk-YS75L3ZS.js';
|
|
4
|
+
import { init_storage_paths, normalizeBranchName, getProjectDir } from './chunk-XK2NY7RB.js';
|
|
5
|
+
import { tryGarbageCollect } from './chunk-IMQ6WSJV.js';
|
|
6
|
+
import { init_logging, log, logMemory } from './chunk-VCCBEJQ5.js';
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
8
|
+
import { dirname, join } from 'path';
|
|
9
|
+
|
|
10
|
+
// src/semantic/faiss/layered-faiss-provider.ts
|
|
11
|
+
init_logging();
|
|
12
|
+
init_storage_paths();
|
|
13
|
+
var DEFAULT_CONFIG = {
|
|
14
|
+
dimensions: 384,
|
|
15
|
+
indexType: "hnsw",
|
|
16
|
+
hnswM: 32,
|
|
17
|
+
hnswEfConstruction: 200,
|
|
18
|
+
hnswEfSearch: 64,
|
|
19
|
+
autoSaveThreshold: 5e4
|
|
20
|
+
// Increased to reduce blocking during indexing
|
|
21
|
+
};
|
|
22
|
+
function getLayeredPaths(projectDir, branchName, baseBranch) {
|
|
23
|
+
const safeBranch = normalizeBranchName(branchName);
|
|
24
|
+
const safeBase = baseBranch ? normalizeBranchName(baseBranch) : safeBranch;
|
|
25
|
+
return {
|
|
26
|
+
// Base layer - uses base branch name (e.g., faiss-dev.bin for base branch "dev")
|
|
27
|
+
baseIndex: join(projectDir, `faiss-${safeBase}.bin`),
|
|
28
|
+
baseIds: join(projectDir, `faiss-${safeBase}.bin.ids.json`),
|
|
29
|
+
baseMeta: join(projectDir, "faiss-base.meta.json"),
|
|
30
|
+
// Delta layer (per feature branch)
|
|
31
|
+
deltaIndex: join(projectDir, `faiss-${safeBranch}.delta.bin`),
|
|
32
|
+
deltaIds: join(projectDir, `faiss-${safeBranch}.delta.ids.json`),
|
|
33
|
+
deltaMeta: join(projectDir, `faiss-${safeBranch}.delta.meta.json`),
|
|
34
|
+
tombstones: join(projectDir, `faiss-${safeBranch}.tombstones.json`)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
var LayeredFaissProvider = class _LayeredFaissProvider {
|
|
38
|
+
config;
|
|
39
|
+
client = null;
|
|
40
|
+
isInitialized = false;
|
|
41
|
+
// Project context
|
|
42
|
+
projectPath = "";
|
|
43
|
+
projectHash = "";
|
|
44
|
+
currentBranch = null;
|
|
45
|
+
baseBranch = null;
|
|
46
|
+
// Layer state
|
|
47
|
+
isOnBaseBranch = false;
|
|
48
|
+
baseIdSet = /* @__PURE__ */ new Set();
|
|
49
|
+
deltaIdSet = /* @__PURE__ */ new Set();
|
|
50
|
+
tombstones = /* @__PURE__ */ new Set();
|
|
51
|
+
// Change tracking
|
|
52
|
+
baseUnsavedCount = 0;
|
|
53
|
+
deltaUnsavedCount = 0;
|
|
54
|
+
/** Maximum delta size before auto-merge to prevent memory leaks */
|
|
55
|
+
static MAX_DELTA_SIZE = 5e3;
|
|
56
|
+
// Initialization mutex
|
|
57
|
+
initializePromise = null;
|
|
58
|
+
constructor(config = {}) {
|
|
59
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
60
|
+
}
|
|
61
|
+
// ===========================================================================
|
|
62
|
+
// Public API
|
|
63
|
+
// ===========================================================================
|
|
64
|
+
/**
|
|
65
|
+
* Check if the provider is initialized
|
|
66
|
+
*/
|
|
67
|
+
get initialized() {
|
|
68
|
+
return this.isInitialized;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get current configured dimensions
|
|
72
|
+
*/
|
|
73
|
+
getDimensions() {
|
|
74
|
+
return this.config.dimensions;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Update dimensions (e.g. when embedding model changes)
|
|
78
|
+
* Only effective before initialize() creates the FAISS index.
|
|
79
|
+
*/
|
|
80
|
+
setDimensions(dimensions) {
|
|
81
|
+
if (this.config.dimensions !== dimensions) {
|
|
82
|
+
log.i("LAYERED_FAISS", "dimensions_updated", { from: this.config.dimensions, to: dimensions });
|
|
83
|
+
this.config.dimensions = dimensions;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// ===========================================================================
|
|
87
|
+
// Lifecycle
|
|
88
|
+
// ===========================================================================
|
|
89
|
+
/**
|
|
90
|
+
* Initialize the provider for a specific project and branch
|
|
91
|
+
* v6: CRITICAL FIX - Check if projectHash changed and reinitialize if needed!
|
|
92
|
+
* Without this, switching projects would continue using the first project's index.
|
|
93
|
+
*/
|
|
94
|
+
async initialize(projectPath, projectHash, branchName) {
|
|
95
|
+
if (this.initializePromise && this.projectHash === projectHash) {
|
|
96
|
+
return this.initializePromise;
|
|
97
|
+
}
|
|
98
|
+
if (this.projectHash && this.projectHash !== projectHash) {
|
|
99
|
+
log.i("LAYERED_FAISS", "project_switch", {
|
|
100
|
+
from: this.projectHash,
|
|
101
|
+
to: projectHash
|
|
102
|
+
});
|
|
103
|
+
if (this.isInitialized) {
|
|
104
|
+
log.i("LAYERED_FAISS", "saving_before_switch", {
|
|
105
|
+
project: this.projectHash,
|
|
106
|
+
baseUnsaved: this.baseUnsavedCount,
|
|
107
|
+
deltaUnsaved: this.deltaUnsavedCount
|
|
108
|
+
});
|
|
109
|
+
await this.save();
|
|
110
|
+
}
|
|
111
|
+
this.isInitialized = false;
|
|
112
|
+
this.initializePromise = null;
|
|
113
|
+
}
|
|
114
|
+
this.initializePromise = this.initializeInternal(projectPath, projectHash, branchName);
|
|
115
|
+
return this.initializePromise;
|
|
116
|
+
}
|
|
117
|
+
async initializeInternal(projectPath, projectHash, branchName) {
|
|
118
|
+
try {
|
|
119
|
+
this.projectPath = projectPath;
|
|
120
|
+
this.projectHash = projectHash;
|
|
121
|
+
this.currentBranch = normalizeBranchName(branchName);
|
|
122
|
+
this.baseIdSet.clear();
|
|
123
|
+
this.deltaIdSet.clear();
|
|
124
|
+
this.tombstones.clear();
|
|
125
|
+
this.baseUnsavedCount = 0;
|
|
126
|
+
this.deltaUnsavedCount = 0;
|
|
127
|
+
const projectDir = getProjectDir(projectPath);
|
|
128
|
+
if (!existsSync(projectDir)) {
|
|
129
|
+
mkdirSync(projectDir, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
this.client = getGpuClient();
|
|
132
|
+
const started = await this.client.start();
|
|
133
|
+
if (!started) {
|
|
134
|
+
log.e("LAYERED_FAISS", "client_start_fail");
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
this.baseBranch = detectBaseBranch(projectPath);
|
|
138
|
+
this.isOnBaseBranch = this.baseBranch === this.currentBranch;
|
|
139
|
+
log.i("LAYERED_FAISS", "init", {
|
|
140
|
+
project: projectHash,
|
|
141
|
+
current: this.currentBranch,
|
|
142
|
+
base: this.baseBranch,
|
|
143
|
+
isOnBase: this.isOnBaseBranch
|
|
144
|
+
});
|
|
145
|
+
await this.loadLayers();
|
|
146
|
+
this.isInitialized = true;
|
|
147
|
+
return true;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
log.e("LAYERED_FAISS", "init_fail", { err: String(error) });
|
|
150
|
+
this.initializePromise = null;
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Load the appropriate layers based on current branch
|
|
156
|
+
*/
|
|
157
|
+
async loadLayers() {
|
|
158
|
+
const projectDir = getProjectDir(this.projectPath);
|
|
159
|
+
const paths = getLayeredPaths(projectDir, this.currentBranch, this.baseBranch);
|
|
160
|
+
log.d("LAYERED_FAISS", "load_layers", {
|
|
161
|
+
projectDir,
|
|
162
|
+
projectPath: this.projectPath,
|
|
163
|
+
currentBranch: this.currentBranch,
|
|
164
|
+
baseBranch: this.baseBranch,
|
|
165
|
+
baseIndexPath: paths.baseIndex,
|
|
166
|
+
baseIndexExists: existsSync(paths.baseIndex),
|
|
167
|
+
baseIdsPath: paths.baseIds,
|
|
168
|
+
baseIdsExists: existsSync(paths.baseIds)
|
|
169
|
+
});
|
|
170
|
+
await this.loadBaseLayer(paths);
|
|
171
|
+
if (!this.isOnBaseBranch) {
|
|
172
|
+
await this.loadDeltaLayer(paths);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Load base layer index
|
|
177
|
+
*/
|
|
178
|
+
async loadBaseLayer(paths) {
|
|
179
|
+
const indexConfig = {
|
|
180
|
+
dimensions: this.config.dimensions,
|
|
181
|
+
indexType: this.config.indexType,
|
|
182
|
+
metric: "l2",
|
|
183
|
+
hnswM: this.config.hnswM,
|
|
184
|
+
hnswEfConstruction: this.config.hnswEfConstruction,
|
|
185
|
+
hnswEfSearch: this.config.hnswEfSearch
|
|
186
|
+
};
|
|
187
|
+
const baseIndexExists = existsSync(paths.baseIndex);
|
|
188
|
+
const loadPath = baseIndexExists ? paths.baseIndex : void 0;
|
|
189
|
+
await this.client.faissInitialize(indexConfig, loadPath);
|
|
190
|
+
if (baseIndexExists && existsSync(paths.baseIds)) {
|
|
191
|
+
try {
|
|
192
|
+
const data = readFileSync(paths.baseIds, "utf-8");
|
|
193
|
+
const ids = JSON.parse(data);
|
|
194
|
+
this.baseIdSet = new Set(ids);
|
|
195
|
+
log.d("LAYERED_FAISS", "base_ids_loaded", { count: this.baseIdSet.size });
|
|
196
|
+
} catch (error) {
|
|
197
|
+
log.w("LAYERED_FAISS", "base_ids_load_fail", { err: String(error) });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const stats = await this.client.faissGetStats();
|
|
201
|
+
log.i("LAYERED_FAISS", "base_loaded", {
|
|
202
|
+
vectors: stats.totalVectors,
|
|
203
|
+
ids: this.baseIdSet.size,
|
|
204
|
+
fromFile: baseIndexExists
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Load delta layer for feature branch
|
|
209
|
+
*/
|
|
210
|
+
async loadDeltaLayer(paths) {
|
|
211
|
+
if (existsSync(paths.tombstones)) {
|
|
212
|
+
try {
|
|
213
|
+
const data = readFileSync(paths.tombstones, "utf-8");
|
|
214
|
+
const tombstoneList = JSON.parse(data);
|
|
215
|
+
this.tombstones = new Set(tombstoneList);
|
|
216
|
+
log.d("LAYERED_FAISS", "tombstones_loaded", { count: this.tombstones.size });
|
|
217
|
+
} catch (error) {
|
|
218
|
+
log.w("LAYERED_FAISS", "tombstones_load_fail", { err: String(error) });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (existsSync(paths.deltaIds)) {
|
|
222
|
+
try {
|
|
223
|
+
const data = readFileSync(paths.deltaIds, "utf-8");
|
|
224
|
+
const ids = JSON.parse(data);
|
|
225
|
+
this.deltaIdSet = new Set(ids);
|
|
226
|
+
log.d("LAYERED_FAISS", "delta_ids_loaded", { count: this.deltaIdSet.size });
|
|
227
|
+
} catch (error) {
|
|
228
|
+
log.w("LAYERED_FAISS", "delta_ids_load_fail", { err: String(error) });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// ===========================================================================
|
|
233
|
+
// Branch switching
|
|
234
|
+
// ===========================================================================
|
|
235
|
+
/**
|
|
236
|
+
* Switch to a different branch
|
|
237
|
+
*/
|
|
238
|
+
async switchBranch(branchName) {
|
|
239
|
+
const normalizedBranch = normalizeBranchName(branchName);
|
|
240
|
+
if (this.currentBranch === normalizedBranch) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
log.i("LAYERED_FAISS", "branch_switch", {
|
|
244
|
+
from: this.currentBranch,
|
|
245
|
+
to: normalizedBranch
|
|
246
|
+
});
|
|
247
|
+
await this.save();
|
|
248
|
+
this.currentBranch = normalizedBranch;
|
|
249
|
+
this.isOnBaseBranch = this.baseBranch === normalizedBranch;
|
|
250
|
+
this.deltaIdSet.clear();
|
|
251
|
+
this.tombstones.clear();
|
|
252
|
+
this.deltaUnsavedCount = 0;
|
|
253
|
+
if (!this.isOnBaseBranch) {
|
|
254
|
+
const projectDir = getProjectDir(this.projectPath);
|
|
255
|
+
const paths = getLayeredPaths(projectDir, normalizedBranch, this.baseBranch);
|
|
256
|
+
await this.loadDeltaLayer(paths);
|
|
257
|
+
}
|
|
258
|
+
log.i("LAYERED_FAISS", "branch_switched", {
|
|
259
|
+
branch: normalizedBranch,
|
|
260
|
+
isOnBase: this.isOnBaseBranch,
|
|
261
|
+
deltaIds: this.deltaIdSet.size,
|
|
262
|
+
tombstones: this.tombstones.size
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// ===========================================================================
|
|
266
|
+
// Search
|
|
267
|
+
// ===========================================================================
|
|
268
|
+
/**
|
|
269
|
+
* Search across all layers
|
|
270
|
+
*
|
|
271
|
+
* Algorithm:
|
|
272
|
+
* 1. Search base index for top K*2 results
|
|
273
|
+
* 2. Filter out tombstones
|
|
274
|
+
* 3. If on feature branch, search delta for top K*2
|
|
275
|
+
* 4. Merge and deduplicate (delta takes precedence)
|
|
276
|
+
* 5. Sort by similarity and return top K
|
|
277
|
+
*/
|
|
278
|
+
async search(queryVector, limit) {
|
|
279
|
+
if (!this.isInitialized || !this.client) {
|
|
280
|
+
log.w("LAYERED_FAISS", "search called but not initialized", {
|
|
281
|
+
isInit: this.isInitialized,
|
|
282
|
+
hasClient: !!this.client
|
|
283
|
+
});
|
|
284
|
+
return [];
|
|
285
|
+
}
|
|
286
|
+
const normalizedQuery = simdL2Normalize(queryVector);
|
|
287
|
+
log.d("LAYERED_FAISS", "search", {
|
|
288
|
+
inputLen: queryVector.length,
|
|
289
|
+
normalizedLen: normalizedQuery.length,
|
|
290
|
+
limit,
|
|
291
|
+
normalizedSample: Array.from(normalizedQuery.slice(0, 3))
|
|
292
|
+
});
|
|
293
|
+
const searchLimit = Math.min(limit * 2, 200);
|
|
294
|
+
const results = [];
|
|
295
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
296
|
+
if (!this.isOnBaseBranch && this.deltaIdSet.size > 0) ;
|
|
297
|
+
log.d("LAYERED_FAISS", "calling faissSearch", {
|
|
298
|
+
vectorLen: normalizedQuery.length,
|
|
299
|
+
vectorType: normalizedQuery.constructor.name,
|
|
300
|
+
searchLimit
|
|
301
|
+
});
|
|
302
|
+
const baseResults = await this.client.faissSearch(normalizedQuery, searchLimit);
|
|
303
|
+
for (const result of baseResults) {
|
|
304
|
+
if (seenIds.has(result.id)) continue;
|
|
305
|
+
if (this.tombstones.has(result.id)) continue;
|
|
306
|
+
if (this.deltaIdSet.has(result.id)) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
results.push({
|
|
310
|
+
id: result.id,
|
|
311
|
+
similarity: 1 - result.distance,
|
|
312
|
+
// Convert L2 distance to similarity
|
|
313
|
+
source: "base"
|
|
314
|
+
});
|
|
315
|
+
seenIds.add(result.id);
|
|
316
|
+
}
|
|
317
|
+
for (const id of this.deltaIdSet) {
|
|
318
|
+
if (!seenIds.has(id) && results.length < limit) {
|
|
319
|
+
const baseResult = baseResults.find((r) => r.id === id);
|
|
320
|
+
if (baseResult) {
|
|
321
|
+
results.push({
|
|
322
|
+
id: baseResult.id,
|
|
323
|
+
similarity: 1 - baseResult.distance,
|
|
324
|
+
source: "delta"
|
|
325
|
+
});
|
|
326
|
+
seenIds.add(id);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return results.sort((a, b) => b.similarity - a.similarity).slice(0, limit);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Search and return in VectorStore-compatible format
|
|
334
|
+
*/
|
|
335
|
+
async searchForVectorStore(queryVector, limit) {
|
|
336
|
+
const results = await this.search(queryVector, limit);
|
|
337
|
+
return results.map((r) => ({
|
|
338
|
+
id: r.id,
|
|
339
|
+
content: "",
|
|
340
|
+
// Content is not stored in FAISS, retrieved separately
|
|
341
|
+
similarity: r.similarity,
|
|
342
|
+
metadata: { source: r.source }
|
|
343
|
+
}));
|
|
344
|
+
}
|
|
345
|
+
// ===========================================================================
|
|
346
|
+
// Add / Remove
|
|
347
|
+
// ===========================================================================
|
|
348
|
+
/**
|
|
349
|
+
* Add a vector embedding
|
|
350
|
+
*/
|
|
351
|
+
async add(embedding) {
|
|
352
|
+
if (!this.isInitialized || !this.client) {
|
|
353
|
+
return { success: false, target: "base", error: "Not initialized" };
|
|
354
|
+
}
|
|
355
|
+
const normalizedVector = simdL2Normalize(embedding.vector);
|
|
356
|
+
if (this.isOnBaseBranch) {
|
|
357
|
+
await this.client.faissAdd([embedding.id], normalizedVector);
|
|
358
|
+
this.baseIdSet.add(embedding.id);
|
|
359
|
+
this.baseUnsavedCount++;
|
|
360
|
+
if (this.baseUnsavedCount >= this.config.autoSaveThreshold) {
|
|
361
|
+
await this.saveBase();
|
|
362
|
+
}
|
|
363
|
+
return { success: true, target: "base" };
|
|
364
|
+
} else {
|
|
365
|
+
this.tombstones.delete(embedding.id);
|
|
366
|
+
this.deltaIdSet.add(embedding.id);
|
|
367
|
+
this.deltaUnsavedCount++;
|
|
368
|
+
log.i("LAYERED_FAISS", "add_to_delta", {
|
|
369
|
+
id: embedding.id.slice(0, 50),
|
|
370
|
+
branch: this.currentBranch,
|
|
371
|
+
deltaUnsaved: this.deltaUnsavedCount
|
|
372
|
+
});
|
|
373
|
+
await this.client.faissAdd([embedding.id], normalizedVector);
|
|
374
|
+
if (this.deltaUnsavedCount >= this.config.autoSaveThreshold) {
|
|
375
|
+
await this.saveDelta();
|
|
376
|
+
}
|
|
377
|
+
return { success: true, target: "delta" };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Add multiple embeddings in batch (optimized - single Named Pipe call)
|
|
382
|
+
*/
|
|
383
|
+
async addBatch(embeddings) {
|
|
384
|
+
if (embeddings.length === 0) {
|
|
385
|
+
return [];
|
|
386
|
+
}
|
|
387
|
+
if (!this.isInitialized || !this.client) {
|
|
388
|
+
return embeddings.map(() => ({ success: false, target: "base", error: "Not initialized" }));
|
|
389
|
+
}
|
|
390
|
+
log.d("LAYERED_FAISS", "addBatch_called", {
|
|
391
|
+
count: embeddings.length,
|
|
392
|
+
branch: this.currentBranch,
|
|
393
|
+
isOnBase: this.isOnBaseBranch
|
|
394
|
+
});
|
|
395
|
+
const startTime = performance.now();
|
|
396
|
+
const allIds = [];
|
|
397
|
+
const dimensions = this.config.dimensions;
|
|
398
|
+
const allVectors = new Float32Array(embeddings.length * dimensions);
|
|
399
|
+
for (let i = 0; i < embeddings.length; i++) {
|
|
400
|
+
const embedding = embeddings[i];
|
|
401
|
+
const normalized = simdL2Normalize(embedding.vector);
|
|
402
|
+
allIds.push(embedding.id);
|
|
403
|
+
allVectors.set(normalized, i * dimensions);
|
|
404
|
+
if (this.isOnBaseBranch) {
|
|
405
|
+
this.baseIdSet.add(embedding.id);
|
|
406
|
+
} else {
|
|
407
|
+
this.tombstones.delete(embedding.id);
|
|
408
|
+
this.deltaIdSet.add(embedding.id);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
await this.client.faissAdd(allIds, allVectors);
|
|
412
|
+
if (this.isOnBaseBranch) {
|
|
413
|
+
this.baseUnsavedCount += embeddings.length;
|
|
414
|
+
if (this.baseUnsavedCount >= this.config.autoSaveThreshold) {
|
|
415
|
+
await this.saveBase();
|
|
416
|
+
}
|
|
417
|
+
} else {
|
|
418
|
+
this.deltaUnsavedCount += embeddings.length;
|
|
419
|
+
if (this.deltaUnsavedCount >= this.config.autoSaveThreshold) {
|
|
420
|
+
await this.saveDelta();
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const elapsed = performance.now() - startTime;
|
|
424
|
+
log.i("LAYERED_FAISS", "addBatch_complete", {
|
|
425
|
+
count: embeddings.length,
|
|
426
|
+
elapsed: `${elapsed.toFixed(1)}ms`,
|
|
427
|
+
speed: `${Math.round(embeddings.length / (elapsed / 1e3))}/s`,
|
|
428
|
+
target: this.isOnBaseBranch ? "base" : "delta",
|
|
429
|
+
deltaIdSetSize: this.deltaIdSet.size,
|
|
430
|
+
tombstonesSize: this.tombstones.size
|
|
431
|
+
});
|
|
432
|
+
if (!this.isOnBaseBranch && this.deltaIdSet.size > _LayeredFaissProvider.MAX_DELTA_SIZE) {
|
|
433
|
+
log.i("LAYERED_FAISS", "auto_merge_delta", {
|
|
434
|
+
deltaSize: this.deltaIdSet.size,
|
|
435
|
+
tombstones: this.tombstones.size,
|
|
436
|
+
threshold: _LayeredFaissProvider.MAX_DELTA_SIZE
|
|
437
|
+
});
|
|
438
|
+
logMemory("LAYERED_FAISS", { deltaSize: this.deltaIdSet.size, tombstones: this.tombstones.size });
|
|
439
|
+
await this.saveBase();
|
|
440
|
+
if (tryGarbageCollect(true)) {
|
|
441
|
+
log.d("LAYERED_FAISS", "gc_after_merge");
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const target = this.isOnBaseBranch ? "base" : "delta";
|
|
445
|
+
return embeddings.map(() => ({ success: true, target }));
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Remove vectors by IDs (IVectorProvider interface)
|
|
449
|
+
*/
|
|
450
|
+
async remove(ids) {
|
|
451
|
+
for (const id of ids) {
|
|
452
|
+
await this.removeOne(id);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Remove a single vector by ID
|
|
457
|
+
*/
|
|
458
|
+
async removeOne(id) {
|
|
459
|
+
if (!this.isInitialized || !this.client) {
|
|
460
|
+
return { success: false, action: "not_found" };
|
|
461
|
+
}
|
|
462
|
+
if (this.isOnBaseBranch) {
|
|
463
|
+
const existed = this.baseIdSet.has(id);
|
|
464
|
+
if (existed) {
|
|
465
|
+
await this.client.faissRemove([id]);
|
|
466
|
+
this.baseIdSet.delete(id);
|
|
467
|
+
this.baseUnsavedCount++;
|
|
468
|
+
return { success: true, action: "removed_from_base" };
|
|
469
|
+
}
|
|
470
|
+
return { success: false, action: "not_found" };
|
|
471
|
+
} else {
|
|
472
|
+
if (this.deltaIdSet.has(id)) {
|
|
473
|
+
this.deltaIdSet.delete(id);
|
|
474
|
+
this.deltaUnsavedCount++;
|
|
475
|
+
return { success: true, action: "removed_from_delta" };
|
|
476
|
+
} else if (this.baseIdSet.has(id)) {
|
|
477
|
+
this.tombstones.add(id);
|
|
478
|
+
this.deltaUnsavedCount++;
|
|
479
|
+
return { success: true, action: "added_tombstone" };
|
|
480
|
+
}
|
|
481
|
+
return { success: false, action: "not_found" };
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Check if ID exists (considering tombstones)
|
|
486
|
+
*/
|
|
487
|
+
has(id) {
|
|
488
|
+
if (this.tombstones.has(id)) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
return this.deltaIdSet.has(id) || this.baseIdSet.has(id);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Get existing IDs from a list (considering tombstones)
|
|
495
|
+
* Required by IVectorProvider interface
|
|
496
|
+
*/
|
|
497
|
+
getExistingIds(ids) {
|
|
498
|
+
const existing = /* @__PURE__ */ new Set();
|
|
499
|
+
for (const id of ids) {
|
|
500
|
+
if (this.has(id)) {
|
|
501
|
+
existing.add(id);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return existing;
|
|
505
|
+
}
|
|
506
|
+
// ===========================================================================
|
|
507
|
+
// Persistence
|
|
508
|
+
// ===========================================================================
|
|
509
|
+
/**
|
|
510
|
+
* Save all layers
|
|
511
|
+
*/
|
|
512
|
+
async save() {
|
|
513
|
+
log.i("LAYERED_FAISS", "save_called", {
|
|
514
|
+
branch: this.currentBranch,
|
|
515
|
+
isOnBase: this.isOnBaseBranch,
|
|
516
|
+
baseUnsaved: this.baseUnsavedCount,
|
|
517
|
+
deltaUnsaved: this.deltaUnsavedCount
|
|
518
|
+
});
|
|
519
|
+
if (this.baseUnsavedCount > 0) {
|
|
520
|
+
await this.saveBase();
|
|
521
|
+
}
|
|
522
|
+
if (this.deltaUnsavedCount > 0) {
|
|
523
|
+
await this.saveDelta();
|
|
524
|
+
} else if (!this.isOnBaseBranch) {
|
|
525
|
+
log.w("LAYERED_FAISS", "delta_not_saved_zero_unsaved", {
|
|
526
|
+
branch: this.currentBranch,
|
|
527
|
+
deltaIdSetSize: this.deltaIdSet.size
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Save base layer
|
|
533
|
+
*/
|
|
534
|
+
async saveBase() {
|
|
535
|
+
if (!this.client || !this.isInitialized) return;
|
|
536
|
+
const projectDir = getProjectDir(this.projectPath);
|
|
537
|
+
const paths = getLayeredPaths(projectDir, this.currentBranch, this.baseBranch);
|
|
538
|
+
try {
|
|
539
|
+
const dir = dirname(paths.baseIndex);
|
|
540
|
+
if (!existsSync(dir)) {
|
|
541
|
+
mkdirSync(dir, { recursive: true });
|
|
542
|
+
}
|
|
543
|
+
await this.client.faissSave(paths.baseIndex);
|
|
544
|
+
writeFileSync(paths.baseIds, JSON.stringify([...this.baseIdSet]), "utf-8");
|
|
545
|
+
const existingMeta = loadBaseMetadata(this.projectPath);
|
|
546
|
+
if (!existingMeta) {
|
|
547
|
+
createInitialBaseMetadata(this.projectPath, this.currentBranch, this.config.dimensions, this.baseIdSet.size);
|
|
548
|
+
this.baseBranch = this.currentBranch;
|
|
549
|
+
log.i("LAYERED_FAISS", "base_meta_created", { branch: this.currentBranch });
|
|
550
|
+
} else {
|
|
551
|
+
updateBaseMetadata(this.projectPath, this.baseIdSet.size);
|
|
552
|
+
}
|
|
553
|
+
this.baseUnsavedCount = 0;
|
|
554
|
+
log.i("LAYERED_FAISS", "base_saved", { vectors: this.baseIdSet.size });
|
|
555
|
+
} catch (error) {
|
|
556
|
+
log.e("LAYERED_FAISS", "base_save_fail", { err: String(error) });
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Save delta layer
|
|
561
|
+
*/
|
|
562
|
+
async saveDelta() {
|
|
563
|
+
if (!this.isInitialized || this.isOnBaseBranch || !this.client) return;
|
|
564
|
+
const projectDir = getProjectDir(this.projectPath);
|
|
565
|
+
const paths = getLayeredPaths(projectDir, this.currentBranch, this.baseBranch);
|
|
566
|
+
try {
|
|
567
|
+
const dir = dirname(paths.deltaIds);
|
|
568
|
+
if (!existsSync(dir)) {
|
|
569
|
+
mkdirSync(dir, { recursive: true });
|
|
570
|
+
}
|
|
571
|
+
await this.client.faissSave(paths.deltaIndex || paths.baseIndex);
|
|
572
|
+
writeFileSync(paths.deltaIds, JSON.stringify([...this.deltaIdSet]), "utf-8");
|
|
573
|
+
writeFileSync(paths.tombstones, JSON.stringify([...this.tombstones]), "utf-8");
|
|
574
|
+
const deltaMeta = {
|
|
575
|
+
branchName: this.currentBranch,
|
|
576
|
+
baseBranch: this.baseBranch,
|
|
577
|
+
deltaVectorCount: this.deltaIdSet.size,
|
|
578
|
+
tombstoneCount: this.tombstones.size,
|
|
579
|
+
createdAt: Date.now(),
|
|
580
|
+
updatedAt: Date.now()
|
|
581
|
+
};
|
|
582
|
+
writeFileSync(paths.deltaMeta, JSON.stringify(deltaMeta, null, 2), "utf-8");
|
|
583
|
+
this.deltaUnsavedCount = 0;
|
|
584
|
+
log.i("LAYERED_FAISS", "delta_saved", {
|
|
585
|
+
branch: this.currentBranch,
|
|
586
|
+
deltaIds: this.deltaIdSet.size,
|
|
587
|
+
tombstones: this.tombstones.size
|
|
588
|
+
});
|
|
589
|
+
} catch (error) {
|
|
590
|
+
log.e("LAYERED_FAISS", "delta_save_fail", { err: String(error) });
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
// ===========================================================================
|
|
594
|
+
// Statistics
|
|
595
|
+
// ===========================================================================
|
|
596
|
+
/**
|
|
597
|
+
* Get statistics about the layered index
|
|
598
|
+
*/
|
|
599
|
+
async getStats() {
|
|
600
|
+
const baseVectors = this.baseIdSet.size;
|
|
601
|
+
const deltaVectors = this.deltaIdSet.size;
|
|
602
|
+
const tombstoneCount = this.tombstones.size;
|
|
603
|
+
const totalVectors = baseVectors + deltaVectors - tombstoneCount;
|
|
604
|
+
return {
|
|
605
|
+
totalVectors: Math.max(0, totalVectors),
|
|
606
|
+
baseVectors,
|
|
607
|
+
deltaVectors,
|
|
608
|
+
tombstones: tombstoneCount,
|
|
609
|
+
currentBranch: this.currentBranch,
|
|
610
|
+
baseBranch: this.baseBranch,
|
|
611
|
+
isOnBaseBranch: this.isOnBaseBranch
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Get count of searchable vectors
|
|
616
|
+
*/
|
|
617
|
+
async count() {
|
|
618
|
+
const stats = await this.getStats();
|
|
619
|
+
return stats.totalVectors;
|
|
620
|
+
}
|
|
621
|
+
// ===========================================================================
|
|
622
|
+
// Cleanup
|
|
623
|
+
// ===========================================================================
|
|
624
|
+
/**
|
|
625
|
+
* Close and save all state
|
|
626
|
+
*/
|
|
627
|
+
async close() {
|
|
628
|
+
if (!this.isInitialized) return;
|
|
629
|
+
await this.save();
|
|
630
|
+
this.isInitialized = false;
|
|
631
|
+
this.initializePromise = null;
|
|
632
|
+
log.i("LAYERED_FAISS", "closed", {
|
|
633
|
+
project: this.projectHash,
|
|
634
|
+
branch: this.currentBranch
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
var layeredProviderInstance = null;
|
|
639
|
+
function getLayeredFaissProvider(config) {
|
|
640
|
+
if (!layeredProviderInstance) {
|
|
641
|
+
layeredProviderInstance = new LayeredFaissProvider(config);
|
|
642
|
+
} else if (config?.dimensions && config.dimensions !== layeredProviderInstance.getDimensions()) {
|
|
643
|
+
layeredProviderInstance.setDimensions(config.dimensions);
|
|
644
|
+
}
|
|
645
|
+
return layeredProviderInstance;
|
|
646
|
+
}
|
|
647
|
+
async function shutdownLayeredFaissProvider() {
|
|
648
|
+
if (layeredProviderInstance) {
|
|
649
|
+
await layeredProviderInstance.close();
|
|
650
|
+
layeredProviderInstance = null;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
export { LayeredFaissProvider, getLayeredFaissProvider, shutdownLayeredFaissProvider };
|
|
655
|
+
//# sourceMappingURL=chunk-B3H5NS3I.js.map
|
|
656
|
+
//# sourceMappingURL=chunk-B3H5NS3I.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import {a as a$2,b as b$1}from'./chunk-IFKMF76K.js';import {I as I$1,a as a$1}from'./chunk-SV3WKUNV.js';import {b as b$2}from'./chunk-AIZJT6AG.js';import {e,d as d$1}from'./chunk-OG62YNVF.js';import {R,I as I$2}from'./chunk-4BI7EBPQ.js';import {a}from'./chunk-DXB73IDG.js';import {existsSync}from'fs';import {dirname,join}from'path';import {fileURLToPath}from'url';import {connect}from'net';R();I$1();e();R();var _={cudaBatchCosine:{dim384:2e3,dim768:1e3,dim1024:500,dim8192:50},cudaNormalize:{dim384:5e3,dim768:2e3,dim1024:1e3,dim8192:100},faissIndexBuild:{bulkInsertThreshold:500,rebuildThreshold:1e3,incrementalDeltaPercent:30},faissSearch:{minIndexSize:100,batchQueryThreshold:10}},l={..._};function A(n,e){return e<=384?n.dim384:e<=768?n.dim768:e<=1024?n.dim1024:n.dim8192}function b(n,e){let s=A(l.cudaBatchCosine,e),t=n>=s;return process.env.ADAPTIVE_DEBUG==="true"&&I$2.d("ADAPTIVE","cudaBatchCosine decision",{count:n,dim:e,threshold:s,useCuda:t}),t}function B(n,e){let s=A(l.cudaNormalize,e);return n>=s}function J(n,e){return n>=l.faissIndexBuild.rebuildThreshold||e!==void 0}function $(n,e=1){return n>=l.faissSearch.minIndexSize||e>=l.faissSearch.batchQueryThreshold}function E(n,e,s,t=1){let r=[],i="libsql-diskann";s&&J(n)?(i="faiss-hnsw",r.push(`Faiss HNSW for rebuild (${n} vectors)`)):n>1e4&&(i="faiss-ivf",r.push(`Faiss IVF for large index (${n} vectors)`));let o="libsql";$(n,t)&&(o=t>1?"faiss":"hybrid",r.push(`Faiss search (index=${n}, queries=${t})`));let c=b(n,e);return c&&r.push(`CUDA reranking (${n} \xD7 ${e}-dim)`),{indexStrategy:i,searchStrategy:o,useCudaForReranking:c,reason:r.join("; ")||"Default CPU strategy"}}var H=process.platform==="win32"?"\\\\.\\pipe\\ultracode-gpu-":"/tmp/ultracode-gpu-",P=class{buffer;length=0;initialSize;constructor(e=65536){this.initialSize=e,this.buffer=Buffer.allocUnsafe(e);}ensureCapacity(e){let s=this.length+e;if(s>this.buffer.length){let t=Math.max(this.buffer.length*2,s),r=Buffer.allocUnsafe(t);this.buffer.copy(r,0,0,this.length),this.buffer=r;}}shrinkIfEmpty(){this.length===0&&this.buffer.length>this.initialSize&&(this.buffer=Buffer.allocUnsafe(this.initialSize));}append(e){this.ensureCapacity(e.length),e.copy(this.buffer,this.length),this.length+=e.length;}get dataLength(){return this.length}readUInt32LE(e){return this.buffer.readUInt32LE(e)}subarray(e,s){return this.buffer.subarray(e,s)}consume(e){e>=this.length?this.length=0:(this.buffer.copy(this.buffer,0,e,this.length),this.length-=e),this.shrinkIfEmpty();}reset(){this.length=0,this.shrinkIfEmpty();}};var y=class{socket=null;options;pipePath;connected=false;pendingRequest=null;responseBuffer=new P(65536);constructor(e){this.options={timeout:3e4,...e},this.pipePath=H+e.pipeId;}get path(){return this.pipePath}get isConnected(){return this.connected}async connect(){if(!this.connected)return new Promise((e,s)=>{this.socket=connect(this.pipePath),this.socket.on("connect",()=>{this.connected=true,this.options.onConnect?.(),e();}),this.socket.on("data",t=>{this.handleData(t);}),this.socket.on("error",t=>{this.connected=false,this.options.onError?.(t),this.pendingRequest&&(this.pendingRequest.reject(t),this.pendingRequest=null),s(t);}),this.socket.on("close",()=>{this.connected=false,this.options.onDisconnect?.(),this.pendingRequest&&(this.pendingRequest.reject(new Error("Connection closed")),this.pendingRequest=null);});})}handleData(e){if(this.responseBuffer.append(e),this.responseBuffer.dataLength>=4){let s=this.responseBuffer.readUInt32LE(0);if(this.responseBuffer.dataLength>=4+s){let t=Buffer.from(this.responseBuffer.subarray(4,4+s));this.responseBuffer.consume(4+s),this.pendingRequest&&(this.pendingRequest.resolve(t),this.pendingRequest=null);}}}async send(e){if(!this.socket||!this.connected)throw new Error("Not connected");if(this.pendingRequest)throw new Error("Another request is pending");let s=new AbortController,t=new Promise((i,o)=>{this.pendingRequest={resolve:u=>{s.abort(),i(u);},reject:u=>{s.abort(),o(u);}};let c=Buffer.allocUnsafe(4+e.length);c.writeUInt32LE(e.length,0),e.copy(c,4),this.socket.write(c);}),r=(async()=>{if(await b$2(this.options.timeout??3e4),s.signal.aborted)return new Promise(()=>{});throw this.pendingRequest=null,new Error("Request timeout")})();return Promise.race([t,r])}disconnect(){this.socket&&(this.socket.destroy(),this.socket=null,this.connected=false);}};function G(n,e){let s=JSON.stringify(n),t=Buffer.from(s);if(!e||e.length===0){let o=Buffer.allocUnsafe(4+t.length);return o.writeUInt32LE(t.length,0),t.copy(o,4),o}let r=Buffer.from(e.buffer,e.byteOffset,e.byteLength),i=Buffer.allocUnsafe(4+t.length+r.length);return i.writeUInt32LE(t.length,0),t.copy(i,4),r.copy(i,4+t.length),i}function x(n){let e=n.readUInt32LE(0),s=n.subarray(4,4+e).toString(),t=JSON.parse(s);if(n.length>4+e){let r=4+e,i=n.length-r,o=n.subarray(r),c=new Float32Array(i/4);for(let u=0;u<c.length;u++)c[u]=o.readFloatLE(u*4);return {header:t,vectors:c}}return {header:t}}function M(n){return n.success===false&&"error"in n}function d(n){return M(n)?n.error:"Unknown GPU error"}function I(n){return n.type==="faiss.add"}function U(n){return n.type==="faiss.search"}function N(n){return n.type==="faiss.batchSearch"}function W(n){return n.type==="faiss.train"}function D(n){return n.type==="cuda.cosine"}function L(n){return n.type==="cuda.batchCosine"}function T(n){if(I(n)){let{vectors:s,...t}=n;return {vectors:s instanceof Float32Array?s:new Float32Array(s),headerData:t}}if(U(n)){let{vector:s,...t}=n;return {vectors:s instanceof Float32Array?s:new Float32Array(s),headerData:t}}if(N(n)){let{vectors:s,...t}=n;return {vectors:s instanceof Float32Array?s:new Float32Array(s),headerData:t}}if(W(n)){let{vectors:s,...t}=n;return {vectors:s instanceof Float32Array?s:new Float32Array(s),headerData:t}}if(D(n)){let{a:s,b:t,...r}=n,i=s instanceof Float32Array?s:new Float32Array(s),o=t instanceof Float32Array?t:new Float32Array(t),c=i.length+o.length,u=new Float32Array(c);u.set(i,0),u.set(o,i.length);let g={...r,dimensions:i.length};return {vectors:u,headerData:g}}if(L(n)){let{query:s,database:t,...r}=n,i=s instanceof Float32Array?s:new Float32Array(s),o=t.map(p=>p instanceof Float32Array?p:new Float32Array(p)),c=i.length+o.reduce((p,O)=>p+O.length,0),u=new Float32Array(c);u.set(i,0);let g=i.length;for(let p of o)u.set(p,g),g+=p.length;let z={...r,dimensions:i.length,queryCount:1,databaseCount:o.length};return {vectors:u,headerData:z}}return {headerData:{...n}}}var Y={nodePath:"node",workerPath:"",timeout:3e4,autoRestart:true,maxRestarts:3,forceSubprocess:false},w=class{config;worker=null;pendingRequests=new Map;requestId=0;restartCount=0;isShuttingDown=false;faissInitConfig=null;responseBuffer="";_cudaAvailable=false;namedPipeClient=null;namedPipePath=null;useNamedPipe=false;startPromise=null;namedPipeRequestQueue=Promise.resolve({});constructor(e={}){this.config={...Y,...e},this.config.workerPath||(this.config.workerPath=this.findWorkerPath()),this.registerCleanupHandlers();}registerCleanupHandlers(){let e=()=>{if(this.worker&&!this.isShuttingDown){this.isShuttingDown=true,I$2.d("GPU","Parent exiting, killing worker..."),this.namedPipeClient&&(this.namedPipeClient.disconnect(),this.namedPipeClient=null),this.worker.kill(),this.worker=null;for(let[,s]of this.pendingRequests)s.abortController.abort(),s.reject(new Error("Client shutting down"));this.pendingRequests.clear();}};process.on("exit",e),process.on("SIGINT",e),process.on("SIGTERM",e);}findWorkerPath(){let e=[],s=dirname(fileURLToPath(import.meta.url)),t=s;s.includes("chunks")?t=dirname(s):s.includes("semantic")&&(t=dirname(dirname(s))),e.push(join(t,"semantic","gpu","gpu-worker.js")),e.push(join(s,"gpu-worker.js"));try{let i=a.resolve("ultracode");e.push(join(dirname(i),"semantic/gpu/gpu-worker.js"));}catch{}e.push(join(process.cwd(),"dist/semantic/gpu/gpu-worker.js"));for(let i of e)if(existsSync(i))return I$2.d("GPU","Found worker at",{path:i}),i;let r=e[0]??join(process.cwd(),"dist/semantic/gpu/gpu-worker.js");return I$2.w("GPU","Worker not found, using fallback",{path:r,candidates:e}),r}async start(){return this.startPromise?this.startPromise:this.worker?true:process.env.ULTRACODE_NO_SUBPROCESS==="1"?(I$2.d("GPU","SKIPPED (ULTRACODE_NO_SUBPROCESS=1)"),false):(this.startPromise=this.startInternal(),this.startPromise)}async startInternal(){try{I$2.i("GPU","Starting worker",{nodePath:this.config.nodePath,workerPath:this.config.workerPath});let e=process.platform==="win32";if(!(typeof Bun<"u")||e){let{spawn:t}=await import('child_process');this.worker=t(this.config.nodePath,["--expose-gc",this.config.workerPath],{stdio:["pipe","pipe","pipe"],windowsHide:!0}),this.worker.unref(),this.worker.stderr&&this.worker.stderr.on("data",r=>{let i=r.toString().trim();i&&I$2.d("GPU",i);});}else {let r=globalThis.Bun?.spawn([this.config.nodePath,"--expose-gc",this.config.workerPath],{stdin:"pipe",stdout:"pipe",stderr:"pipe"}),i={stdin:r.stdin,stdout:r.stdout,stderr:r.stderr,pid:r.pid,kill:()=>r.kill(),on:(o,c)=>{(o==="exit"||o==="close")&&r.exited.then(u=>c(u));}};this.worker=i;}this.setupStdoutReader(),await this.waitForReady();try{let t=await this.cudaInfo();this._cudaAvailable=t.available;}catch{}return I$2.i("GPU","Worker started",{cuda:this._cudaAvailable}),!0}catch(e){return I$2.e("GPU","Failed to start worker",{error:e.message}),false}}setupStdoutReader(){this.worker?.stdout&&(this.worker.stdout.on("data",e=>{this.responseBuffer+=e.toString(),this.processResponseBuffer();}),this.worker.stdout.on("error",e=>{this.isShuttingDown||(I$2.e("GPU","stdout read error",{error:e.message}),this.handleWorkerCrash());}),this.worker.on("exit",e=>{this.isShuttingDown||(I$2.w("GPU","Worker exited",{code:e}),this.handleWorkerCrash());}));}processResponseBuffer(){let e=this.responseBuffer.split(`
|
|
2
|
+
`);this.responseBuffer=e.pop()||"";for(let s of e)if(s.trim())try{let t=JSON.parse(s);if(t.type==="pipe.ready"&&t.path){this.namedPipePath=t.path,I$2.d("GPU","Received pipe.ready",{path:t.path});continue}let r=t;this.handleResponse(r);}catch{}}handleResponse(e){let s=this.pendingRequests.entries().next().value;if(s){let[t,r]=s;r.abortController.abort(),this.pendingRequests.delete(t),r.resolve(e);}}async waitForReady(){let s=Date.now();for(;Date.now()-s<5e3&&!this.namedPipePath;)await d$1(50);if(this.namedPipePath)try{let t=this.namedPipePath.split(/[/\\]/).pop()?.replace("ultracode-gpu-","").replace(".sock","")||"";this.namedPipeClient=new y({pipeId:t,timeout:this.config.timeout,onConnect:()=>{I$2.i("GPU","Connected to Named Pipe",{path:this.namedPipePath});},onDisconnect:()=>{I$2.d("GPU","Named Pipe disconnected"),this.useNamedPipe=!1;},onError:r=>{I$2.w("GPU","Named Pipe error",{error:r.message}),this.useNamedPipe=!1;}}),await this.namedPipeClient.connect(),this.useNamedPipe=!0,I$2.i("GPU","Using Named Pipe for binary IPC");}catch(t){I$2.w("GPU","Failed to connect to Named Pipe, using stdin/stdout",{error:t.message}),this.namedPipeClient=null,this.useNamedPipe=false;}else I$2.d("GPU","Named Pipe not available, using stdin/stdout"),await d$1(100);}async handleWorkerCrash(){if(!this.isShuttingDown){for(let[,e]of this.pendingRequests)e.abortController.abort(),e.reject(new Error("Worker crashed"));this.pendingRequests.clear(),this.worker=null,this.config.autoRestart&&this.restartCount<this.config.maxRestarts&&(this.restartCount++,await this.start()&&this.faissInitConfig&&await this.faissInitialize(this.faissInitConfig));}}async stop(){if(this.isShuttingDown=true,this.namedPipeClient&&(this.namedPipeClient.disconnect(),this.namedPipeClient=null,this.useNamedPipe=false),this.worker){try{this.worker.stdin?.write(`${JSON.stringify({type:"shutdown"})}
|
|
3
|
+
`),await d$1(500);}catch{}if(this.worker){try{this.worker.kill("SIGTERM");}catch{}this.worker=null;}}for(let[,e]of this.pendingRequests)e.abortController.abort(),e.reject(new Error("Client shutting down"));this.pendingRequests.clear();}isRunning(){return this.worker!==null&&!this.isShuttingDown}async sendRequest(e){if(!this.worker?.stdin)throw new Error("Worker not running");if(this.useNamedPipe&&this.namedPipeClient?.isConnected)return I$2.i("GPU","Using Named Pipe",{type:e.type}),this.sendNamedPipeRequest(e);let s=JSON.stringify(e);I$2.i("GPU","Using stdin/stdout JSON",{type:e.type,jsonLen:s.length});let t=++this.requestId,r=new AbortController,i=new Promise((c,u)=>{this.pendingRequests.set(t,{resolve:c,reject:u,abortController:r}),this.worker.stdin.write(`${s}
|
|
4
|
+
`);}),o=(async()=>{if(await d$1(this.config.timeout),r.signal.aborted)return new Promise(()=>{});throw this.pendingRequests.delete(t),new Error(`Request timeout: ${e.type}`)})();return Promise.race([i,o])}async sendNamedPipeRequest(e){let t=this.namedPipeRequestQueue.catch(()=>{}).then(()=>this.sendNamedPipeRequestInternal(e));return this.namedPipeRequestQueue=t,t}async sendNamedPipeRequestInternal(e){if(!this.namedPipeClient?.isConnected)throw new Error("Named Pipe not connected");let{vectors:s,headerData:t}=T(e),r=G(t,s),i=await this.namedPipeClient.send(r),{header:o}=x(i);return o}async faissInitialize(e,s){this.faissInitConfig=e,this.worker||await this.start();let t=await this.sendRequest({type:"faiss.init",config:e,loadPath:s});if(!t.success)throw new Error(d(t));return t}async faissAdd(e,s){let t=s instanceof Float32Array?Array.from(s):s,r=await this.sendRequest({type:"faiss.add",ids:e,vectors:t});if(!r.success)throw new Error(d(r));return r}async faissSearch(e,s){let t=e instanceof Float32Array?Array.from(e):e;I$2.i("GPU","faissSearch input",{vectorLen:t?.length,k:s,isArray:Array.isArray(t)});let r={type:"faiss.search",vector:t,k:s};I$2.i("GPU","faissSearch request",{hasVector:!!r.vector,vectorLen:r.vector?.length,keys:Object.keys(r)});let i=await this.sendRequest(r);if(!i.success)throw new Error(d(i));return i.results}async faissBatchSearch(e,s,t){let r=e instanceof Float32Array?Array.from(e):e,i=await this.sendRequest({type:"faiss.batchSearch",vectors:r,nQueries:s,k:t});if(!i.success)throw new Error(d(i));return i.results}async faissTrain(e,s){let t=e instanceof Float32Array?Array.from(e):e,r=await this.sendRequest({type:"faiss.train",vectors:t,nVectors:s});if(!r.success)throw new Error(d(r));return r}async faissSave(e){let s=e||join(a$1(),"faiss-index.bin"),t=await this.sendRequest({type:"faiss.save",path:s});if(!t.success)throw new Error(d(t));return t}async faissLoad(e){let s=await this.sendRequest({type:"faiss.load",path:e});if(!s.success)throw new Error(d(s));return s}async faissRemove(e){let s=await this.sendRequest({type:"faiss.remove",ids:e});if(!s.success)throw new Error(d(s))}async faissGetStats(){let e=await this.sendRequest({type:"faiss.stats"});if(!e.success)throw new Error(d(e));return e.stats}async cudaInfo(){let e=await this.sendRequest({type:"cuda.info"});if(!e.success)throw new Error(d(e));return e}isCudaAvailable(){return this._cudaAvailable}async cudaCosineSimilarity(e,s){let t=e instanceof Float32Array?Array.from(e):e,r=s instanceof Float32Array?Array.from(s):s,i=await this.sendRequest({type:"cuda.cosine",a:t,b:r});if(!i.success)throw new Error(d(i));return i.similarity}async cudaBatchCosineSimilarity(e,s){let t=e instanceof Float32Array?Array.from(e):e,r=s.map(o=>o instanceof Float32Array?Array.from(o):o),i=await this.sendRequest({type:"cuda.batchCosine",query:t,database:r});if(!i.success)throw new Error(d(i));return new Float32Array(i.similarities)}async cudaEuclideanDistance(e,s){let t=e instanceof Float32Array?Array.from(e):e,r=s instanceof Float32Array?Array.from(s):s,i=await this.sendRequest({type:"cuda.euclidean",a:t,b:r});if(!i.success)throw new Error(d(i));return i.distance}async cudaNormalizeVectors(e){let s=e.map(r=>r instanceof Float32Array?Array.from(r):r),t=await this.sendRequest({type:"cuda.normalize",vectors:s});if(!t.success)throw new Error(d(t));return t.vectors.map(r=>new Float32Array(r))}async adaptiveBatchCosineSimilarity(e,s){let t=s.length,r=e.length;if(this._cudaAvailable&&b(t,r))try{return {similarities:await this.cudaBatchCosineSimilarity(e,s),usedCuda:!0}}catch(o){I$2.w("GPU","CUDA batch cosine failed, falling back to CPU",{error:o.message});}let i=new Float32Array(t);for(let o=0;o<t;o++)i[o]=a$2(e,s[o]);return {similarities:i,usedCuda:false}}async adaptiveNormalizeVectors(e){let s=e.length,t=e[0]?.length??0;if(this._cudaAvailable&&B(s,t))try{return {normalized:await this.cudaNormalizeVectors(e),usedCuda:!0}}catch(i){I$2.w("GPU","CUDA normalize failed, falling back to CPU",{error:i.message});}return {normalized:e.map(i=>{let o=new Float32Array(i);return b$1(o)}),usedCuda:false}}getRecommendedStrategy(e,s,t,r=1){return E(e,s,t,r)}async getStats(){let e=await this.sendRequest({type:"stats"});if(!e.success)throw new Error(d(e));return e}},f=null;function ve(n){return f||(I$2.i("GPU","Using subprocess mode"),f=new w(n)),f}async function qe(){f&&(await f.stop(),f=null);}export{E as a,ve as b,qe as c};
|