ruvector 0.2.21 → 0.2.23
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/README.md +2 -2
- package/bin/cli.js +160 -0
- package/package.json +9 -5
- package/src/decompiler/api-prober.js +302 -0
- package/src/decompiler/model-decompiler.js +423 -0
- package/dist/analysis/complexity.d.ts +0 -52
- package/dist/analysis/complexity.d.ts.map +0 -1
- package/dist/analysis/complexity.js +0 -146
- package/dist/analysis/index.d.ts +0 -15
- package/dist/analysis/index.d.ts.map +0 -1
- package/dist/analysis/index.js +0 -38
- package/dist/analysis/patterns.d.ts +0 -71
- package/dist/analysis/patterns.d.ts.map +0 -1
- package/dist/analysis/patterns.js +0 -243
- package/dist/analysis/security.d.ts +0 -51
- package/dist/analysis/security.d.ts.map +0 -1
- package/dist/analysis/security.js +0 -139
- package/dist/core/adaptive-embedder.d.ts +0 -156
- package/dist/core/adaptive-embedder.d.ts.map +0 -1
- package/dist/core/adaptive-embedder.js +0 -838
- package/dist/core/agentdb-fast.d.ts +0 -149
- package/dist/core/agentdb-fast.d.ts.map +0 -1
- package/dist/core/agentdb-fast.js +0 -301
- package/dist/core/ast-parser.d.ts +0 -108
- package/dist/core/ast-parser.d.ts.map +0 -1
- package/dist/core/ast-parser.js +0 -602
- package/dist/core/attention-fallbacks.d.ts +0 -321
- package/dist/core/attention-fallbacks.d.ts.map +0 -1
- package/dist/core/attention-fallbacks.js +0 -552
- package/dist/core/cluster-wrapper.d.ts +0 -148
- package/dist/core/cluster-wrapper.d.ts.map +0 -1
- package/dist/core/cluster-wrapper.js +0 -271
- package/dist/core/coverage-router.d.ts +0 -88
- package/dist/core/coverage-router.d.ts.map +0 -1
- package/dist/core/coverage-router.js +0 -315
- package/dist/core/diff-embeddings.d.ts +0 -93
- package/dist/core/diff-embeddings.d.ts.map +0 -1
- package/dist/core/diff-embeddings.js +0 -334
- package/dist/core/gnn-wrapper.d.ts +0 -143
- package/dist/core/gnn-wrapper.d.ts.map +0 -1
- package/dist/core/gnn-wrapper.js +0 -213
- package/dist/core/graph-algorithms.d.ts +0 -83
- package/dist/core/graph-algorithms.d.ts.map +0 -1
- package/dist/core/graph-algorithms.js +0 -514
- package/dist/core/graph-wrapper.d.ts +0 -147
- package/dist/core/graph-wrapper.d.ts.map +0 -1
- package/dist/core/graph-wrapper.js +0 -299
- package/dist/core/index.d.ts +0 -48
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -89
- package/dist/core/intelligence-engine.d.ts +0 -258
- package/dist/core/intelligence-engine.d.ts.map +0 -1
- package/dist/core/intelligence-engine.js +0 -1030
- package/dist/core/learning-engine.d.ts +0 -160
- package/dist/core/learning-engine.d.ts.map +0 -1
- package/dist/core/learning-engine.js +0 -589
- package/dist/core/neural-embeddings.d.ts +0 -393
- package/dist/core/neural-embeddings.d.ts.map +0 -1
- package/dist/core/neural-embeddings.js +0 -1091
- package/dist/core/neural-perf.d.ts +0 -331
- package/dist/core/neural-perf.d.ts.map +0 -1
- package/dist/core/neural-perf.js +0 -704
- package/dist/core/onnx/loader.js +0 -348
- package/dist/core/onnx/pkg/LICENSE +0 -21
- package/dist/core/onnx/pkg/loader.js +0 -348
- package/dist/core/onnx/pkg/package.json +0 -3
- package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm.d.ts +0 -112
- package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm.js +0 -5
- package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.js +0 -638
- package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.wasm +0 -0
- package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_bg.wasm.d.ts +0 -29
- package/dist/core/onnx/pkg/ruvector_onnx_embeddings_wasm_cjs.js +0 -127
- package/dist/core/onnx-embedder.d.ts +0 -105
- package/dist/core/onnx-embedder.d.ts.map +0 -1
- package/dist/core/onnx-embedder.js +0 -410
- package/dist/core/onnx-llm.d.ts +0 -206
- package/dist/core/onnx-llm.d.ts.map +0 -1
- package/dist/core/onnx-llm.js +0 -430
- package/dist/core/onnx-optimized.d.ts +0 -109
- package/dist/core/onnx-optimized.d.ts.map +0 -1
- package/dist/core/onnx-optimized.js +0 -419
- package/dist/core/parallel-intelligence.d.ts +0 -109
- package/dist/core/parallel-intelligence.d.ts.map +0 -1
- package/dist/core/parallel-intelligence.js +0 -340
- package/dist/core/parallel-workers.d.ts +0 -177
- package/dist/core/parallel-workers.d.ts.map +0 -1
- package/dist/core/parallel-workers.js +0 -671
- package/dist/core/router-wrapper.d.ts +0 -62
- package/dist/core/router-wrapper.d.ts.map +0 -1
- package/dist/core/router-wrapper.js +0 -209
- package/dist/core/rvf-wrapper.d.ts +0 -86
- package/dist/core/rvf-wrapper.d.ts.map +0 -1
- package/dist/core/rvf-wrapper.js +0 -102
- package/dist/core/sona-wrapper.d.ts +0 -226
- package/dist/core/sona-wrapper.d.ts.map +0 -1
- package/dist/core/sona-wrapper.js +0 -282
- package/dist/core/tensor-compress.d.ts +0 -134
- package/dist/core/tensor-compress.d.ts.map +0 -1
- package/dist/core/tensor-compress.js +0 -432
- package/dist/index.d.ts +0 -105
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -221
- package/dist/services/embedding-service.d.ts +0 -136
- package/dist/services/embedding-service.d.ts.map +0 -1
- package/dist/services/embedding-service.js +0 -294
- package/dist/services/index.d.ts +0 -6
- package/dist/services/index.d.ts.map +0 -1
- package/dist/services/index.js +0 -26
- package/dist/types.d.ts +0 -145
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/workers/benchmark.d.ts +0 -44
- package/dist/workers/benchmark.d.ts.map +0 -1
- package/dist/workers/benchmark.js +0 -230
- package/dist/workers/index.d.ts +0 -10
- package/dist/workers/index.d.ts.map +0 -1
- package/dist/workers/index.js +0 -25
- package/dist/workers/native-worker.d.ts +0 -76
- package/dist/workers/native-worker.d.ts.map +0 -1
- package/dist/workers/native-worker.js +0 -490
- package/dist/workers/types.d.ts +0 -69
- package/dist/workers/types.d.ts.map +0 -1
- package/dist/workers/types.js +0 -7
|
@@ -1,838 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* AdaptiveEmbedder - Micro-LoRA Style Optimization for ONNX Embeddings
|
|
4
|
-
*
|
|
5
|
-
* Applies continual learning techniques to frozen ONNX embeddings:
|
|
6
|
-
*
|
|
7
|
-
* 1. MICRO-LORA ADAPTERS
|
|
8
|
-
* - Low-rank projection layers (rank 2-8) on top of frozen embeddings
|
|
9
|
-
* - Domain-specific fine-tuning with minimal parameters
|
|
10
|
-
* - ~0.1% of base model parameters
|
|
11
|
-
*
|
|
12
|
-
* 2. CONTRASTIVE LEARNING
|
|
13
|
-
* - Files edited together → embeddings closer
|
|
14
|
-
* - Semantic clustering from trajectories
|
|
15
|
-
* - Online learning from user behavior
|
|
16
|
-
*
|
|
17
|
-
* 3. EWC++ (Elastic Weight Consolidation)
|
|
18
|
-
* - Prevents catastrophic forgetting
|
|
19
|
-
* - Consolidates important adaptations
|
|
20
|
-
* - Fisher information regularization
|
|
21
|
-
*
|
|
22
|
-
* 4. MEMORY-AUGMENTED RETRIEVAL
|
|
23
|
-
* - Episodic memory for context-aware embeddings
|
|
24
|
-
* - Attention over past similar embeddings
|
|
25
|
-
* - Domain prototype learning
|
|
26
|
-
*
|
|
27
|
-
* Architecture:
|
|
28
|
-
* ONNX(text) → [frozen 384d] → LoRA_A → LoRA_B → [adapted 384d]
|
|
29
|
-
* (384×r) (r×384)
|
|
30
|
-
*/
|
|
31
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.AdaptiveEmbedder = void 0;
|
|
33
|
-
exports.getAdaptiveEmbedder = getAdaptiveEmbedder;
|
|
34
|
-
exports.initAdaptiveEmbedder = initAdaptiveEmbedder;
|
|
35
|
-
const onnx_embedder_1 = require("./onnx-embedder");
|
|
36
|
-
// ============================================================================
|
|
37
|
-
// Optimized Micro-LoRA Layer with Float32Array and Caching
|
|
38
|
-
// ============================================================================
|
|
39
|
-
/**
|
|
40
|
-
* Low-rank adaptation layer for embeddings (OPTIMIZED)
|
|
41
|
-
* Implements: output = input + scale * (input @ A @ B)
|
|
42
|
-
*
|
|
43
|
-
* Optimizations:
|
|
44
|
-
* - Float32Array for 2-3x faster math operations
|
|
45
|
-
* - Flattened matrices for cache-friendly access
|
|
46
|
-
* - Pre-allocated buffers to avoid GC pressure
|
|
47
|
-
* - LRU embedding cache for repeated inputs
|
|
48
|
-
*/
|
|
49
|
-
class MicroLoRA {
|
|
50
|
-
constructor(dim, rank, scale = 0.1) {
|
|
51
|
-
// EWC Fisher information (importance weights)
|
|
52
|
-
this.fisherA = null;
|
|
53
|
-
this.fisherB = null;
|
|
54
|
-
this.savedA = null;
|
|
55
|
-
this.savedB = null;
|
|
56
|
-
// LRU cache for repeated embeddings (key: hash, value: output)
|
|
57
|
-
this.cache = new Map();
|
|
58
|
-
this.cacheMaxSize = 256;
|
|
59
|
-
this.dim = dim;
|
|
60
|
-
this.rank = rank;
|
|
61
|
-
this.scale = scale;
|
|
62
|
-
// Initialize A with small random values (Xavier-like)
|
|
63
|
-
// Initialize B to EXACTLY ZERO so untrained LoRA is identity
|
|
64
|
-
// This preserves semantic signal until training occurs
|
|
65
|
-
const stdA = Math.sqrt(2 / (dim + rank));
|
|
66
|
-
this.A = this.initFlatMatrix(dim, rank, stdA);
|
|
67
|
-
this.B = new Float32Array(rank * dim); // Zero-initialized (identity)
|
|
68
|
-
// Pre-allocate buffers
|
|
69
|
-
this.hiddenBuffer = new Float32Array(rank);
|
|
70
|
-
this.outputBuffer = new Float32Array(dim);
|
|
71
|
-
}
|
|
72
|
-
initFlatMatrix(rows, cols, std) {
|
|
73
|
-
const arr = new Float32Array(rows * cols);
|
|
74
|
-
for (let i = 0; i < arr.length; i++) {
|
|
75
|
-
arr[i] = (Math.random() - 0.5) * 2 * std;
|
|
76
|
-
}
|
|
77
|
-
return arr;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Fast hash for cache key (FNV-1a variant)
|
|
81
|
-
*/
|
|
82
|
-
hashInput(input) {
|
|
83
|
-
let h = 2166136261;
|
|
84
|
-
const len = Math.min(input.length, 32); // Sample first 32 for speed
|
|
85
|
-
for (let i = 0; i < len; i++) {
|
|
86
|
-
h ^= Math.floor(input[i] * 10000);
|
|
87
|
-
h = Math.imul(h, 16777619);
|
|
88
|
-
}
|
|
89
|
-
return h.toString(36);
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Forward pass: input + scale * (input @ A @ B)
|
|
93
|
-
* OPTIMIZED with Float32Array and loop unrolling
|
|
94
|
-
*/
|
|
95
|
-
forward(input) {
|
|
96
|
-
// Check cache first
|
|
97
|
-
const cacheKey = this.hashInput(input);
|
|
98
|
-
const cached = this.cache.get(cacheKey);
|
|
99
|
-
if (cached) {
|
|
100
|
-
return Array.from(cached);
|
|
101
|
-
}
|
|
102
|
-
// Zero the hidden buffer
|
|
103
|
-
this.hiddenBuffer.fill(0);
|
|
104
|
-
// Compute input @ A (dim → rank) - SIMD-friendly loop
|
|
105
|
-
// Unroll by 4 for better pipelining
|
|
106
|
-
const dim4 = this.dim - (this.dim % 4);
|
|
107
|
-
for (let r = 0; r < this.rank; r++) {
|
|
108
|
-
let sum = 0;
|
|
109
|
-
const rOffset = r;
|
|
110
|
-
// Unrolled loop
|
|
111
|
-
for (let d = 0; d < dim4; d += 4) {
|
|
112
|
-
const aIdx = d * this.rank + rOffset;
|
|
113
|
-
sum += input[d] * this.A[aIdx];
|
|
114
|
-
sum += input[d + 1] * this.A[aIdx + this.rank];
|
|
115
|
-
sum += input[d + 2] * this.A[aIdx + 2 * this.rank];
|
|
116
|
-
sum += input[d + 3] * this.A[aIdx + 3 * this.rank];
|
|
117
|
-
}
|
|
118
|
-
// Remainder
|
|
119
|
-
for (let d = dim4; d < this.dim; d++) {
|
|
120
|
-
sum += input[d] * this.A[d * this.rank + rOffset];
|
|
121
|
-
}
|
|
122
|
-
this.hiddenBuffer[r] = sum;
|
|
123
|
-
}
|
|
124
|
-
// Compute hidden @ B (rank → dim) and add residual
|
|
125
|
-
// Copy input to output buffer first
|
|
126
|
-
for (let d = 0; d < this.dim; d++) {
|
|
127
|
-
this.outputBuffer[d] = input[d];
|
|
128
|
-
}
|
|
129
|
-
// Add scaled LoRA contribution
|
|
130
|
-
for (let d = 0; d < this.dim; d++) {
|
|
131
|
-
let delta = 0;
|
|
132
|
-
for (let r = 0; r < this.rank; r++) {
|
|
133
|
-
delta += this.hiddenBuffer[r] * this.B[r * this.dim + d];
|
|
134
|
-
}
|
|
135
|
-
this.outputBuffer[d] += this.scale * delta;
|
|
136
|
-
}
|
|
137
|
-
// Cache result (LRU eviction if full)
|
|
138
|
-
if (this.cache.size >= this.cacheMaxSize) {
|
|
139
|
-
const firstKey = this.cache.keys().next().value;
|
|
140
|
-
if (firstKey)
|
|
141
|
-
this.cache.delete(firstKey);
|
|
142
|
-
}
|
|
143
|
-
this.cache.set(cacheKey, new Float32Array(this.outputBuffer));
|
|
144
|
-
return Array.from(this.outputBuffer);
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Clear cache (call after weight updates)
|
|
148
|
-
*/
|
|
149
|
-
clearCache() {
|
|
150
|
-
this.cache.clear();
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Backward pass with contrastive loss
|
|
154
|
-
* Pulls positive pairs closer, pushes negatives apart
|
|
155
|
-
* OPTIMIZED: Uses Float32Array buffers
|
|
156
|
-
*/
|
|
157
|
-
backward(anchor, positive, negatives, lr, ewcLambda = 0) {
|
|
158
|
-
if (!positive && negatives.length === 0)
|
|
159
|
-
return 0;
|
|
160
|
-
// Clear cache since weights will change
|
|
161
|
-
this.clearCache();
|
|
162
|
-
// Compute adapted embeddings
|
|
163
|
-
const anchorOut = this.forward(anchor);
|
|
164
|
-
const positiveOut = positive ? this.forward(positive) : null;
|
|
165
|
-
const negativeOuts = negatives.map(n => this.forward(n));
|
|
166
|
-
// Contrastive loss with temperature scaling
|
|
167
|
-
const temp = 0.07;
|
|
168
|
-
let loss = 0;
|
|
169
|
-
if (positiveOut) {
|
|
170
|
-
// Positive similarity
|
|
171
|
-
const posSim = this.cosineSimilarity(anchorOut, positiveOut) / temp;
|
|
172
|
-
// Negative similarities
|
|
173
|
-
const negSims = negativeOuts.map(n => this.cosineSimilarity(anchorOut, n) / temp);
|
|
174
|
-
// InfoNCE loss
|
|
175
|
-
const maxSim = Math.max(posSim, ...negSims);
|
|
176
|
-
const expPos = Math.exp(posSim - maxSim);
|
|
177
|
-
const expNegs = negSims.reduce((sum, s) => sum + Math.exp(s - maxSim), 0);
|
|
178
|
-
loss = -Math.log(expPos / (expPos + expNegs) + 1e-8);
|
|
179
|
-
// Compute gradients (simplified)
|
|
180
|
-
const gradScale = lr * this.scale;
|
|
181
|
-
// Update A based on gradient direction (flattened access)
|
|
182
|
-
for (let d = 0; d < this.dim; d++) {
|
|
183
|
-
for (let r = 0; r < this.rank; r++) {
|
|
184
|
-
const idx = d * this.rank + r;
|
|
185
|
-
// Gradient from positive (pull closer)
|
|
186
|
-
const pOutR = r < positiveOut.length ? positiveOut[r] : 0;
|
|
187
|
-
const aOutR = r < anchorOut.length ? anchorOut[r] : 0;
|
|
188
|
-
const gradA = anchor[d] * (pOutR - aOutR) * gradScale;
|
|
189
|
-
this.A[idx] += gradA;
|
|
190
|
-
// EWC regularization
|
|
191
|
-
if (ewcLambda > 0 && this.fisherA && this.savedA) {
|
|
192
|
-
this.A[idx] -= ewcLambda * this.fisherA[idx] * (this.A[idx] - this.savedA[idx]);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
// Update B (flattened access)
|
|
197
|
-
for (let r = 0; r < this.rank; r++) {
|
|
198
|
-
const anchorR = r < anchor.length ? anchor[r] : 0;
|
|
199
|
-
for (let d = 0; d < this.dim; d++) {
|
|
200
|
-
const idx = r * this.dim + d;
|
|
201
|
-
const gradB = anchorR * (positiveOut[d] - anchorOut[d]) * gradScale * 0.1;
|
|
202
|
-
this.B[idx] += gradB;
|
|
203
|
-
if (ewcLambda > 0 && this.fisherB && this.savedB) {
|
|
204
|
-
this.B[idx] -= ewcLambda * this.fisherB[idx] * (this.B[idx] - this.savedB[idx]);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return loss;
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* EWC consolidation - save current weights and compute Fisher information
|
|
213
|
-
* OPTIMIZED: Uses Float32Array
|
|
214
|
-
*/
|
|
215
|
-
consolidate(embeddings) {
|
|
216
|
-
// Save current weights
|
|
217
|
-
this.savedA = new Float32Array(this.A);
|
|
218
|
-
this.savedB = new Float32Array(this.B);
|
|
219
|
-
// Estimate Fisher information (diagonal approximation)
|
|
220
|
-
this.fisherA = new Float32Array(this.dim * this.rank);
|
|
221
|
-
this.fisherB = new Float32Array(this.rank * this.dim);
|
|
222
|
-
const numEmb = embeddings.length;
|
|
223
|
-
for (const emb of embeddings) {
|
|
224
|
-
// Accumulate squared gradients as Fisher estimate
|
|
225
|
-
for (let d = 0; d < this.dim; d++) {
|
|
226
|
-
const embD = emb[d] * emb[d] / numEmb;
|
|
227
|
-
for (let r = 0; r < this.rank; r++) {
|
|
228
|
-
this.fisherA[d * this.rank + r] += embD;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
// Clear cache after consolidation
|
|
233
|
-
this.clearCache();
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Optimized cosine similarity with early termination
|
|
237
|
-
*/
|
|
238
|
-
cosineSimilarity(a, b) {
|
|
239
|
-
let dot = 0, normA = 0, normB = 0;
|
|
240
|
-
const len = Math.min(a.length, b.length);
|
|
241
|
-
// Unrolled loop for speed
|
|
242
|
-
const len4 = len - (len % 4);
|
|
243
|
-
for (let i = 0; i < len4; i += 4) {
|
|
244
|
-
dot += a[i] * b[i] + a[i + 1] * b[i + 1] + a[i + 2] * b[i + 2] + a[i + 3] * b[i + 3];
|
|
245
|
-
normA += a[i] * a[i] + a[i + 1] * a[i + 1] + a[i + 2] * a[i + 2] + a[i + 3] * a[i + 3];
|
|
246
|
-
normB += b[i] * b[i] + b[i + 1] * b[i + 1] + b[i + 2] * b[i + 2] + b[i + 3] * b[i + 3];
|
|
247
|
-
}
|
|
248
|
-
// Remainder
|
|
249
|
-
for (let i = len4; i < len; i++) {
|
|
250
|
-
dot += a[i] * b[i];
|
|
251
|
-
normA += a[i] * a[i];
|
|
252
|
-
normB += b[i] * b[i];
|
|
253
|
-
}
|
|
254
|
-
return dot / (Math.sqrt(normA * normB) + 1e-8);
|
|
255
|
-
}
|
|
256
|
-
getParams() {
|
|
257
|
-
return this.dim * this.rank + this.rank * this.dim;
|
|
258
|
-
}
|
|
259
|
-
getCacheStats() {
|
|
260
|
-
return {
|
|
261
|
-
size: this.cache.size,
|
|
262
|
-
maxSize: this.cacheMaxSize,
|
|
263
|
-
hitRate: 0, // Would need hit counter for accurate tracking
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Export weights as 2D arrays for serialization
|
|
268
|
-
*/
|
|
269
|
-
export() {
|
|
270
|
-
// Convert flattened Float32Array back to 2D number[][]
|
|
271
|
-
const A = [];
|
|
272
|
-
for (let d = 0; d < this.dim; d++) {
|
|
273
|
-
const row = [];
|
|
274
|
-
for (let r = 0; r < this.rank; r++) {
|
|
275
|
-
row.push(this.A[d * this.rank + r]);
|
|
276
|
-
}
|
|
277
|
-
A.push(row);
|
|
278
|
-
}
|
|
279
|
-
const B = [];
|
|
280
|
-
for (let r = 0; r < this.rank; r++) {
|
|
281
|
-
const row = [];
|
|
282
|
-
for (let d = 0; d < this.dim; d++) {
|
|
283
|
-
row.push(this.B[r * this.dim + d]);
|
|
284
|
-
}
|
|
285
|
-
B.push(row);
|
|
286
|
-
}
|
|
287
|
-
return { A, B };
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Import weights from 2D arrays
|
|
291
|
-
*/
|
|
292
|
-
import(weights) {
|
|
293
|
-
// Convert 2D number[][] to flattened Float32Array
|
|
294
|
-
for (let d = 0; d < this.dim && d < weights.A.length; d++) {
|
|
295
|
-
for (let r = 0; r < this.rank && r < weights.A[d].length; r++) {
|
|
296
|
-
this.A[d * this.rank + r] = weights.A[d][r];
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
for (let r = 0; r < this.rank && r < weights.B.length; r++) {
|
|
300
|
-
for (let d = 0; d < this.dim && d < weights.B[r].length; d++) {
|
|
301
|
-
this.B[r * this.dim + d] = weights.B[r][d];
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
// Clear cache after import
|
|
305
|
-
this.clearCache();
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
// ============================================================================
|
|
309
|
-
// Domain Prototype Learning (OPTIMIZED with Float32Array)
|
|
310
|
-
// ============================================================================
|
|
311
|
-
class PrototypeMemory {
|
|
312
|
-
constructor(maxPrototypes = 50, dimension = 384) {
|
|
313
|
-
this.prototypes = new Map();
|
|
314
|
-
this.maxPrototypes = maxPrototypes;
|
|
315
|
-
this.scratchBuffer = new Float32Array(dimension);
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Update prototype with new embedding (online mean update)
|
|
319
|
-
* OPTIMIZED: Uses Float32Array internally
|
|
320
|
-
*/
|
|
321
|
-
update(domain, embedding) {
|
|
322
|
-
const existing = this.prototypes.get(domain);
|
|
323
|
-
if (existing) {
|
|
324
|
-
// Online mean update: new_mean = old_mean + (x - old_mean) / n
|
|
325
|
-
const n = existing.count + 1;
|
|
326
|
-
const invN = 1 / n;
|
|
327
|
-
// Unrolled update loop
|
|
328
|
-
const len = Math.min(embedding.length, existing.centroid.length);
|
|
329
|
-
const len4 = len - (len % 4);
|
|
330
|
-
for (let i = 0; i < len4; i += 4) {
|
|
331
|
-
const d0 = embedding[i] - existing.centroid[i];
|
|
332
|
-
const d1 = embedding[i + 1] - existing.centroid[i + 1];
|
|
333
|
-
const d2 = embedding[i + 2] - existing.centroid[i + 2];
|
|
334
|
-
const d3 = embedding[i + 3] - existing.centroid[i + 3];
|
|
335
|
-
existing.centroid[i] += d0 * invN;
|
|
336
|
-
existing.centroid[i + 1] += d1 * invN;
|
|
337
|
-
existing.centroid[i + 2] += d2 * invN;
|
|
338
|
-
existing.centroid[i + 3] += d3 * invN;
|
|
339
|
-
existing.variance += d0 * (embedding[i] - existing.centroid[i]);
|
|
340
|
-
existing.variance += d1 * (embedding[i + 1] - existing.centroid[i + 1]);
|
|
341
|
-
existing.variance += d2 * (embedding[i + 2] - existing.centroid[i + 2]);
|
|
342
|
-
existing.variance += d3 * (embedding[i + 3] - existing.centroid[i + 3]);
|
|
343
|
-
}
|
|
344
|
-
for (let i = len4; i < len; i++) {
|
|
345
|
-
const delta = embedding[i] - existing.centroid[i];
|
|
346
|
-
existing.centroid[i] += delta * invN;
|
|
347
|
-
existing.variance += delta * (embedding[i] - existing.centroid[i]);
|
|
348
|
-
}
|
|
349
|
-
existing.count = n;
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
// Create new prototype
|
|
353
|
-
if (this.prototypes.size >= this.maxPrototypes) {
|
|
354
|
-
// Remove least used prototype
|
|
355
|
-
let minCount = Infinity;
|
|
356
|
-
let minKey = '';
|
|
357
|
-
for (const [key, proto] of this.prototypes) {
|
|
358
|
-
if (proto.count < minCount) {
|
|
359
|
-
minCount = proto.count;
|
|
360
|
-
minKey = key;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
this.prototypes.delete(minKey);
|
|
364
|
-
}
|
|
365
|
-
this.prototypes.set(domain, {
|
|
366
|
-
domain,
|
|
367
|
-
centroid: Array.from(embedding),
|
|
368
|
-
count: 1,
|
|
369
|
-
variance: 0,
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Find closest prototype and return domain-adjusted embedding
|
|
375
|
-
* OPTIMIZED: Single-pass similarity with early exit
|
|
376
|
-
*/
|
|
377
|
-
adjust(embedding) {
|
|
378
|
-
if (this.prototypes.size === 0) {
|
|
379
|
-
return { adjusted: Array.from(embedding), domain: null, confidence: 0 };
|
|
380
|
-
}
|
|
381
|
-
let bestSim = -Infinity;
|
|
382
|
-
let bestProto = null;
|
|
383
|
-
for (const proto of this.prototypes.values()) {
|
|
384
|
-
const sim = this.cosineSimilarityFast(embedding, proto.centroid);
|
|
385
|
-
if (sim > bestSim) {
|
|
386
|
-
bestSim = sim;
|
|
387
|
-
bestProto = proto;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
if (!bestProto || bestSim < 0.5) {
|
|
391
|
-
return { adjusted: Array.from(embedding), domain: null, confidence: 0 };
|
|
392
|
-
}
|
|
393
|
-
// Adjust embedding toward prototype (soft assignment)
|
|
394
|
-
const alpha = 0.1 * bestSim;
|
|
395
|
-
const oneMinusAlpha = 1 - alpha;
|
|
396
|
-
const adjusted = new Array(embedding.length);
|
|
397
|
-
// Unrolled adjustment
|
|
398
|
-
const len = embedding.length;
|
|
399
|
-
const len4 = len - (len % 4);
|
|
400
|
-
for (let i = 0; i < len4; i += 4) {
|
|
401
|
-
adjusted[i] = embedding[i] * oneMinusAlpha + bestProto.centroid[i] * alpha;
|
|
402
|
-
adjusted[i + 1] = embedding[i + 1] * oneMinusAlpha + bestProto.centroid[i + 1] * alpha;
|
|
403
|
-
adjusted[i + 2] = embedding[i + 2] * oneMinusAlpha + bestProto.centroid[i + 2] * alpha;
|
|
404
|
-
adjusted[i + 3] = embedding[i + 3] * oneMinusAlpha + bestProto.centroid[i + 3] * alpha;
|
|
405
|
-
}
|
|
406
|
-
for (let i = len4; i < len; i++) {
|
|
407
|
-
adjusted[i] = embedding[i] * oneMinusAlpha + bestProto.centroid[i] * alpha;
|
|
408
|
-
}
|
|
409
|
-
return {
|
|
410
|
-
adjusted,
|
|
411
|
-
domain: bestProto.domain,
|
|
412
|
-
confidence: bestSim,
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Fast cosine similarity with loop unrolling
|
|
417
|
-
*/
|
|
418
|
-
cosineSimilarityFast(a, b) {
|
|
419
|
-
let dot = 0, normA = 0, normB = 0;
|
|
420
|
-
const len = Math.min(a.length, b.length);
|
|
421
|
-
const len4 = len - (len % 4);
|
|
422
|
-
for (let i = 0; i < len4; i += 4) {
|
|
423
|
-
dot += a[i] * b[i] + a[i + 1] * b[i + 1] + a[i + 2] * b[i + 2] + a[i + 3] * b[i + 3];
|
|
424
|
-
normA += a[i] * a[i] + a[i + 1] * a[i + 1] + a[i + 2] * a[i + 2] + a[i + 3] * a[i + 3];
|
|
425
|
-
normB += b[i] * b[i] + b[i + 1] * b[i + 1] + b[i + 2] * b[i + 2] + b[i + 3] * b[i + 3];
|
|
426
|
-
}
|
|
427
|
-
for (let i = len4; i < len; i++) {
|
|
428
|
-
dot += a[i] * b[i];
|
|
429
|
-
normA += a[i] * a[i];
|
|
430
|
-
normB += b[i] * b[i];
|
|
431
|
-
}
|
|
432
|
-
return dot / (Math.sqrt(normA * normB) + 1e-8);
|
|
433
|
-
}
|
|
434
|
-
getPrototypes() {
|
|
435
|
-
return Array.from(this.prototypes.values());
|
|
436
|
-
}
|
|
437
|
-
export() {
|
|
438
|
-
return this.getPrototypes();
|
|
439
|
-
}
|
|
440
|
-
import(prototypes) {
|
|
441
|
-
this.prototypes.clear();
|
|
442
|
-
for (const p of prototypes) {
|
|
443
|
-
this.prototypes.set(p.domain, p);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
class EpisodicMemory {
|
|
448
|
-
constructor(capacity = 1000, dimension = 384) {
|
|
449
|
-
this.entries = [];
|
|
450
|
-
this.capacity = capacity;
|
|
451
|
-
this.dimension = dimension;
|
|
452
|
-
this.augmentBuffer = new Float32Array(dimension);
|
|
453
|
-
this.weightsBuffer = new Float32Array(Math.min(capacity, 16)); // Max k
|
|
454
|
-
}
|
|
455
|
-
add(embedding, context) {
|
|
456
|
-
if (this.entries.length >= this.capacity) {
|
|
457
|
-
// Find and remove least used entry (O(n) but infrequent)
|
|
458
|
-
let minIdx = 0;
|
|
459
|
-
let minCount = this.entries[0].useCount;
|
|
460
|
-
for (let i = 1; i < this.entries.length; i++) {
|
|
461
|
-
if (this.entries[i].useCount < minCount) {
|
|
462
|
-
minCount = this.entries[i].useCount;
|
|
463
|
-
minIdx = i;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
this.entries.splice(minIdx, 1);
|
|
467
|
-
}
|
|
468
|
-
// Convert to Float32Array and pre-compute norm
|
|
469
|
-
const emb = embedding instanceof Float32Array
|
|
470
|
-
? new Float32Array(embedding)
|
|
471
|
-
: new Float32Array(embedding);
|
|
472
|
-
let normSq = 0;
|
|
473
|
-
for (let i = 0; i < emb.length; i++) {
|
|
474
|
-
normSq += emb[i] * emb[i];
|
|
475
|
-
}
|
|
476
|
-
this.entries.push({
|
|
477
|
-
embedding: emb,
|
|
478
|
-
context,
|
|
479
|
-
timestamp: Date.now(),
|
|
480
|
-
useCount: 0,
|
|
481
|
-
normSquared: normSq,
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Retrieve similar past embeddings for context augmentation
|
|
486
|
-
* OPTIMIZED: Uses pre-computed norms for fast similarity
|
|
487
|
-
*/
|
|
488
|
-
retrieve(query, k = 5) {
|
|
489
|
-
if (this.entries.length === 0)
|
|
490
|
-
return [];
|
|
491
|
-
// Pre-compute query norm
|
|
492
|
-
let queryNormSq = 0;
|
|
493
|
-
for (let i = 0; i < query.length; i++) {
|
|
494
|
-
queryNormSq += query[i] * query[i];
|
|
495
|
-
}
|
|
496
|
-
const queryNorm = Math.sqrt(queryNormSq);
|
|
497
|
-
// Score all entries
|
|
498
|
-
const scored = [];
|
|
499
|
-
for (const entry of this.entries) {
|
|
500
|
-
// Fast dot product with loop unrolling
|
|
501
|
-
let dot = 0;
|
|
502
|
-
const len = Math.min(query.length, entry.embedding.length);
|
|
503
|
-
const len4 = len - (len % 4);
|
|
504
|
-
for (let i = 0; i < len4; i += 4) {
|
|
505
|
-
dot += query[i] * entry.embedding[i];
|
|
506
|
-
dot += query[i + 1] * entry.embedding[i + 1];
|
|
507
|
-
dot += query[i + 2] * entry.embedding[i + 2];
|
|
508
|
-
dot += query[i + 3] * entry.embedding[i + 3];
|
|
509
|
-
}
|
|
510
|
-
for (let i = len4; i < len; i++) {
|
|
511
|
-
dot += query[i] * entry.embedding[i];
|
|
512
|
-
}
|
|
513
|
-
const similarity = dot / (queryNorm * Math.sqrt(entry.normSquared) + 1e-8);
|
|
514
|
-
scored.push({ entry, similarity });
|
|
515
|
-
}
|
|
516
|
-
// Partial sort for top-k (faster than full sort for large arrays)
|
|
517
|
-
if (scored.length <= k) {
|
|
518
|
-
scored.sort((a, b) => b.similarity - a.similarity);
|
|
519
|
-
for (const s of scored)
|
|
520
|
-
s.entry.useCount++;
|
|
521
|
-
return scored.map(s => s.entry);
|
|
522
|
-
}
|
|
523
|
-
// Quick select for top-k
|
|
524
|
-
scored.sort((a, b) => b.similarity - a.similarity);
|
|
525
|
-
const topK = scored.slice(0, k);
|
|
526
|
-
for (const s of topK)
|
|
527
|
-
s.entry.useCount++;
|
|
528
|
-
return topK.map(s => s.entry);
|
|
529
|
-
}
|
|
530
|
-
/**
|
|
531
|
-
* Augment embedding with episodic memory (attention-like)
|
|
532
|
-
* OPTIMIZED: Uses pre-allocated buffers
|
|
533
|
-
*/
|
|
534
|
-
augment(embedding, k = 3) {
|
|
535
|
-
const similar = this.retrieve(embedding, k);
|
|
536
|
-
if (similar.length === 0)
|
|
537
|
-
return Array.from(embedding);
|
|
538
|
-
// Pre-compute query norm
|
|
539
|
-
let queryNormSq = 0;
|
|
540
|
-
for (let i = 0; i < embedding.length; i++) {
|
|
541
|
-
queryNormSq += embedding[i] * embedding[i];
|
|
542
|
-
}
|
|
543
|
-
const queryNorm = Math.sqrt(queryNormSq);
|
|
544
|
-
// Compute weights
|
|
545
|
-
let sumWeights = 1; // Start with 1 for query
|
|
546
|
-
for (let j = 0; j < similar.length; j++) {
|
|
547
|
-
// Fast dot product for similarity
|
|
548
|
-
let dot = 0;
|
|
549
|
-
const emb = similar[j].embedding;
|
|
550
|
-
const len = Math.min(embedding.length, emb.length);
|
|
551
|
-
for (let i = 0; i < len; i++) {
|
|
552
|
-
dot += embedding[i] * emb[i];
|
|
553
|
-
}
|
|
554
|
-
const sim = dot / (queryNorm * Math.sqrt(similar[j].normSquared) + 1e-8);
|
|
555
|
-
const weight = Math.exp(sim / 0.1);
|
|
556
|
-
this.weightsBuffer[j] = weight;
|
|
557
|
-
sumWeights += weight;
|
|
558
|
-
}
|
|
559
|
-
const invSumWeights = 1 / sumWeights;
|
|
560
|
-
// Weighted average
|
|
561
|
-
const dim = embedding.length;
|
|
562
|
-
for (let i = 0; i < dim; i++) {
|
|
563
|
-
let sum = embedding[i]; // Query contribution
|
|
564
|
-
for (let j = 0; j < similar.length; j++) {
|
|
565
|
-
sum += this.weightsBuffer[j] * similar[j].embedding[i];
|
|
566
|
-
}
|
|
567
|
-
this.augmentBuffer[i] = sum * invSumWeights;
|
|
568
|
-
}
|
|
569
|
-
return Array.from(this.augmentBuffer.subarray(0, dim));
|
|
570
|
-
}
|
|
571
|
-
size() {
|
|
572
|
-
return this.entries.length;
|
|
573
|
-
}
|
|
574
|
-
clear() {
|
|
575
|
-
this.entries = [];
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
// ============================================================================
|
|
579
|
-
// Adaptive Embedder (Main Class)
|
|
580
|
-
// ============================================================================
|
|
581
|
-
class AdaptiveEmbedder {
|
|
582
|
-
constructor(config = {}) {
|
|
583
|
-
this.onnxReady = false;
|
|
584
|
-
this.dimension = 384;
|
|
585
|
-
// Stats
|
|
586
|
-
this.adaptationCount = 0;
|
|
587
|
-
this.ewcCount = 0;
|
|
588
|
-
this.contrastiveCount = 0;
|
|
589
|
-
// Co-edit buffer for contrastive learning
|
|
590
|
-
this.coEditBuffer = [];
|
|
591
|
-
this.config = {
|
|
592
|
-
loraRank: config.loraRank ?? 4,
|
|
593
|
-
learningRate: config.learningRate ?? 0.01,
|
|
594
|
-
ewcLambda: config.ewcLambda ?? 0.1,
|
|
595
|
-
numPrototypes: config.numPrototypes ?? 50,
|
|
596
|
-
contrastiveLearning: config.contrastiveLearning ?? true,
|
|
597
|
-
contrastiveTemp: config.contrastiveTemp ?? 0.07,
|
|
598
|
-
memoryCapacity: config.memoryCapacity ?? 1000,
|
|
599
|
-
};
|
|
600
|
-
// Pass dimension for pre-allocation of Float32Array buffers
|
|
601
|
-
this.lora = new MicroLoRA(this.dimension, this.config.loraRank);
|
|
602
|
-
this.prototypes = new PrototypeMemory(this.config.numPrototypes, this.dimension);
|
|
603
|
-
this.episodic = new EpisodicMemory(this.config.memoryCapacity, this.dimension);
|
|
604
|
-
}
|
|
605
|
-
/**
|
|
606
|
-
* Initialize ONNX backend
|
|
607
|
-
*/
|
|
608
|
-
async init() {
|
|
609
|
-
if ((0, onnx_embedder_1.isOnnxAvailable)()) {
|
|
610
|
-
await (0, onnx_embedder_1.initOnnxEmbedder)();
|
|
611
|
-
this.onnxReady = true;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* Generate adaptive embedding
|
|
616
|
-
* Pipeline: ONNX → LoRA → Prototype Adjustment → Episodic Augmentation
|
|
617
|
-
*/
|
|
618
|
-
async embed(text, options) {
|
|
619
|
-
// Step 1: Get base ONNX embedding
|
|
620
|
-
let baseEmb;
|
|
621
|
-
if (this.onnxReady) {
|
|
622
|
-
const result = await (0, onnx_embedder_1.embed)(text);
|
|
623
|
-
baseEmb = result.embedding;
|
|
624
|
-
}
|
|
625
|
-
else {
|
|
626
|
-
// Fallback to hash embedding
|
|
627
|
-
baseEmb = this.hashEmbed(text);
|
|
628
|
-
}
|
|
629
|
-
// Step 2: Apply LoRA adaptation
|
|
630
|
-
let adapted = this.lora.forward(baseEmb);
|
|
631
|
-
// Step 3: Prototype adjustment (if domain specified)
|
|
632
|
-
if (options?.domain) {
|
|
633
|
-
this.prototypes.update(options.domain, adapted);
|
|
634
|
-
}
|
|
635
|
-
const { adjusted, domain } = this.prototypes.adjust(adapted);
|
|
636
|
-
adapted = adjusted;
|
|
637
|
-
// Step 4: Episodic memory augmentation
|
|
638
|
-
if (options?.useEpisodic !== false) {
|
|
639
|
-
adapted = this.episodic.augment(adapted);
|
|
640
|
-
}
|
|
641
|
-
// Step 5: Store in episodic memory
|
|
642
|
-
if (options?.storeInMemory !== false) {
|
|
643
|
-
this.episodic.add(adapted, text.slice(0, 100));
|
|
644
|
-
}
|
|
645
|
-
// Normalize
|
|
646
|
-
return this.normalize(adapted);
|
|
647
|
-
}
|
|
648
|
-
/**
|
|
649
|
-
* Batch embed with adaptation
|
|
650
|
-
*/
|
|
651
|
-
async embedBatch(texts, options) {
|
|
652
|
-
const results = [];
|
|
653
|
-
if (this.onnxReady) {
|
|
654
|
-
const baseResults = await (0, onnx_embedder_1.embedBatch)(texts);
|
|
655
|
-
for (let i = 0; i < baseResults.length; i++) {
|
|
656
|
-
let adapted = this.lora.forward(baseResults[i].embedding);
|
|
657
|
-
if (options?.domain) {
|
|
658
|
-
this.prototypes.update(options.domain, adapted);
|
|
659
|
-
}
|
|
660
|
-
const { adjusted } = this.prototypes.adjust(adapted);
|
|
661
|
-
results.push(this.normalize(adjusted));
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
else {
|
|
665
|
-
for (const text of texts) {
|
|
666
|
-
results.push(await this.embed(text, options));
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
return results;
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Learn from co-edit pattern (contrastive learning)
|
|
673
|
-
* Files edited together should have similar embeddings
|
|
674
|
-
*/
|
|
675
|
-
async learnCoEdit(file1, content1, file2, content2) {
|
|
676
|
-
if (!this.config.contrastiveLearning)
|
|
677
|
-
return 0;
|
|
678
|
-
// Get embeddings
|
|
679
|
-
const emb1 = await this.embed(content1.slice(0, 512), { storeInMemory: false });
|
|
680
|
-
const emb2 = await this.embed(content2.slice(0, 512), { storeInMemory: false });
|
|
681
|
-
// Store in buffer for batch learning
|
|
682
|
-
this.coEditBuffer.push({ file1, emb1, file2, emb2 });
|
|
683
|
-
// Process batch when buffer is full
|
|
684
|
-
if (this.coEditBuffer.length >= 16) {
|
|
685
|
-
return this.processCoEditBatch();
|
|
686
|
-
}
|
|
687
|
-
return 0;
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Process co-edit batch with contrastive loss
|
|
691
|
-
*/
|
|
692
|
-
processCoEditBatch() {
|
|
693
|
-
if (this.coEditBuffer.length < 2)
|
|
694
|
-
return 0;
|
|
695
|
-
let totalLoss = 0;
|
|
696
|
-
for (const { emb1, emb2 } of this.coEditBuffer) {
|
|
697
|
-
// Use other pairs as negatives
|
|
698
|
-
const negatives = this.coEditBuffer
|
|
699
|
-
.filter(p => p.emb1 !== emb1)
|
|
700
|
-
.slice(0, 4)
|
|
701
|
-
.map(p => p.emb1);
|
|
702
|
-
// Backward pass with contrastive loss
|
|
703
|
-
const loss = this.lora.backward(emb1, emb2, negatives, this.config.learningRate, this.config.ewcLambda);
|
|
704
|
-
totalLoss += loss;
|
|
705
|
-
this.contrastiveCount++;
|
|
706
|
-
}
|
|
707
|
-
this.coEditBuffer = [];
|
|
708
|
-
this.adaptationCount++;
|
|
709
|
-
return totalLoss / this.coEditBuffer.length;
|
|
710
|
-
}
|
|
711
|
-
/**
|
|
712
|
-
* Learn from trajectory outcome (reinforcement-like)
|
|
713
|
-
*/
|
|
714
|
-
async learnFromOutcome(context, action, success, quality = 0.5) {
|
|
715
|
-
const contextEmb = await this.embed(context, { storeInMemory: false });
|
|
716
|
-
const actionEmb = await this.embed(action, { storeInMemory: false });
|
|
717
|
-
if (success && quality > 0.7) {
|
|
718
|
-
// Positive outcome - pull embeddings closer
|
|
719
|
-
this.lora.backward(contextEmb, actionEmb, [], this.config.learningRate * quality, this.config.ewcLambda);
|
|
720
|
-
this.adaptationCount++;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* EWC consolidation - prevent forgetting important adaptations
|
|
725
|
-
* OPTIMIZED: Works with Float32Array episodic entries
|
|
726
|
-
*/
|
|
727
|
-
async consolidate() {
|
|
728
|
-
// Collect current episodic memories for Fisher estimation
|
|
729
|
-
const embeddings = [];
|
|
730
|
-
const entries = this.episodic.entries || [];
|
|
731
|
-
// Get last 100 entries for Fisher estimation
|
|
732
|
-
const recentEntries = entries.slice(-100);
|
|
733
|
-
for (const entry of recentEntries) {
|
|
734
|
-
if (entry.embedding instanceof Float32Array) {
|
|
735
|
-
embeddings.push(entry.embedding);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
if (embeddings.length > 10) {
|
|
739
|
-
this.lora.consolidate(embeddings);
|
|
740
|
-
this.ewcCount++;
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* Fallback hash embedding
|
|
745
|
-
*/
|
|
746
|
-
hashEmbed(text) {
|
|
747
|
-
const embedding = new Array(this.dimension).fill(0);
|
|
748
|
-
const tokens = text.toLowerCase().split(/\s+/);
|
|
749
|
-
for (let t = 0; t < tokens.length; t++) {
|
|
750
|
-
const token = tokens[t];
|
|
751
|
-
const posWeight = 1 / (1 + t * 0.1);
|
|
752
|
-
for (let i = 0; i < token.length; i++) {
|
|
753
|
-
const code = token.charCodeAt(i);
|
|
754
|
-
const h1 = (code * 31 + i * 17 + t * 7) % this.dimension;
|
|
755
|
-
const h2 = (code * 37 + i * 23 + t * 11) % this.dimension;
|
|
756
|
-
embedding[h1] += posWeight;
|
|
757
|
-
embedding[h2] += posWeight * 0.5;
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
return this.normalize(embedding);
|
|
761
|
-
}
|
|
762
|
-
normalize(v) {
|
|
763
|
-
const norm = Math.sqrt(v.reduce((a, b) => a + b * b, 0));
|
|
764
|
-
return norm > 0 ? v.map(x => x / norm) : v;
|
|
765
|
-
}
|
|
766
|
-
/**
|
|
767
|
-
* Get statistics
|
|
768
|
-
*/
|
|
769
|
-
getStats() {
|
|
770
|
-
return {
|
|
771
|
-
baseModel: 'all-MiniLM-L6-v2',
|
|
772
|
-
dimension: this.dimension,
|
|
773
|
-
loraRank: this.config.loraRank,
|
|
774
|
-
loraParams: this.lora.getParams(),
|
|
775
|
-
adaptations: this.adaptationCount,
|
|
776
|
-
prototypes: this.prototypes.getPrototypes().length,
|
|
777
|
-
memorySize: this.episodic.size(),
|
|
778
|
-
ewcConsolidations: this.ewcCount,
|
|
779
|
-
contrastiveUpdates: this.contrastiveCount,
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
/**
|
|
783
|
-
* Export learned weights
|
|
784
|
-
*/
|
|
785
|
-
export() {
|
|
786
|
-
return {
|
|
787
|
-
lora: this.lora.export(),
|
|
788
|
-
prototypes: this.prototypes.export(),
|
|
789
|
-
stats: this.getStats(),
|
|
790
|
-
};
|
|
791
|
-
}
|
|
792
|
-
/**
|
|
793
|
-
* Import learned weights
|
|
794
|
-
*/
|
|
795
|
-
import(data) {
|
|
796
|
-
if (data.lora) {
|
|
797
|
-
this.lora.import(data.lora);
|
|
798
|
-
}
|
|
799
|
-
if (data.prototypes) {
|
|
800
|
-
this.prototypes.import(data.prototypes);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
/**
|
|
804
|
-
* Reset adaptations
|
|
805
|
-
*/
|
|
806
|
-
reset() {
|
|
807
|
-
this.lora = new MicroLoRA(this.dimension, this.config.loraRank);
|
|
808
|
-
this.prototypes = new PrototypeMemory(this.config.numPrototypes, this.dimension);
|
|
809
|
-
this.episodic.clear();
|
|
810
|
-
this.adaptationCount = 0;
|
|
811
|
-
this.ewcCount = 0;
|
|
812
|
-
this.contrastiveCount = 0;
|
|
813
|
-
this.coEditBuffer = [];
|
|
814
|
-
}
|
|
815
|
-
/**
|
|
816
|
-
* Get LoRA cache statistics
|
|
817
|
-
*/
|
|
818
|
-
getCacheStats() {
|
|
819
|
-
return this.lora.getCacheStats?.() ?? { size: 0, maxSize: 256 };
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
exports.AdaptiveEmbedder = AdaptiveEmbedder;
|
|
823
|
-
// ============================================================================
|
|
824
|
-
// Factory & Singleton
|
|
825
|
-
// ============================================================================
|
|
826
|
-
let instance = null;
|
|
827
|
-
function getAdaptiveEmbedder(config) {
|
|
828
|
-
if (!instance) {
|
|
829
|
-
instance = new AdaptiveEmbedder(config);
|
|
830
|
-
}
|
|
831
|
-
return instance;
|
|
832
|
-
}
|
|
833
|
-
async function initAdaptiveEmbedder(config) {
|
|
834
|
-
const embedder = getAdaptiveEmbedder(config);
|
|
835
|
-
await embedder.init();
|
|
836
|
-
return embedder;
|
|
837
|
-
}
|
|
838
|
-
exports.default = AdaptiveEmbedder;
|