agentic-qe 3.7.21 → 3.8.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/.claude/helpers/brain-checkpoint.cjs +4 -1
- package/.claude/helpers/statusline-v3.cjs +3 -1
- package/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +45 -0
- package/README.md +2 -14
- package/assets/helpers/statusline-v3.cjs +3 -1
- package/dist/cli/brain-commands.js +6 -10
- package/dist/cli/bundle.js +7441 -4327
- package/dist/cli/commands/audit.d.ts +43 -0
- package/dist/cli/commands/audit.js +125 -0
- package/dist/cli/commands/hooks.js +29 -6
- package/dist/cli/commands/init.js +1 -73
- package/dist/cli/commands/learning.js +270 -13
- package/dist/cli/commands/ruvector-commands.d.ts +15 -0
- package/dist/cli/commands/ruvector-commands.js +271 -0
- package/dist/cli/handlers/init-handler.d.ts +0 -1
- package/dist/cli/handlers/init-handler.js +0 -6
- package/dist/cli/index.js +4 -2
- package/dist/context/sources/defect-source.js +2 -2
- package/dist/context/sources/memory-source.js +2 -2
- package/dist/context/sources/requirements-source.js +2 -2
- package/dist/coordination/behavior-tree/decorators.d.ts +108 -0
- package/dist/coordination/behavior-tree/decorators.js +251 -0
- package/dist/coordination/behavior-tree/index.d.ts +12 -0
- package/dist/coordination/behavior-tree/index.js +15 -0
- package/dist/coordination/behavior-tree/nodes.d.ts +165 -0
- package/dist/coordination/behavior-tree/nodes.js +338 -0
- package/dist/coordination/behavior-tree/qe-trees.d.ts +105 -0
- package/dist/coordination/behavior-tree/qe-trees.js +181 -0
- package/dist/coordination/coherence-action-gate.d.ts +284 -0
- package/dist/coordination/coherence-action-gate.js +512 -0
- package/dist/coordination/index.d.ts +4 -0
- package/dist/coordination/index.js +8 -0
- package/dist/coordination/reasoning-qec.d.ts +315 -0
- package/dist/coordination/reasoning-qec.js +585 -0
- package/dist/coordination/task-executor.d.ts +16 -0
- package/dist/coordination/task-executor.js +99 -0
- package/dist/coordination/workflow-orchestrator.d.ts +29 -0
- package/dist/coordination/workflow-orchestrator.js +42 -0
- package/dist/domains/visual-accessibility/cnn-visual-regression.d.ts +135 -0
- package/dist/domains/visual-accessibility/cnn-visual-regression.js +327 -0
- package/dist/domains/visual-accessibility/index.d.ts +1 -0
- package/dist/domains/visual-accessibility/index.js +4 -0
- package/dist/governance/coherence-validator.d.ts +112 -0
- package/dist/governance/coherence-validator.js +180 -0
- package/dist/governance/index.d.ts +1 -0
- package/dist/governance/index.js +2 -0
- package/dist/governance/witness-chain.d.ts +311 -0
- package/dist/governance/witness-chain.js +509 -0
- package/dist/init/index.d.ts +0 -2
- package/dist/init/index.js +0 -1
- package/dist/init/init-wizard-steps.d.ts +10 -0
- package/dist/init/init-wizard-steps.js +87 -1
- package/dist/init/init-wizard.d.ts +1 -9
- package/dist/init/init-wizard.js +3 -69
- package/dist/init/orchestrator.js +0 -1
- package/dist/init/phases/01-detection.js +0 -27
- package/dist/init/phases/07-hooks.js +6 -4
- package/dist/init/phases/phase-interface.d.ts +0 -1
- package/dist/init/settings-merge.js +1 -1
- package/dist/integrations/browser/qe-dashboard/clustering.d.ts +48 -0
- package/dist/integrations/browser/qe-dashboard/clustering.js +183 -0
- package/dist/integrations/browser/qe-dashboard/index.d.ts +12 -0
- package/dist/integrations/browser/qe-dashboard/index.js +15 -0
- package/dist/integrations/browser/qe-dashboard/pattern-explorer.d.ts +165 -0
- package/dist/integrations/browser/qe-dashboard/pattern-explorer.js +260 -0
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.d.ts +144 -0
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.js +277 -0
- package/dist/integrations/ruvector/cognitive-container-codec.d.ts +51 -0
- package/dist/integrations/ruvector/cognitive-container-codec.js +180 -0
- package/dist/integrations/ruvector/cognitive-container.d.ts +125 -0
- package/dist/integrations/ruvector/cognitive-container.js +306 -0
- package/dist/integrations/ruvector/coherence-gate.d.ts +309 -0
- package/dist/integrations/ruvector/coherence-gate.js +631 -0
- package/dist/integrations/ruvector/compressed-hnsw-integration.d.ts +176 -0
- package/dist/integrations/ruvector/compressed-hnsw-integration.js +301 -0
- package/dist/integrations/ruvector/dither-adapter.d.ts +122 -0
- package/dist/integrations/ruvector/dither-adapter.js +295 -0
- package/dist/integrations/ruvector/domain-transfer.d.ts +129 -0
- package/dist/integrations/ruvector/domain-transfer.js +220 -0
- package/dist/integrations/ruvector/feature-flags.d.ts +214 -2
- package/dist/integrations/ruvector/feature-flags.js +167 -2
- package/dist/integrations/ruvector/filter-adapter.d.ts +71 -0
- package/dist/integrations/ruvector/filter-adapter.js +285 -0
- package/dist/integrations/ruvector/gnn-wrapper.d.ts +20 -0
- package/dist/integrations/ruvector/gnn-wrapper.js +40 -0
- package/dist/integrations/ruvector/hnsw-health-monitor.d.ts +237 -0
- package/dist/integrations/ruvector/hnsw-health-monitor.js +394 -0
- package/dist/integrations/ruvector/index.d.ts +8 -2
- package/dist/integrations/ruvector/index.js +18 -2
- package/dist/integrations/ruvector/interfaces.d.ts +40 -0
- package/dist/integrations/ruvector/sona-persistence.d.ts +54 -0
- package/dist/integrations/ruvector/sona-persistence.js +162 -0
- package/dist/integrations/ruvector/sona-three-loop.d.ts +392 -0
- package/dist/integrations/ruvector/sona-three-loop.js +814 -0
- package/dist/integrations/ruvector/sona-wrapper.d.ts +97 -0
- package/dist/integrations/ruvector/sona-wrapper.js +147 -3
- package/dist/integrations/ruvector/spectral-math.d.ts +101 -0
- package/dist/integrations/ruvector/spectral-math.js +254 -0
- package/dist/integrations/ruvector/temporal-compression.d.ts +163 -0
- package/dist/integrations/ruvector/temporal-compression.js +318 -0
- package/dist/integrations/ruvector/thompson-sampler.d.ts +61 -0
- package/dist/integrations/ruvector/thompson-sampler.js +118 -0
- package/dist/integrations/ruvector/transfer-coherence-stub.d.ts +80 -0
- package/dist/integrations/ruvector/transfer-coherence-stub.js +63 -0
- package/dist/integrations/ruvector/transfer-verification.d.ts +119 -0
- package/dist/integrations/ruvector/transfer-verification.js +115 -0
- package/dist/kernel/hnsw-adapter.d.ts +52 -1
- package/dist/kernel/hnsw-adapter.js +139 -4
- package/dist/kernel/hnsw-index-provider.d.ts +5 -0
- package/dist/kernel/native-hnsw-backend.d.ts +110 -0
- package/dist/kernel/native-hnsw-backend.js +408 -0
- package/dist/kernel/unified-memory.js +5 -6
- package/dist/learning/aqe-learning-engine.d.ts +2 -0
- package/dist/learning/aqe-learning-engine.js +65 -0
- package/dist/learning/experience-capture-middleware.js +20 -0
- package/dist/learning/experience-capture.d.ts +10 -0
- package/dist/learning/experience-capture.js +34 -0
- package/dist/learning/index.d.ts +2 -2
- package/dist/learning/index.js +4 -4
- package/dist/learning/metrics-tracker.d.ts +11 -0
- package/dist/learning/metrics-tracker.js +29 -13
- package/dist/learning/pattern-lifecycle.d.ts +30 -1
- package/dist/learning/pattern-lifecycle.js +92 -20
- package/dist/learning/pattern-store.d.ts +8 -0
- package/dist/learning/pattern-store.js +8 -2
- package/dist/learning/qe-unified-memory.js +1 -28
- package/dist/learning/regret-tracker.d.ts +201 -0
- package/dist/learning/regret-tracker.js +361 -0
- package/dist/mcp/bundle.js +5915 -474
- package/dist/routing/index.d.ts +4 -2
- package/dist/routing/index.js +3 -1
- package/dist/routing/neural-tiny-dancer-router.d.ts +268 -0
- package/dist/routing/neural-tiny-dancer-router.js +514 -0
- package/dist/routing/queen-integration.js +5 -5
- package/dist/routing/routing-config.d.ts +6 -0
- package/dist/routing/routing-config.js +1 -0
- package/dist/routing/simple-neural-router.d.ts +76 -0
- package/dist/routing/simple-neural-router.js +202 -0
- package/dist/routing/tiny-dancer-router.d.ts +20 -1
- package/dist/routing/tiny-dancer-router.js +21 -2
- package/dist/test-scheduling/dag-attention-scheduler.d.ts +81 -0
- package/dist/test-scheduling/dag-attention-scheduler.js +358 -0
- package/dist/test-scheduling/dag-attention-types.d.ts +81 -0
- package/dist/test-scheduling/dag-attention-types.js +10 -0
- package/dist/test-scheduling/index.d.ts +1 -0
- package/dist/test-scheduling/index.js +4 -0
- package/dist/test-scheduling/pipeline.d.ts +8 -0
- package/dist/test-scheduling/pipeline.js +28 -0
- package/package.json +6 -2
- package/dist/cli/commands/migrate.d.ts +0 -9
- package/dist/cli/commands/migrate.js +0 -566
- package/dist/init/init-wizard-migration.d.ts +0 -52
- package/dist/init/init-wizard-migration.js +0 -345
- package/dist/init/migration/config-migrator.d.ts +0 -31
- package/dist/init/migration/config-migrator.js +0 -149
- package/dist/init/migration/data-migrator.d.ts +0 -72
- package/dist/init/migration/data-migrator.js +0 -232
- package/dist/init/migration/detector.d.ts +0 -44
- package/dist/init/migration/detector.js +0 -105
- package/dist/init/migration/index.d.ts +0 -8
- package/dist/init/migration/index.js +0 -8
- package/dist/learning/v2-to-v3-migration.d.ts +0 -86
- package/dist/learning/v2-to-v3-migration.js +0 -529
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Explorer for QE Dashboard (Task 4.6)
|
|
3
|
+
*
|
|
4
|
+
* Provides pattern exploration, similarity clustering, and visualization
|
|
5
|
+
* data preparation for the browser-based QE intelligence dashboard.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Load and index patterns into the vector store
|
|
9
|
+
* - Similarity search across patterns
|
|
10
|
+
* - K-Means clustering for visualization
|
|
11
|
+
* - Domain distribution analysis
|
|
12
|
+
* - Health dashboard data aggregation
|
|
13
|
+
*
|
|
14
|
+
* No browser-specific APIs: works in Node.js for testing.
|
|
15
|
+
*
|
|
16
|
+
* @module integrations/browser/qe-dashboard/pattern-explorer
|
|
17
|
+
*/
|
|
18
|
+
import { WasmVectorStore, cosineSimilarity, } from './wasm-vector-store.js';
|
|
19
|
+
import { generateEmbedding, generateQueryEmbedding, kMeansClustering, } from './clustering.js';
|
|
20
|
+
// Re-export kMeansClustering for external consumers
|
|
21
|
+
export { kMeansClustering } from './clustering.js';
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// PatternExplorer
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* Pattern exploration and visualization data preparation.
|
|
27
|
+
*
|
|
28
|
+
* Loads QE patterns into a vector store, provides similarity search,
|
|
29
|
+
* clustering, domain distribution, and aggregated dashboard data.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const explorer = new PatternExplorer();
|
|
34
|
+
* await explorer.initialize();
|
|
35
|
+
*
|
|
36
|
+
* explorer.loadPatterns(patterns);
|
|
37
|
+
*
|
|
38
|
+
* // Find similar patterns
|
|
39
|
+
* const similar = explorer.searchSimilar('authentication testing', 5);
|
|
40
|
+
*
|
|
41
|
+
* // Cluster for visualization
|
|
42
|
+
* const clusters = explorer.clusterPatterns(4);
|
|
43
|
+
*
|
|
44
|
+
* // Dashboard data
|
|
45
|
+
* const dashboard = explorer.getHealthDashboardData();
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export class PatternExplorer {
|
|
49
|
+
store;
|
|
50
|
+
patterns = new Map();
|
|
51
|
+
embeddings = new Map();
|
|
52
|
+
constructor(store) {
|
|
53
|
+
this.store = store ?? new WasmVectorStore();
|
|
54
|
+
}
|
|
55
|
+
/** Initialize the explorer (loads WASM backend if available) */
|
|
56
|
+
async initialize() {
|
|
57
|
+
await this.store.initialize();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Load patterns into the explorer
|
|
61
|
+
*
|
|
62
|
+
* Each pattern is embedded (using provided embedding or auto-generated)
|
|
63
|
+
* and indexed in the vector store for similarity search.
|
|
64
|
+
*/
|
|
65
|
+
loadPatterns(patterns) {
|
|
66
|
+
for (const pattern of patterns) {
|
|
67
|
+
const embedding = pattern.embedding ?? generateEmbedding(pattern);
|
|
68
|
+
this.patterns.set(pattern.id, pattern);
|
|
69
|
+
this.embeddings.set(pattern.id, embedding);
|
|
70
|
+
this.store.add(pattern.id, embedding, {
|
|
71
|
+
domain: pattern.domain,
|
|
72
|
+
confidence: pattern.confidence,
|
|
73
|
+
success: pattern.success,
|
|
74
|
+
tags: pattern.tags,
|
|
75
|
+
createdAt: pattern.createdAt,
|
|
76
|
+
}, pattern.domain);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Search for patterns similar to a text query
|
|
81
|
+
*
|
|
82
|
+
* @param query - Natural language query string
|
|
83
|
+
* @param k - Number of results to return
|
|
84
|
+
* @returns Array of matched patterns ordered by similarity
|
|
85
|
+
*/
|
|
86
|
+
searchSimilar(query, k) {
|
|
87
|
+
const queryEmb = generateQueryEmbedding(query);
|
|
88
|
+
const results = this.store.search(queryEmb, k);
|
|
89
|
+
return results
|
|
90
|
+
.map((r) => this.patterns.get(r.id))
|
|
91
|
+
.filter((p) => p !== undefined);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Cluster patterns into groups using K-Means
|
|
95
|
+
*
|
|
96
|
+
* @param numClusters - Number of clusters to create
|
|
97
|
+
* @returns Array of PatternCluster with centroid, members, and statistics
|
|
98
|
+
*/
|
|
99
|
+
clusterPatterns(numClusters) {
|
|
100
|
+
const patternList = Array.from(this.patterns.values());
|
|
101
|
+
if (patternList.length === 0)
|
|
102
|
+
return [];
|
|
103
|
+
const vectors = patternList.map((p) => this.embeddings.get(p.id) ?? generateEmbedding(p));
|
|
104
|
+
const assignments = kMeansClustering(vectors, numClusters);
|
|
105
|
+
return this.buildClusters(patternList, vectors, assignments);
|
|
106
|
+
}
|
|
107
|
+
/** Get distribution statistics per domain */
|
|
108
|
+
getDomainDistribution() {
|
|
109
|
+
const domainMap = new Map();
|
|
110
|
+
for (const pattern of this.patterns.values()) {
|
|
111
|
+
if (!domainMap.has(pattern.domain)) {
|
|
112
|
+
domainMap.set(pattern.domain, []);
|
|
113
|
+
}
|
|
114
|
+
domainMap.get(pattern.domain).push(pattern);
|
|
115
|
+
}
|
|
116
|
+
const stats = [];
|
|
117
|
+
for (const [domain, patterns] of domainMap) {
|
|
118
|
+
stats.push(this.computeDomainStats(domain, patterns));
|
|
119
|
+
}
|
|
120
|
+
stats.sort((a, b) => b.patternCount - a.patternCount);
|
|
121
|
+
return stats;
|
|
122
|
+
}
|
|
123
|
+
/** Get aggregated health dashboard data */
|
|
124
|
+
getHealthDashboardData() {
|
|
125
|
+
const allPatterns = Array.from(this.patterns.values());
|
|
126
|
+
const totalPatterns = allPatterns.length;
|
|
127
|
+
const avgConfidence = totalPatterns > 0
|
|
128
|
+
? allPatterns.reduce((sum, p) => sum + p.confidence, 0) / totalPatterns
|
|
129
|
+
: 0;
|
|
130
|
+
const successCount = allPatterns.filter((p) => p.success === true).length;
|
|
131
|
+
const successTotal = allPatterns.filter((p) => p.success !== undefined).length;
|
|
132
|
+
const successRate = successTotal > 0 ? successCount / successTotal : 0;
|
|
133
|
+
const domainStats = this.getDomainDistribution();
|
|
134
|
+
const confidenceHistogram = new Array(10).fill(0);
|
|
135
|
+
for (const p of allPatterns) {
|
|
136
|
+
confidenceHistogram[Math.min(Math.floor(p.confidence * 10), 9)]++;
|
|
137
|
+
}
|
|
138
|
+
const thirtyDaysAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;
|
|
139
|
+
const recentActivity = allPatterns.filter((p) => (p.createdAt || 0) > thirtyDaysAgo).length;
|
|
140
|
+
const storeInfo = this.store.getStats();
|
|
141
|
+
return {
|
|
142
|
+
totalPatterns,
|
|
143
|
+
domainCount: domainStats.length,
|
|
144
|
+
avgConfidence,
|
|
145
|
+
successRate,
|
|
146
|
+
domainStats,
|
|
147
|
+
confidenceHistogram,
|
|
148
|
+
recentActivity,
|
|
149
|
+
storeStats: {
|
|
150
|
+
totalVectors: storeInfo.totalVectors,
|
|
151
|
+
dimensions: storeInfo.dimensions,
|
|
152
|
+
wasmActive: storeInfo.wasmActive,
|
|
153
|
+
memoryBytes: storeInfo.memoryBytes,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/** Get a pattern by id */
|
|
158
|
+
getPattern(id) {
|
|
159
|
+
return this.patterns.get(id);
|
|
160
|
+
}
|
|
161
|
+
/** Get total number of loaded patterns */
|
|
162
|
+
get patternCount() {
|
|
163
|
+
return this.patterns.size;
|
|
164
|
+
}
|
|
165
|
+
/** Clear all patterns and reset the explorer */
|
|
166
|
+
clear() {
|
|
167
|
+
this.patterns.clear();
|
|
168
|
+
this.embeddings.clear();
|
|
169
|
+
this.store.clear();
|
|
170
|
+
}
|
|
171
|
+
// ==========================================================================
|
|
172
|
+
// Private helpers
|
|
173
|
+
// ==========================================================================
|
|
174
|
+
buildClusters(patternList, vectors, assignments) {
|
|
175
|
+
const clusterMap = new Map();
|
|
176
|
+
for (let i = 0; i < assignments.length; i++) {
|
|
177
|
+
const cid = assignments[i];
|
|
178
|
+
if (!clusterMap.has(cid)) {
|
|
179
|
+
clusterMap.set(cid, { patterns: [], vectors: [] });
|
|
180
|
+
}
|
|
181
|
+
const entry = clusterMap.get(cid);
|
|
182
|
+
entry.patterns.push(patternList[i]);
|
|
183
|
+
entry.vectors.push(vectors[i]);
|
|
184
|
+
}
|
|
185
|
+
const clusters = [];
|
|
186
|
+
for (const [id, { patterns, vectors: vecs }] of clusterMap) {
|
|
187
|
+
const centroid = this.computeNormalizedCentroid(vecs);
|
|
188
|
+
const dominantDomain = this.findDominantDomain(patterns);
|
|
189
|
+
const avgConfidence = patterns.reduce((s, p) => s + p.confidence, 0) / patterns.length;
|
|
190
|
+
let cohesionSum = 0;
|
|
191
|
+
for (const v of vecs) {
|
|
192
|
+
cohesionSum += cosineSimilarity(v, centroid);
|
|
193
|
+
}
|
|
194
|
+
clusters.push({
|
|
195
|
+
id,
|
|
196
|
+
centroid,
|
|
197
|
+
patterns,
|
|
198
|
+
dominantDomain,
|
|
199
|
+
avgConfidence,
|
|
200
|
+
cohesion: vecs.length > 0 ? cohesionSum / vecs.length : 0,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
clusters.sort((a, b) => b.patterns.length - a.patterns.length);
|
|
204
|
+
return clusters;
|
|
205
|
+
}
|
|
206
|
+
computeNormalizedCentroid(vectors) {
|
|
207
|
+
const dim = vectors[0].length;
|
|
208
|
+
const centroid = new Float32Array(dim);
|
|
209
|
+
for (const v of vectors) {
|
|
210
|
+
for (let d = 0; d < dim; d++)
|
|
211
|
+
centroid[d] += v[d];
|
|
212
|
+
}
|
|
213
|
+
for (let d = 0; d < dim; d++)
|
|
214
|
+
centroid[d] /= vectors.length;
|
|
215
|
+
let norm = 0;
|
|
216
|
+
for (let d = 0; d < dim; d++)
|
|
217
|
+
norm += centroid[d] * centroid[d];
|
|
218
|
+
norm = Math.sqrt(norm);
|
|
219
|
+
if (norm > 0) {
|
|
220
|
+
for (let d = 0; d < dim; d++)
|
|
221
|
+
centroid[d] /= norm;
|
|
222
|
+
}
|
|
223
|
+
return centroid;
|
|
224
|
+
}
|
|
225
|
+
findDominantDomain(patterns) {
|
|
226
|
+
const counts = new Map();
|
|
227
|
+
for (const p of patterns) {
|
|
228
|
+
counts.set(p.domain, (counts.get(p.domain) || 0) + 1);
|
|
229
|
+
}
|
|
230
|
+
let best = '';
|
|
231
|
+
let max = 0;
|
|
232
|
+
for (const [domain, count] of counts) {
|
|
233
|
+
if (count > max) {
|
|
234
|
+
max = count;
|
|
235
|
+
best = domain;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return best;
|
|
239
|
+
}
|
|
240
|
+
computeDomainStats(domain, patterns) {
|
|
241
|
+
const patternCount = patterns.length;
|
|
242
|
+
const avgConfidence = patterns.reduce((s, p) => s + p.confidence, 0) / patternCount;
|
|
243
|
+
const successCount = patterns.filter((p) => p.success === true).length;
|
|
244
|
+
const successTotal = patterns.filter((p) => p.success !== undefined).length;
|
|
245
|
+
const successRate = successTotal > 0 ? successCount / successTotal : 0;
|
|
246
|
+
const latestTimestamp = patterns.reduce((max, p) => Math.max(max, p.createdAt || 0), 0);
|
|
247
|
+
const tagCounts = new Map();
|
|
248
|
+
for (const p of patterns) {
|
|
249
|
+
for (const tag of p.tags || []) {
|
|
250
|
+
tagCounts.set(tag, (tagCounts.get(tag) || 0) + 1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const topTags = Array.from(tagCounts.entries())
|
|
254
|
+
.map(([tag, count]) => ({ tag, count }))
|
|
255
|
+
.sort((a, b) => b.count - a.count)
|
|
256
|
+
.slice(0, 5);
|
|
257
|
+
return { domain, patternCount, avgConfidence, successRate, latestTimestamp, topTags };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=pattern-explorer.js.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side WASM Vector Store for QE Dashboard (Task 4.6)
|
|
3
|
+
*
|
|
4
|
+
* Provides a vector store that attempts to load `rvlite` for WASM-based
|
|
5
|
+
* vector search. When unavailable, falls back to a lightweight in-memory
|
|
6
|
+
* vector store with cosine similarity search and namespace support.
|
|
7
|
+
*
|
|
8
|
+
* Design Goals:
|
|
9
|
+
* - No browser-specific APIs (must work in Node.js for testing)
|
|
10
|
+
* - All WASM imports are optional with TypeScript fallback
|
|
11
|
+
* - Efficient cosine similarity computation
|
|
12
|
+
* - Namespace-scoped vector storage
|
|
13
|
+
*
|
|
14
|
+
* @module integrations/browser/qe-dashboard/wasm-vector-store
|
|
15
|
+
*/
|
|
16
|
+
/** Result of a similarity search */
|
|
17
|
+
export interface SearchResult {
|
|
18
|
+
/** Unique identifier for the matched vector */
|
|
19
|
+
id: string;
|
|
20
|
+
/** Cosine similarity score (0..1 for normalized vectors, -1..1 otherwise) */
|
|
21
|
+
similarity: number;
|
|
22
|
+
/** Metadata associated with the vector */
|
|
23
|
+
metadata: Record<string, unknown>;
|
|
24
|
+
/** Namespace the vector belongs to */
|
|
25
|
+
namespace?: string;
|
|
26
|
+
}
|
|
27
|
+
/** Statistics about the vector store */
|
|
28
|
+
export interface StoreStats {
|
|
29
|
+
/** Total number of vectors stored */
|
|
30
|
+
totalVectors: number;
|
|
31
|
+
/** Number of distinct namespaces */
|
|
32
|
+
namespaceCount: number;
|
|
33
|
+
/** Vectors per namespace */
|
|
34
|
+
namespaceSizes: Record<string, number>;
|
|
35
|
+
/** Dimensionality of stored vectors (0 if empty) */
|
|
36
|
+
dimensions: number;
|
|
37
|
+
/** Whether the WASM backend is active */
|
|
38
|
+
wasmActive: boolean;
|
|
39
|
+
/** Approximate memory usage in bytes */
|
|
40
|
+
memoryBytes: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Compute cosine similarity between two vectors
|
|
44
|
+
*
|
|
45
|
+
* cosine_similarity = dot(a, b) / (||a|| * ||b||)
|
|
46
|
+
*
|
|
47
|
+
* Optimized to accept pre-computed norms for repeated queries.
|
|
48
|
+
*/
|
|
49
|
+
export declare function cosineSimilarity(a: Float32Array, b: Float32Array, normA?: number, normB?: number): number;
|
|
50
|
+
/**
|
|
51
|
+
* Browser-side vector store with optional WASM acceleration.
|
|
52
|
+
*
|
|
53
|
+
* Tries to load `rvlite` for WASM-based vector search. When unavailable,
|
|
54
|
+
* uses a pure TypeScript in-memory implementation with cosine similarity.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const store = new WasmVectorStore();
|
|
59
|
+
* await store.initialize();
|
|
60
|
+
*
|
|
61
|
+
* // Add vectors
|
|
62
|
+
* store.add('pattern-1', new Float32Array([0.1, 0.2, 0.3]), { domain: 'testing' });
|
|
63
|
+
* store.add('pattern-2', new Float32Array([0.4, 0.5, 0.6]), { domain: 'coverage' });
|
|
64
|
+
*
|
|
65
|
+
* // Search
|
|
66
|
+
* const results = store.search(new Float32Array([0.1, 0.2, 0.3]), 5);
|
|
67
|
+
* console.log(results[0].id); // 'pattern-1'
|
|
68
|
+
* console.log(results[0].similarity); // ~1.0
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export declare class WasmVectorStore {
|
|
72
|
+
private vectors;
|
|
73
|
+
private wasmModule;
|
|
74
|
+
private dimensions;
|
|
75
|
+
private initialized;
|
|
76
|
+
/**
|
|
77
|
+
* Initialize the vector store
|
|
78
|
+
*
|
|
79
|
+
* Attempts to load the WASM backend. Falls back to TypeScript if unavailable.
|
|
80
|
+
* Safe to call multiple times (idempotent).
|
|
81
|
+
*/
|
|
82
|
+
initialize(): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Add a vector with optional metadata and namespace
|
|
85
|
+
*
|
|
86
|
+
* @param id - Unique identifier for the vector
|
|
87
|
+
* @param vector - The vector data as Float32Array
|
|
88
|
+
* @param metadata - Optional key-value metadata
|
|
89
|
+
* @param namespace - Optional namespace for scoped queries (default: 'default')
|
|
90
|
+
* @throws Error if vector dimensions are inconsistent
|
|
91
|
+
*/
|
|
92
|
+
add(id: string, vector: Float32Array, metadata?: Record<string, unknown>, namespace?: string): void;
|
|
93
|
+
/**
|
|
94
|
+
* Search for the k most similar vectors to a query
|
|
95
|
+
*
|
|
96
|
+
* @param query - The query vector
|
|
97
|
+
* @param k - Number of results to return
|
|
98
|
+
* @param namespace - Optional namespace filter (searches all if omitted)
|
|
99
|
+
* @returns Array of SearchResult sorted by descending similarity
|
|
100
|
+
*/
|
|
101
|
+
search(query: Float32Array, k: number, namespace?: string): SearchResult[];
|
|
102
|
+
/**
|
|
103
|
+
* Remove a vector by id
|
|
104
|
+
*
|
|
105
|
+
* @param id - The vector id to remove
|
|
106
|
+
* @returns true if the vector was found and removed, false otherwise
|
|
107
|
+
*/
|
|
108
|
+
remove(id: string): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Check if a vector exists
|
|
111
|
+
*
|
|
112
|
+
* @param id - The vector id to check
|
|
113
|
+
* @returns true if a vector with this id exists
|
|
114
|
+
*/
|
|
115
|
+
has(id: string): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Get the number of stored vectors
|
|
118
|
+
*/
|
|
119
|
+
get size(): number;
|
|
120
|
+
/**
|
|
121
|
+
* Clear all vectors, optionally within a specific namespace
|
|
122
|
+
*
|
|
123
|
+
* @param namespace - If provided, only clear vectors in this namespace
|
|
124
|
+
*/
|
|
125
|
+
clear(namespace?: string): void;
|
|
126
|
+
/**
|
|
127
|
+
* Get store statistics
|
|
128
|
+
*
|
|
129
|
+
* @returns StoreStats with counts, dimensions, memory usage, etc.
|
|
130
|
+
*/
|
|
131
|
+
getStats(): StoreStats;
|
|
132
|
+
/**
|
|
133
|
+
* Get all vector ids, optionally filtered by namespace
|
|
134
|
+
*
|
|
135
|
+
* @param namespace - Optional namespace filter
|
|
136
|
+
* @returns Array of vector ids
|
|
137
|
+
*/
|
|
138
|
+
getIds(namespace?: string): string[];
|
|
139
|
+
/**
|
|
140
|
+
* Whether the WASM backend is loaded
|
|
141
|
+
*/
|
|
142
|
+
get isWasmActive(): boolean;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=wasm-vector-store.d.ts.map
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side WASM Vector Store for QE Dashboard (Task 4.6)
|
|
3
|
+
*
|
|
4
|
+
* Provides a vector store that attempts to load `rvlite` for WASM-based
|
|
5
|
+
* vector search. When unavailable, falls back to a lightweight in-memory
|
|
6
|
+
* vector store with cosine similarity search and namespace support.
|
|
7
|
+
*
|
|
8
|
+
* Design Goals:
|
|
9
|
+
* - No browser-specific APIs (must work in Node.js for testing)
|
|
10
|
+
* - All WASM imports are optional with TypeScript fallback
|
|
11
|
+
* - Efficient cosine similarity computation
|
|
12
|
+
* - Namespace-scoped vector storage
|
|
13
|
+
*
|
|
14
|
+
* @module integrations/browser/qe-dashboard/wasm-vector-store
|
|
15
|
+
*/
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Math Utilities
|
|
18
|
+
// ============================================================================
|
|
19
|
+
/**
|
|
20
|
+
* Compute the L2 norm of a vector
|
|
21
|
+
*/
|
|
22
|
+
function l2Norm(v) {
|
|
23
|
+
let sum = 0;
|
|
24
|
+
for (let i = 0; i < v.length; i++) {
|
|
25
|
+
sum += v[i] * v[i];
|
|
26
|
+
}
|
|
27
|
+
return Math.sqrt(sum);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Compute cosine similarity between two vectors
|
|
31
|
+
*
|
|
32
|
+
* cosine_similarity = dot(a, b) / (||a|| * ||b||)
|
|
33
|
+
*
|
|
34
|
+
* Optimized to accept pre-computed norms for repeated queries.
|
|
35
|
+
*/
|
|
36
|
+
export function cosineSimilarity(a, b, normA, normB) {
|
|
37
|
+
if (a.length !== b.length) {
|
|
38
|
+
throw new Error(`Vector dimension mismatch: ${a.length} vs ${b.length}`);
|
|
39
|
+
}
|
|
40
|
+
let dot = 0;
|
|
41
|
+
for (let i = 0; i < a.length; i++) {
|
|
42
|
+
dot += a[i] * b[i];
|
|
43
|
+
}
|
|
44
|
+
const na = normA ?? l2Norm(a);
|
|
45
|
+
const nb = normB ?? l2Norm(b);
|
|
46
|
+
if (na === 0 || nb === 0) {
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
return dot / (na * nb);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Attempt to load the rvlite WASM module
|
|
53
|
+
* Returns null if unavailable (expected in most environments)
|
|
54
|
+
*/
|
|
55
|
+
async function tryLoadRvlite() {
|
|
56
|
+
try {
|
|
57
|
+
// Dynamic import - will fail gracefully if rvlite is not installed
|
|
58
|
+
const rvlite = await import('rvlite');
|
|
59
|
+
if (rvlite && typeof rvlite.add === 'function') {
|
|
60
|
+
return rvlite;
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Expected: rvlite is not installed in most environments
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// ============================================================================
|
|
70
|
+
// WasmVectorStore
|
|
71
|
+
// ============================================================================
|
|
72
|
+
/**
|
|
73
|
+
* Browser-side vector store with optional WASM acceleration.
|
|
74
|
+
*
|
|
75
|
+
* Tries to load `rvlite` for WASM-based vector search. When unavailable,
|
|
76
|
+
* uses a pure TypeScript in-memory implementation with cosine similarity.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* const store = new WasmVectorStore();
|
|
81
|
+
* await store.initialize();
|
|
82
|
+
*
|
|
83
|
+
* // Add vectors
|
|
84
|
+
* store.add('pattern-1', new Float32Array([0.1, 0.2, 0.3]), { domain: 'testing' });
|
|
85
|
+
* store.add('pattern-2', new Float32Array([0.4, 0.5, 0.6]), { domain: 'coverage' });
|
|
86
|
+
*
|
|
87
|
+
* // Search
|
|
88
|
+
* const results = store.search(new Float32Array([0.1, 0.2, 0.3]), 5);
|
|
89
|
+
* console.log(results[0].id); // 'pattern-1'
|
|
90
|
+
* console.log(results[0].similarity); // ~1.0
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export class WasmVectorStore {
|
|
94
|
+
vectors = new Map();
|
|
95
|
+
wasmModule = null;
|
|
96
|
+
dimensions = 0;
|
|
97
|
+
initialized = false;
|
|
98
|
+
/**
|
|
99
|
+
* Initialize the vector store
|
|
100
|
+
*
|
|
101
|
+
* Attempts to load the WASM backend. Falls back to TypeScript if unavailable.
|
|
102
|
+
* Safe to call multiple times (idempotent).
|
|
103
|
+
*/
|
|
104
|
+
async initialize() {
|
|
105
|
+
if (this.initialized)
|
|
106
|
+
return;
|
|
107
|
+
this.wasmModule = await tryLoadRvlite();
|
|
108
|
+
this.initialized = true;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Add a vector with optional metadata and namespace
|
|
112
|
+
*
|
|
113
|
+
* @param id - Unique identifier for the vector
|
|
114
|
+
* @param vector - The vector data as Float32Array
|
|
115
|
+
* @param metadata - Optional key-value metadata
|
|
116
|
+
* @param namespace - Optional namespace for scoped queries (default: 'default')
|
|
117
|
+
* @throws Error if vector dimensions are inconsistent
|
|
118
|
+
*/
|
|
119
|
+
add(id, vector, metadata = {}, namespace = 'default') {
|
|
120
|
+
if (vector.length === 0) {
|
|
121
|
+
throw new Error('Cannot add zero-length vector');
|
|
122
|
+
}
|
|
123
|
+
// Enforce consistent dimensionality
|
|
124
|
+
if (this.dimensions === 0) {
|
|
125
|
+
this.dimensions = vector.length;
|
|
126
|
+
}
|
|
127
|
+
else if (vector.length !== this.dimensions) {
|
|
128
|
+
throw new Error(`Dimension mismatch: expected ${this.dimensions}, got ${vector.length}`);
|
|
129
|
+
}
|
|
130
|
+
// Delegate to WASM if available
|
|
131
|
+
if (this.wasmModule) {
|
|
132
|
+
this.wasmModule.add(id, vector, namespace);
|
|
133
|
+
// Still keep in JS map for metadata lookups
|
|
134
|
+
}
|
|
135
|
+
const norm = l2Norm(vector);
|
|
136
|
+
this.vectors.set(id, { vector, metadata, namespace, norm });
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Search for the k most similar vectors to a query
|
|
140
|
+
*
|
|
141
|
+
* @param query - The query vector
|
|
142
|
+
* @param k - Number of results to return
|
|
143
|
+
* @param namespace - Optional namespace filter (searches all if omitted)
|
|
144
|
+
* @returns Array of SearchResult sorted by descending similarity
|
|
145
|
+
*/
|
|
146
|
+
search(query, k, namespace) {
|
|
147
|
+
if (query.length === 0) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
if (this.vectors.size === 0) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
// Dimension check for query
|
|
154
|
+
if (this.dimensions > 0 && query.length !== this.dimensions) {
|
|
155
|
+
throw new Error(`Query dimension mismatch: expected ${this.dimensions}, got ${query.length}`);
|
|
156
|
+
}
|
|
157
|
+
const queryNorm = l2Norm(query);
|
|
158
|
+
const results = [];
|
|
159
|
+
for (const [id, entry] of this.vectors) {
|
|
160
|
+
// Namespace filter
|
|
161
|
+
if (namespace !== undefined && entry.namespace !== namespace) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const similarity = cosineSimilarity(query, entry.vector, queryNorm, entry.norm);
|
|
165
|
+
results.push({
|
|
166
|
+
id,
|
|
167
|
+
similarity,
|
|
168
|
+
metadata: { ...entry.metadata },
|
|
169
|
+
namespace: entry.namespace,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
// Sort by descending similarity
|
|
173
|
+
results.sort((a, b) => b.similarity - a.similarity);
|
|
174
|
+
return results.slice(0, Math.max(0, k));
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Remove a vector by id
|
|
178
|
+
*
|
|
179
|
+
* @param id - The vector id to remove
|
|
180
|
+
* @returns true if the vector was found and removed, false otherwise
|
|
181
|
+
*/
|
|
182
|
+
remove(id) {
|
|
183
|
+
if (this.wasmModule) {
|
|
184
|
+
this.wasmModule.remove(id);
|
|
185
|
+
}
|
|
186
|
+
const existed = this.vectors.delete(id);
|
|
187
|
+
// Reset dimensions if store is now empty
|
|
188
|
+
if (this.vectors.size === 0) {
|
|
189
|
+
this.dimensions = 0;
|
|
190
|
+
}
|
|
191
|
+
return existed;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Check if a vector exists
|
|
195
|
+
*
|
|
196
|
+
* @param id - The vector id to check
|
|
197
|
+
* @returns true if a vector with this id exists
|
|
198
|
+
*/
|
|
199
|
+
has(id) {
|
|
200
|
+
return this.vectors.has(id);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Get the number of stored vectors
|
|
204
|
+
*/
|
|
205
|
+
get size() {
|
|
206
|
+
return this.vectors.size;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Clear all vectors, optionally within a specific namespace
|
|
210
|
+
*
|
|
211
|
+
* @param namespace - If provided, only clear vectors in this namespace
|
|
212
|
+
*/
|
|
213
|
+
clear(namespace) {
|
|
214
|
+
if (namespace === undefined) {
|
|
215
|
+
this.vectors.clear();
|
|
216
|
+
this.dimensions = 0;
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
for (const [id, entry] of this.vectors) {
|
|
220
|
+
if (entry.namespace === namespace) {
|
|
221
|
+
this.vectors.delete(id);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (this.vectors.size === 0) {
|
|
225
|
+
this.dimensions = 0;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get store statistics
|
|
231
|
+
*
|
|
232
|
+
* @returns StoreStats with counts, dimensions, memory usage, etc.
|
|
233
|
+
*/
|
|
234
|
+
getStats() {
|
|
235
|
+
const namespaceSizes = {};
|
|
236
|
+
let memoryBytes = 0;
|
|
237
|
+
for (const [, entry] of this.vectors) {
|
|
238
|
+
const ns = entry.namespace;
|
|
239
|
+
namespaceSizes[ns] = (namespaceSizes[ns] || 0) + 1;
|
|
240
|
+
// Approximate: Float32Array bytes + metadata overhead
|
|
241
|
+
memoryBytes += entry.vector.byteLength + 128;
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
totalVectors: this.vectors.size,
|
|
245
|
+
namespaceCount: Object.keys(namespaceSizes).length,
|
|
246
|
+
namespaceSizes,
|
|
247
|
+
dimensions: this.dimensions,
|
|
248
|
+
wasmActive: this.wasmModule !== null,
|
|
249
|
+
memoryBytes,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get all vector ids, optionally filtered by namespace
|
|
254
|
+
*
|
|
255
|
+
* @param namespace - Optional namespace filter
|
|
256
|
+
* @returns Array of vector ids
|
|
257
|
+
*/
|
|
258
|
+
getIds(namespace) {
|
|
259
|
+
if (namespace === undefined) {
|
|
260
|
+
return Array.from(this.vectors.keys());
|
|
261
|
+
}
|
|
262
|
+
const ids = [];
|
|
263
|
+
for (const [id, entry] of this.vectors) {
|
|
264
|
+
if (entry.namespace === namespace) {
|
|
265
|
+
ids.push(id);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return ids;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Whether the WASM backend is loaded
|
|
272
|
+
*/
|
|
273
|
+
get isWasmActive() {
|
|
274
|
+
return this.wasmModule !== null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=wasm-vector-store.js.map
|