agentic-qe 1.3.3 → 1.3.5
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/agents/qe-api-contract-validator.md +20 -0
- package/.claude/agents/qe-chaos-engineer.md +20 -0
- package/.claude/agents/qe-coverage-analyzer.md +21 -0
- package/.claude/agents/qe-deployment-readiness.md +20 -0
- package/.claude/agents/qe-flaky-test-hunter.md +20 -0
- package/.claude/agents/qe-fleet-commander.md +20 -0
- package/.claude/agents/qe-performance-tester.md +21 -0
- package/.claude/agents/qe-production-intelligence.md +20 -0
- package/.claude/agents/qe-quality-analyzer.md +20 -0
- package/.claude/agents/qe-quality-gate.md +20 -0
- package/.claude/agents/qe-regression-risk-analyzer.md +20 -0
- package/.claude/agents/qe-requirements-validator.md +20 -0
- package/.claude/agents/qe-security-scanner.md +21 -0
- package/.claude/agents/qe-test-data-architect.md +19 -0
- package/.claude/agents/qe-test-executor.md +20 -0
- package/.claude/agents/qe-test-generator.md +22 -0
- package/.claude/agents/qe-visual-tester.md +22 -0
- package/CHANGELOG.md +252 -0
- package/README.md +325 -1019
- package/README.md.backup-20251026 +1366 -0
- package/bin/aqe-mcp +1 -1
- package/dist/agents/BaseAgent.js +1 -1
- package/dist/agents/BaseAgent.js.map +1 -1
- package/dist/agents/CoverageAnalyzerAgent.js +16 -16
- package/dist/agents/CoverageAnalyzerAgent.js.map +1 -1
- package/dist/agents/FlakyTestHunterAgent.js +1 -1
- package/dist/agents/FlakyTestHunterAgent.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +71 -37
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/routing/index.d.ts +1 -1
- package/dist/cli/commands/routing/index.d.ts.map +1 -1
- package/dist/cli/commands/routing/index.js +29 -19
- package/dist/cli/commands/routing/index.js.map +1 -1
- package/dist/core/memory/EnhancedAgentDBService.d.ts +127 -0
- package/dist/core/memory/EnhancedAgentDBService.d.ts.map +1 -0
- package/dist/core/memory/EnhancedAgentDBService.js +298 -0
- package/dist/core/memory/EnhancedAgentDBService.js.map +1 -0
- package/dist/core/neural/NeuralTrainer.js +2 -2
- package/dist/core/neural/NeuralTrainer.js.map +1 -1
- package/dist/learning/ExperienceReplayBuffer.d.ts +143 -0
- package/dist/learning/ExperienceReplayBuffer.d.ts.map +1 -0
- package/dist/learning/ExperienceReplayBuffer.js +255 -0
- package/dist/learning/ExperienceReplayBuffer.js.map +1 -0
- package/dist/learning/FixRecommendationEngine.d.ts +68 -0
- package/dist/learning/FixRecommendationEngine.d.ts.map +1 -0
- package/dist/learning/FixRecommendationEngine.js +500 -0
- package/dist/learning/FixRecommendationEngine.js.map +1 -0
- package/dist/learning/FlakyTestDetector.d.ts +19 -0
- package/dist/learning/FlakyTestDetector.d.ts.map +1 -1
- package/dist/learning/FlakyTestDetector.js +121 -2
- package/dist/learning/FlakyTestDetector.js.map +1 -1
- package/dist/learning/LearningEngine.d.ts +50 -1
- package/dist/learning/LearningEngine.d.ts.map +1 -1
- package/dist/learning/LearningEngine.js +140 -0
- package/dist/learning/LearningEngine.js.map +1 -1
- package/dist/learning/QLearning.d.ts +154 -0
- package/dist/learning/QLearning.d.ts.map +1 -0
- package/dist/learning/QLearning.js +337 -0
- package/dist/learning/QLearning.js.map +1 -0
- package/dist/learning/index.d.ts +4 -0
- package/dist/learning/index.d.ts.map +1 -1
- package/dist/learning/index.js +7 -0
- package/dist/learning/index.js.map +1 -1
- package/dist/learning/types.d.ts +11 -0
- package/dist/learning/types.d.ts.map +1 -1
- package/dist/mcp/streaming/CoverageAnalyzeStreamHandler.d.ts +11 -1
- package/dist/mcp/streaming/CoverageAnalyzeStreamHandler.d.ts.map +1 -1
- package/dist/mcp/streaming/CoverageAnalyzeStreamHandler.js +12 -0
- package/dist/mcp/streaming/CoverageAnalyzeStreamHandler.js.map +1 -1
- package/dist/mcp/streaming/TestExecuteStreamHandler.d.ts +10 -1
- package/dist/mcp/streaming/TestExecuteStreamHandler.d.ts.map +1 -1
- package/dist/mcp/streaming/TestExecuteStreamHandler.js +11 -0
- package/dist/mcp/streaming/TestExecuteStreamHandler.js.map +1 -1
- package/dist/reasoning/PatternQualityScorer.d.ts +134 -0
- package/dist/reasoning/PatternQualityScorer.d.ts.map +1 -0
- package/dist/reasoning/PatternQualityScorer.js +340 -0
- package/dist/reasoning/PatternQualityScorer.js.map +1 -0
- package/dist/reasoning/QEReasoningBank.d.ts +138 -4
- package/dist/reasoning/QEReasoningBank.d.ts.map +1 -1
- package/dist/reasoning/QEReasoningBank.js +560 -12
- package/dist/reasoning/QEReasoningBank.js.map +1 -1
- package/dist/reasoning/VectorSimilarity.d.ts +131 -0
- package/dist/reasoning/VectorSimilarity.d.ts.map +1 -0
- package/dist/reasoning/VectorSimilarity.js +250 -0
- package/dist/reasoning/VectorSimilarity.js.map +1 -0
- package/dist/reasoning/index.d.ts +8 -1
- package/dist/reasoning/index.d.ts.map +1 -1
- package/dist/reasoning/index.js +13 -2
- package/dist/reasoning/index.js.map +1 -1
- package/dist/streaming/BaseStreamHandler.d.ts +89 -0
- package/dist/streaming/BaseStreamHandler.d.ts.map +1 -0
- package/dist/streaming/BaseStreamHandler.js +168 -0
- package/dist/streaming/BaseStreamHandler.js.map +1 -0
- package/dist/streaming/TestGenerateStreamHandler.d.ts +103 -0
- package/dist/streaming/TestGenerateStreamHandler.d.ts.map +1 -0
- package/dist/streaming/TestGenerateStreamHandler.js +321 -0
- package/dist/streaming/TestGenerateStreamHandler.js.map +1 -0
- package/dist/streaming/index.d.ts +16 -0
- package/dist/streaming/index.d.ts.map +1 -0
- package/dist/streaming/index.js +39 -0
- package/dist/streaming/index.js.map +1 -0
- package/dist/utils/__mocks__/Logger.d.ts +26 -0
- package/dist/utils/__mocks__/Logger.d.ts.map +1 -0
- package/dist/utils/__mocks__/Logger.js +42 -0
- package/dist/utils/__mocks__/Logger.js.map +1 -0
- package/package.json +15 -3
- package/dist/agents/mixins/NeuralCapableMixin.d.ts +0 -130
- package/dist/agents/mixins/NeuralCapableMixin.d.ts.map +0 -1
- package/dist/agents/mixins/NeuralCapableMixin.js +0 -358
- package/dist/agents/mixins/NeuralCapableMixin.js.map +0 -1
- package/dist/agents/mixins/QUICCapableMixin.d.ts +0 -34
- package/dist/agents/mixins/QUICCapableMixin.d.ts.map +0 -1
- package/dist/agents/mixins/QUICCapableMixin.js +0 -346
- package/dist/agents/mixins/QUICCapableMixin.js.map +0 -1
- package/dist/core/security/CertificateValidator.d.ts +0 -130
- package/dist/core/security/CertificateValidator.d.ts.map +0 -1
- package/dist/core/security/CertificateValidator.js +0 -376
- package/dist/core/security/CertificateValidator.js.map +0 -1
- package/dist/core/transport/QUICTransport.d.ts +0 -62
- package/dist/core/transport/QUICTransport.d.ts.map +0 -1
- package/dist/core/transport/QUICTransport.js +0 -381
- package/dist/core/transport/QUICTransport.js.map +0 -1
- package/dist/core/transport/SecureQUICTransport.d.ts +0 -71
- package/dist/core/transport/SecureQUICTransport.d.ts.map +0 -1
- package/dist/core/transport/SecureQUICTransport.js +0 -253
- package/dist/core/transport/SecureQUICTransport.js.map +0 -1
- package/dist/learning/AdvancedFeatureExtractor.d.ts +0 -123
- package/dist/learning/AdvancedFeatureExtractor.d.ts.map +0 -1
- package/dist/learning/AdvancedFeatureExtractor.js +0 -423
- package/dist/learning/AdvancedFeatureExtractor.js.map +0 -1
- package/dist/learning/NeuralPatternMatcher.d.ts +0 -184
- package/dist/learning/NeuralPatternMatcher.d.ts.map +0 -1
- package/dist/learning/NeuralPatternMatcher.js +0 -702
- package/dist/learning/NeuralPatternMatcher.js.map +0 -1
- package/dist/learning/NeuralTrainer.d.ts +0 -209
- package/dist/learning/NeuralTrainer.d.ts.map +0 -1
- package/dist/learning/NeuralTrainer.js +0 -478
- package/dist/learning/NeuralTrainer.js.map +0 -1
- package/dist/transport/QUICTransport.d.ts +0 -340
- package/dist/transport/QUICTransport.d.ts.map +0 -1
- package/dist/transport/QUICTransport.js +0 -814
- package/dist/transport/QUICTransport.js.map +0 -1
- package/dist/transport/UDPTransport.d.ts +0 -348
- package/dist/transport/UDPTransport.d.ts.map +0 -1
- package/dist/transport/UDPTransport.js +0 -820
- package/dist/transport/UDPTransport.js.map +0 -1
|
@@ -29,8 +29,43 @@
|
|
|
29
29
|
* });
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
35
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
36
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
37
|
+
}
|
|
38
|
+
Object.defineProperty(o, k2, desc);
|
|
39
|
+
}) : (function(o, m, k, k2) {
|
|
40
|
+
if (k2 === undefined) k2 = k;
|
|
41
|
+
o[k2] = m[k];
|
|
42
|
+
}));
|
|
43
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
44
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
45
|
+
}) : function(o, v) {
|
|
46
|
+
o["default"] = v;
|
|
47
|
+
});
|
|
48
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
49
|
+
var ownKeys = function(o) {
|
|
50
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
51
|
+
var ar = [];
|
|
52
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
53
|
+
return ar;
|
|
54
|
+
};
|
|
55
|
+
return ownKeys(o);
|
|
56
|
+
};
|
|
57
|
+
return function (mod) {
|
|
58
|
+
if (mod && mod.__esModule) return mod;
|
|
59
|
+
var result = {};
|
|
60
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
61
|
+
__setModuleDefault(result, mod);
|
|
62
|
+
return result;
|
|
63
|
+
};
|
|
64
|
+
})();
|
|
32
65
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
66
|
exports.QEReasoningBank = void 0;
|
|
67
|
+
const VectorSimilarity_1 = require("./VectorSimilarity");
|
|
68
|
+
const PatternQualityScorer_1 = require("./PatternQualityScorer");
|
|
34
69
|
/**
|
|
35
70
|
* QEReasoningBank - Main class for pattern storage and retrieval
|
|
36
71
|
*
|
|
@@ -49,13 +84,27 @@ exports.QEReasoningBank = void 0;
|
|
|
49
84
|
* @public
|
|
50
85
|
*/
|
|
51
86
|
class QEReasoningBank {
|
|
52
|
-
constructor() {
|
|
87
|
+
constructor(config = {}) {
|
|
53
88
|
this.patterns = new Map();
|
|
54
89
|
this.patternIndex = new Map();
|
|
90
|
+
this.keywordIndex = new Map(); // NEW: keyword-based fast lookup
|
|
91
|
+
this.frameworkIndex = new Map(); // NEW: framework-based index
|
|
55
92
|
this.versionHistory = new Map();
|
|
93
|
+
this.vectorCache = new Map();
|
|
94
|
+
this.similarityCache = new Map(); // NEW: cache for similar patterns
|
|
95
|
+
this.cacheExpiryTime = 5 * 60 * 1000; // 5 minutes cache TTL
|
|
96
|
+
this.lastCacheCleanup = Date.now();
|
|
97
|
+
this.vectorSimilarity = new VectorSimilarity_1.VectorSimilarity({ useIDF: true });
|
|
98
|
+
this.qualityScorer = new PatternQualityScorer_1.PatternQualityScorer();
|
|
99
|
+
this.minQuality = config.minQuality ?? 0.7;
|
|
100
|
+
this.performanceMetrics = {
|
|
101
|
+
lookupTimes: [],
|
|
102
|
+
cachehits: 0,
|
|
103
|
+
cacheMisses: 0
|
|
104
|
+
};
|
|
56
105
|
}
|
|
57
106
|
/**
|
|
58
|
-
* Store a new test pattern
|
|
107
|
+
* Store a new test pattern with quality scoring and vector indexing
|
|
59
108
|
*/
|
|
60
109
|
async storePattern(pattern) {
|
|
61
110
|
// Validate pattern
|
|
@@ -65,6 +114,22 @@ class QEReasoningBank {
|
|
|
65
114
|
if (pattern.confidence < 0 || pattern.confidence > 1) {
|
|
66
115
|
throw new Error('Confidence must be between 0 and 1');
|
|
67
116
|
}
|
|
117
|
+
// Calculate pattern quality if not provided
|
|
118
|
+
if (pattern.quality === undefined) {
|
|
119
|
+
const qualityScore = this.qualityScorer.calculateQuality({
|
|
120
|
+
id: pattern.id,
|
|
121
|
+
name: pattern.name,
|
|
122
|
+
code: pattern.examples[0] || pattern.template,
|
|
123
|
+
template: pattern.template,
|
|
124
|
+
description: pattern.description,
|
|
125
|
+
tags: pattern.metadata.tags,
|
|
126
|
+
usageCount: pattern.usageCount,
|
|
127
|
+
metadata: {
|
|
128
|
+
successRate: pattern.successRate
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
pattern.quality = qualityScore.overall;
|
|
132
|
+
}
|
|
68
133
|
// Version existing pattern
|
|
69
134
|
if (this.patterns.has(pattern.id)) {
|
|
70
135
|
const existing = this.patterns.get(pattern.id);
|
|
@@ -74,6 +139,11 @@ class QEReasoningBank {
|
|
|
74
139
|
}
|
|
75
140
|
// Store pattern
|
|
76
141
|
this.patterns.set(pattern.id, { ...pattern });
|
|
142
|
+
// Generate and cache vector embedding
|
|
143
|
+
const patternText = this.getPatternText(pattern);
|
|
144
|
+
this.vectorSimilarity.indexDocument(patternText);
|
|
145
|
+
const vector = this.vectorSimilarity.generateEmbedding(patternText);
|
|
146
|
+
this.vectorCache.set(pattern.id, vector);
|
|
77
147
|
// Update index for fast lookup
|
|
78
148
|
this.updateIndex(pattern);
|
|
79
149
|
}
|
|
@@ -84,24 +154,163 @@ class QEReasoningBank {
|
|
|
84
154
|
return this.patterns.get(id) || null;
|
|
85
155
|
}
|
|
86
156
|
/**
|
|
87
|
-
* Find matching patterns
|
|
157
|
+
* Find matching patterns using vector similarity with performance optimizations
|
|
158
|
+
* Target: 85%+ matching accuracy, <50ms p95 latency
|
|
159
|
+
*
|
|
160
|
+
* **Optimizations:**
|
|
161
|
+
* - Caching: Frequently accessed patterns cached for 5 minutes
|
|
162
|
+
* - Indexing: Multi-level indexes (keyword, framework, tag) for fast lookup
|
|
163
|
+
* - Early termination: Stop processing when enough high-quality matches found
|
|
164
|
+
* - Batch processing: Process multiple candidates in batches
|
|
88
165
|
*/
|
|
89
166
|
async findMatchingPatterns(context, limit = 10) {
|
|
167
|
+
const startTime = performance.now();
|
|
168
|
+
// Check cache first
|
|
169
|
+
const cacheKey = this.getCacheKey(context);
|
|
170
|
+
if (this.similarityCache.has(cacheKey)) {
|
|
171
|
+
this.performanceMetrics.cachehits++;
|
|
172
|
+
const cached = this.similarityCache.get(cacheKey);
|
|
173
|
+
this.recordLookupTime(performance.now() - startTime);
|
|
174
|
+
return cached.slice(0, limit);
|
|
175
|
+
}
|
|
176
|
+
this.performanceMetrics.cacheMisses++;
|
|
177
|
+
// Use indexed lookup for fast candidate selection
|
|
178
|
+
const candidates = this.getIndexedCandidates(context);
|
|
179
|
+
// If we have indexed candidates, use them; otherwise fall back to full scan
|
|
180
|
+
const vectorMatches = candidates.length > 0
|
|
181
|
+
? this.scoreIndexedCandidates(candidates, context, limit * 2)
|
|
182
|
+
: await this.vectorSimilaritySearch(context, limit * 2);
|
|
90
183
|
const matches = [];
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
184
|
+
let highQualityCount = 0;
|
|
185
|
+
// Process candidates with early termination
|
|
186
|
+
for (const vectorMatch of vectorMatches) {
|
|
187
|
+
const pattern = this.patterns.get(vectorMatch.id);
|
|
188
|
+
if (!pattern)
|
|
189
|
+
continue;
|
|
190
|
+
// Calculate hybrid confidence (vector similarity + rule-based)
|
|
191
|
+
const ruleBased = this.calculateMatchConfidence(pattern, context);
|
|
192
|
+
const vectorSim = vectorMatch.similarity;
|
|
193
|
+
// Hybrid scoring: 60% vector similarity, 40% rule-based
|
|
194
|
+
const confidence = vectorSim * 0.6 + ruleBased * 0.4;
|
|
195
|
+
// Apply quality filter
|
|
196
|
+
const meetsQuality = (pattern.quality ?? 1.0) >= this.minQuality;
|
|
197
|
+
if (confidence > 0.3 && meetsQuality) {
|
|
198
|
+
const applicability = confidence * pattern.successRate * (pattern.quality ?? 1.0);
|
|
94
199
|
matches.push({
|
|
95
200
|
pattern,
|
|
96
201
|
confidence,
|
|
97
|
-
|
|
98
|
-
|
|
202
|
+
similarity: vectorSim,
|
|
203
|
+
reasoning: this.generateReasoning(pattern, context, vectorSim),
|
|
204
|
+
applicability
|
|
99
205
|
});
|
|
206
|
+
// Early termination: if we have enough high-quality matches, stop
|
|
207
|
+
if (applicability > 0.85) {
|
|
208
|
+
highQualityCount++;
|
|
209
|
+
if (highQualityCount >= limit && matches.length >= limit * 1.5) {
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
100
213
|
}
|
|
101
214
|
}
|
|
102
|
-
// Sort by applicability
|
|
215
|
+
// Sort by applicability (confidence × success rate × quality)
|
|
103
216
|
matches.sort((a, b) => b.applicability - a.applicability);
|
|
104
|
-
|
|
217
|
+
const result = matches.slice(0, limit);
|
|
218
|
+
// Cache the result
|
|
219
|
+
this.similarityCache.set(cacheKey, result);
|
|
220
|
+
this.cleanupCacheIfNeeded();
|
|
221
|
+
const lookupTime = performance.now() - startTime;
|
|
222
|
+
this.recordLookupTime(lookupTime);
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get cache key for context
|
|
227
|
+
*/
|
|
228
|
+
getCacheKey(context) {
|
|
229
|
+
return JSON.stringify({
|
|
230
|
+
codeType: context.codeType,
|
|
231
|
+
framework: context.framework,
|
|
232
|
+
language: context.language,
|
|
233
|
+
keywords: context.keywords?.sort()
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get indexed candidates for fast lookup
|
|
238
|
+
* Uses multi-level indexes (framework, keywords, tags)
|
|
239
|
+
*/
|
|
240
|
+
getIndexedCandidates(context) {
|
|
241
|
+
const candidateIds = new Set();
|
|
242
|
+
// Framework index lookup (most specific)
|
|
243
|
+
if (context.framework && this.frameworkIndex.has(context.framework)) {
|
|
244
|
+
this.frameworkIndex.get(context.framework).forEach(id => candidateIds.add(id));
|
|
245
|
+
}
|
|
246
|
+
// Keyword index lookup
|
|
247
|
+
if (context.keywords && context.keywords.length > 0) {
|
|
248
|
+
for (const keyword of context.keywords) {
|
|
249
|
+
if (this.keywordIndex.has(keyword.toLowerCase())) {
|
|
250
|
+
this.keywordIndex.get(keyword.toLowerCase()).forEach(id => candidateIds.add(id));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return Array.from(candidateIds);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Score indexed candidates efficiently
|
|
258
|
+
*/
|
|
259
|
+
scoreIndexedCandidates(candidateIds, context, limit) {
|
|
260
|
+
const queryText = this.buildQueryText(context);
|
|
261
|
+
const queryVector = this.vectorSimilarity.generateEmbedding(queryText);
|
|
262
|
+
const scored = candidateIds
|
|
263
|
+
.map(id => {
|
|
264
|
+
const vector = this.vectorCache.get(id);
|
|
265
|
+
if (!vector)
|
|
266
|
+
return null;
|
|
267
|
+
const similarity = this.vectorSimilarity.cosineSimilarity(queryVector, vector);
|
|
268
|
+
return { id, similarity };
|
|
269
|
+
})
|
|
270
|
+
.filter((item) => item !== null)
|
|
271
|
+
.sort((a, b) => b.similarity - a.similarity)
|
|
272
|
+
.slice(0, limit);
|
|
273
|
+
return scored;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Fallback vector similarity search
|
|
277
|
+
*/
|
|
278
|
+
async vectorSimilaritySearch(context, limit) {
|
|
279
|
+
const queryText = this.buildQueryText(context);
|
|
280
|
+
const queryVector = this.vectorSimilarity.generateEmbedding(queryText);
|
|
281
|
+
return this.vectorSimilarity.findTopK(queryVector, this.vectorCache, limit);
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Record lookup time for performance monitoring
|
|
285
|
+
*/
|
|
286
|
+
recordLookupTime(timeMs) {
|
|
287
|
+
this.performanceMetrics.lookupTimes.push(timeMs);
|
|
288
|
+
// Keep only last 1000 measurements
|
|
289
|
+
if (this.performanceMetrics.lookupTimes.length > 1000) {
|
|
290
|
+
this.performanceMetrics.lookupTimes.shift();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Cleanup cache if needed (every 5 minutes)
|
|
295
|
+
*/
|
|
296
|
+
cleanupCacheIfNeeded() {
|
|
297
|
+
const now = Date.now();
|
|
298
|
+
if (now - this.lastCacheCleanup > this.cacheExpiryTime) {
|
|
299
|
+
this.similarityCache.clear();
|
|
300
|
+
this.lastCacheCleanup = now;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Find similar patterns by example code
|
|
305
|
+
* Uses vector similarity for code-based matching
|
|
306
|
+
*/
|
|
307
|
+
async findSimilarPatterns(exampleCode, framework, limit = 5) {
|
|
308
|
+
return this.findMatchingPatterns({
|
|
309
|
+
codeType: 'test',
|
|
310
|
+
framework,
|
|
311
|
+
sourceCode: exampleCode,
|
|
312
|
+
keywords: this.extractKeywords(exampleCode)
|
|
313
|
+
}, limit);
|
|
105
314
|
}
|
|
106
315
|
/**
|
|
107
316
|
* Update pattern success metrics
|
|
@@ -121,7 +330,7 @@ class QEReasoningBank {
|
|
|
121
330
|
pattern.metadata.updatedAt = new Date();
|
|
122
331
|
}
|
|
123
332
|
/**
|
|
124
|
-
* Get pattern statistics
|
|
333
|
+
* Get pattern statistics (alias for getStats for backward compatibility)
|
|
125
334
|
*/
|
|
126
335
|
async getStatistics() {
|
|
127
336
|
const patterns = Array.from(this.patterns.values());
|
|
@@ -129,6 +338,7 @@ class QEReasoningBank {
|
|
|
129
338
|
totalPatterns: patterns.length,
|
|
130
339
|
averageConfidence: patterns.reduce((sum, p) => sum + p.confidence, 0) / patterns.length || 0,
|
|
131
340
|
averageSuccessRate: patterns.reduce((sum, p) => sum + p.successRate, 0) / patterns.length || 0,
|
|
341
|
+
averageQuality: patterns.reduce((sum, p) => sum + (p.quality ?? 0), 0) / patterns.length || 0,
|
|
132
342
|
byCategory: {},
|
|
133
343
|
byFramework: {}
|
|
134
344
|
};
|
|
@@ -173,12 +383,33 @@ class QEReasoningBank {
|
|
|
173
383
|
this.patternIndex.set(pattern.category, new Set());
|
|
174
384
|
}
|
|
175
385
|
this.patternIndex.get(pattern.category).add(pattern.id);
|
|
386
|
+
// Index by framework (NEW: fast framework lookup)
|
|
387
|
+
if (!this.frameworkIndex.has(pattern.framework)) {
|
|
388
|
+
this.frameworkIndex.set(pattern.framework, new Set());
|
|
389
|
+
}
|
|
390
|
+
this.frameworkIndex.get(pattern.framework).add(pattern.id);
|
|
176
391
|
// Index by tags
|
|
177
392
|
for (const tag of pattern.metadata.tags) {
|
|
178
393
|
if (!this.patternIndex.has(`tag:${tag}`)) {
|
|
179
394
|
this.patternIndex.set(`tag:${tag}`, new Set());
|
|
180
395
|
}
|
|
181
396
|
this.patternIndex.get(`tag:${tag}`).add(pattern.id);
|
|
397
|
+
// Index by keyword (NEW: fast keyword lookup)
|
|
398
|
+
const keyword = tag.toLowerCase();
|
|
399
|
+
if (!this.keywordIndex.has(keyword)) {
|
|
400
|
+
this.keywordIndex.set(keyword, new Set());
|
|
401
|
+
}
|
|
402
|
+
this.keywordIndex.get(keyword).add(pattern.id);
|
|
403
|
+
}
|
|
404
|
+
// Index name keywords (NEW)
|
|
405
|
+
const nameWords = pattern.name.toLowerCase().split(/\s+/);
|
|
406
|
+
for (const word of nameWords) {
|
|
407
|
+
if (word.length > 2) { // Skip very short words
|
|
408
|
+
if (!this.keywordIndex.has(word)) {
|
|
409
|
+
this.keywordIndex.set(word, new Set());
|
|
410
|
+
}
|
|
411
|
+
this.keywordIndex.get(word).add(pattern.id);
|
|
412
|
+
}
|
|
182
413
|
}
|
|
183
414
|
}
|
|
184
415
|
calculateMatchConfidence(pattern, context) {
|
|
@@ -211,8 +442,12 @@ class QEReasoningBank {
|
|
|
211
442
|
score += pattern.confidence * 0.10;
|
|
212
443
|
return factors > 0 ? Math.min(score, 1.0) : 0;
|
|
213
444
|
}
|
|
214
|
-
generateReasoning(pattern, context) {
|
|
445
|
+
generateReasoning(pattern, context, similarity) {
|
|
215
446
|
const reasons = [];
|
|
447
|
+
// Vector similarity score
|
|
448
|
+
if (similarity !== undefined) {
|
|
449
|
+
reasons.push(`Similarity: ${(similarity * 100).toFixed(1)}%`);
|
|
450
|
+
}
|
|
216
451
|
if (context.framework && pattern.framework === context.framework) {
|
|
217
452
|
reasons.push(`Framework match: ${pattern.framework}`);
|
|
218
453
|
}
|
|
@@ -225,10 +460,323 @@ class QEReasoningBank {
|
|
|
225
460
|
reasons.push(`Tag matches: ${matchingKeywords.join(', ')}`);
|
|
226
461
|
}
|
|
227
462
|
}
|
|
463
|
+
if (pattern.quality !== undefined) {
|
|
464
|
+
reasons.push(`Quality: ${(pattern.quality * 100).toFixed(1)}%`);
|
|
465
|
+
}
|
|
228
466
|
reasons.push(`Success rate: ${(pattern.successRate * 100).toFixed(1)}%`);
|
|
229
467
|
reasons.push(`Used ${pattern.usageCount} times`);
|
|
230
468
|
return reasons.join('; ');
|
|
231
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* Build query text from context for vector matching
|
|
472
|
+
*/
|
|
473
|
+
buildQueryText(context) {
|
|
474
|
+
const parts = [];
|
|
475
|
+
parts.push(context.codeType);
|
|
476
|
+
if (context.framework) {
|
|
477
|
+
parts.push(context.framework);
|
|
478
|
+
}
|
|
479
|
+
if (context.language) {
|
|
480
|
+
parts.push(context.language);
|
|
481
|
+
}
|
|
482
|
+
if (context.keywords) {
|
|
483
|
+
parts.push(...context.keywords);
|
|
484
|
+
}
|
|
485
|
+
if (context.sourceCode) {
|
|
486
|
+
// Extract meaningful tokens from source code
|
|
487
|
+
parts.push(...this.extractKeywords(context.sourceCode));
|
|
488
|
+
}
|
|
489
|
+
return parts.join(' ');
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Get pattern text for vector embedding
|
|
493
|
+
*/
|
|
494
|
+
getPatternText(pattern) {
|
|
495
|
+
const parts = [];
|
|
496
|
+
parts.push(pattern.name);
|
|
497
|
+
parts.push(pattern.description);
|
|
498
|
+
parts.push(pattern.category);
|
|
499
|
+
parts.push(pattern.framework);
|
|
500
|
+
parts.push(...pattern.metadata.tags);
|
|
501
|
+
if (pattern.examples.length > 0) {
|
|
502
|
+
parts.push(...this.extractKeywords(pattern.examples[0]));
|
|
503
|
+
}
|
|
504
|
+
return parts.join(' ');
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Extract keywords from code
|
|
508
|
+
*/
|
|
509
|
+
extractKeywords(code) {
|
|
510
|
+
// Simple keyword extraction (can be enhanced with AST analysis)
|
|
511
|
+
const keywords = [];
|
|
512
|
+
// Extract function/method names
|
|
513
|
+
const functionMatches = code.match(/function\s+(\w+)|const\s+(\w+)\s*=/g);
|
|
514
|
+
if (functionMatches) {
|
|
515
|
+
keywords.push(...functionMatches.map(m => m.replace(/function|const|=|\s/g, '')));
|
|
516
|
+
}
|
|
517
|
+
// Extract identifiers (camelCase, snake_case)
|
|
518
|
+
const identifiers = code.match(/\b[a-z_][a-zA-Z0-9_]{2,}\b/g);
|
|
519
|
+
if (identifiers) {
|
|
520
|
+
keywords.push(...identifiers.slice(0, 20)); // Limit to avoid noise
|
|
521
|
+
}
|
|
522
|
+
return keywords.filter(k => k.length > 2);
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Match a pattern by exact ID
|
|
526
|
+
* Alias for getPattern for consistency with pattern matching API
|
|
527
|
+
*/
|
|
528
|
+
async matchPattern(patternId) {
|
|
529
|
+
return this.getPattern(patternId);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Find patterns using flexible search criteria
|
|
533
|
+
* Supports multiple search strategies: vector similarity, tag matching, keyword search
|
|
534
|
+
*/
|
|
535
|
+
async findPattern(criteria, limit = 10) {
|
|
536
|
+
// If query provided, use vector similarity search
|
|
537
|
+
if (criteria.query) {
|
|
538
|
+
return this.findMatchingPatterns({
|
|
539
|
+
codeType: 'test',
|
|
540
|
+
framework: criteria.framework,
|
|
541
|
+
language: criteria.language,
|
|
542
|
+
keywords: criteria.tags,
|
|
543
|
+
sourceCode: criteria.query
|
|
544
|
+
}, limit);
|
|
545
|
+
}
|
|
546
|
+
// Otherwise, use filter-based search
|
|
547
|
+
let patterns = Array.from(this.patterns.values());
|
|
548
|
+
// Apply filters
|
|
549
|
+
if (criteria.category) {
|
|
550
|
+
patterns = patterns.filter(p => p.category === criteria.category);
|
|
551
|
+
}
|
|
552
|
+
if (criteria.framework) {
|
|
553
|
+
patterns = patterns.filter(p => p.framework === criteria.framework);
|
|
554
|
+
}
|
|
555
|
+
if (criteria.language) {
|
|
556
|
+
patterns = patterns.filter(p => p.language === criteria.language);
|
|
557
|
+
}
|
|
558
|
+
if (criteria.tags && criteria.tags.length > 0) {
|
|
559
|
+
patterns = patterns.filter(p => criteria.tags.some(tag => p.metadata.tags.includes(tag)));
|
|
560
|
+
}
|
|
561
|
+
if (criteria.minConfidence !== undefined) {
|
|
562
|
+
patterns = patterns.filter(p => p.confidence >= criteria.minConfidence);
|
|
563
|
+
}
|
|
564
|
+
if (criteria.minQuality !== undefined) {
|
|
565
|
+
patterns = patterns.filter(p => (p.quality ?? 0) >= criteria.minQuality);
|
|
566
|
+
}
|
|
567
|
+
// Sort by quality and success rate
|
|
568
|
+
patterns.sort((a, b) => {
|
|
569
|
+
const scoreA = (a.quality ?? 0.5) * a.successRate * a.confidence;
|
|
570
|
+
const scoreB = (b.quality ?? 0.5) * b.successRate * b.confidence;
|
|
571
|
+
return scoreB - scoreA;
|
|
572
|
+
});
|
|
573
|
+
// Convert to PatternMatch format
|
|
574
|
+
return patterns.slice(0, limit).map(pattern => ({
|
|
575
|
+
pattern,
|
|
576
|
+
confidence: pattern.confidence,
|
|
577
|
+
similarity: 1.0, // Filter-based match is exact
|
|
578
|
+
reasoning: this.generateReasoning(pattern, {
|
|
579
|
+
codeType: 'test',
|
|
580
|
+
framework: criteria.framework,
|
|
581
|
+
language: criteria.language,
|
|
582
|
+
keywords: criteria.tags
|
|
583
|
+
}),
|
|
584
|
+
applicability: pattern.confidence * pattern.successRate * (pattern.quality ?? 1.0)
|
|
585
|
+
}));
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Calculate similarity score between two patterns
|
|
589
|
+
* Returns a score between 0-1 indicating how similar the patterns are
|
|
590
|
+
*/
|
|
591
|
+
calculateSimilarity(pattern1, pattern2) {
|
|
592
|
+
let score = 0;
|
|
593
|
+
let weights = 0;
|
|
594
|
+
// Framework match (25% weight)
|
|
595
|
+
weights += 0.25;
|
|
596
|
+
if (pattern1.framework === pattern2.framework) {
|
|
597
|
+
score += 0.25;
|
|
598
|
+
}
|
|
599
|
+
// Language match (20% weight)
|
|
600
|
+
weights += 0.20;
|
|
601
|
+
if (pattern1.language === pattern2.language) {
|
|
602
|
+
score += 0.20;
|
|
603
|
+
}
|
|
604
|
+
// Category match (20% weight)
|
|
605
|
+
weights += 0.20;
|
|
606
|
+
if (pattern1.category === pattern2.category) {
|
|
607
|
+
score += 0.20;
|
|
608
|
+
}
|
|
609
|
+
// Tag overlap (35% weight)
|
|
610
|
+
weights += 0.35;
|
|
611
|
+
const tags1 = new Set(pattern1.metadata.tags);
|
|
612
|
+
const tags2 = new Set(pattern2.metadata.tags);
|
|
613
|
+
const intersection = new Set([...tags1].filter(t => tags2.has(t)));
|
|
614
|
+
const union = new Set([...tags1, ...tags2]);
|
|
615
|
+
const jaccardSimilarity = union.size > 0 ? intersection.size / union.size : 0;
|
|
616
|
+
score += jaccardSimilarity * 0.35;
|
|
617
|
+
return score / weights;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Search patterns by similarity to a reference pattern
|
|
621
|
+
*/
|
|
622
|
+
async searchSimilarPatterns(referencePattern, limit = 5) {
|
|
623
|
+
const matches = [];
|
|
624
|
+
for (const pattern of Array.from(this.patterns.values())) {
|
|
625
|
+
if (pattern.id === referencePattern.id)
|
|
626
|
+
continue;
|
|
627
|
+
const similarity = this.calculateSimilarity(referencePattern, pattern);
|
|
628
|
+
if (similarity > 0.3) {
|
|
629
|
+
matches.push({
|
|
630
|
+
pattern,
|
|
631
|
+
confidence: pattern.confidence,
|
|
632
|
+
similarity,
|
|
633
|
+
reasoning: `Similar pattern: ${(similarity * 100).toFixed(1)}% match`,
|
|
634
|
+
applicability: similarity * pattern.successRate * (pattern.quality ?? 1.0)
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
// Sort by similarity and quality
|
|
639
|
+
matches.sort((a, b) => b.applicability - a.applicability);
|
|
640
|
+
return matches.slice(0, limit);
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Load patterns from registry file
|
|
644
|
+
*/
|
|
645
|
+
async loadFromRegistry(registryPath) {
|
|
646
|
+
try {
|
|
647
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
648
|
+
const registryData = await fs.readFile(registryPath, 'utf-8');
|
|
649
|
+
const registry = JSON.parse(registryData);
|
|
650
|
+
if (!registry.patterns || !Array.isArray(registry.patterns)) {
|
|
651
|
+
throw new Error('Invalid registry format: patterns array not found');
|
|
652
|
+
}
|
|
653
|
+
let loadedCount = 0;
|
|
654
|
+
for (const pattern of registry.patterns) {
|
|
655
|
+
// Convert date strings to Date objects
|
|
656
|
+
if (pattern.metadata?.createdAt) {
|
|
657
|
+
pattern.metadata.createdAt = new Date(pattern.metadata.createdAt);
|
|
658
|
+
}
|
|
659
|
+
if (pattern.metadata?.updatedAt) {
|
|
660
|
+
pattern.metadata.updatedAt = new Date(pattern.metadata.updatedAt);
|
|
661
|
+
}
|
|
662
|
+
await this.storePattern(pattern);
|
|
663
|
+
loadedCount++;
|
|
664
|
+
}
|
|
665
|
+
return loadedCount;
|
|
666
|
+
}
|
|
667
|
+
catch (error) {
|
|
668
|
+
throw new Error(`Failed to load patterns from registry: ${error instanceof Error ? error.message : String(error)}`);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Save patterns to registry file
|
|
673
|
+
*/
|
|
674
|
+
async saveToRegistry(registryPath, filter) {
|
|
675
|
+
try {
|
|
676
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
|
|
677
|
+
const path = await Promise.resolve().then(() => __importStar(require('path')));
|
|
678
|
+
const patterns = this.exportPatterns(filter);
|
|
679
|
+
const stats = this.getStats();
|
|
680
|
+
const registry = {
|
|
681
|
+
version: '1.0.0',
|
|
682
|
+
lastUpdated: new Date().toISOString(),
|
|
683
|
+
patterns,
|
|
684
|
+
statistics: stats
|
|
685
|
+
};
|
|
686
|
+
// Ensure directory exists
|
|
687
|
+
const dir = path.dirname(registryPath);
|
|
688
|
+
await fs.mkdir(dir, { recursive: true });
|
|
689
|
+
await fs.writeFile(registryPath, JSON.stringify(registry, null, 2), 'utf-8');
|
|
690
|
+
}
|
|
691
|
+
catch (error) {
|
|
692
|
+
throw new Error(`Failed to save patterns to registry: ${error instanceof Error ? error.message : String(error)}`);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Export patterns for cross-project sharing
|
|
697
|
+
*/
|
|
698
|
+
exportPatterns(filter) {
|
|
699
|
+
let patterns = Array.from(this.patterns.values());
|
|
700
|
+
if (filter) {
|
|
701
|
+
patterns = patterns.filter(p => {
|
|
702
|
+
if (filter.framework && p.framework !== filter.framework)
|
|
703
|
+
return false;
|
|
704
|
+
if (filter.category && p.category !== filter.category)
|
|
705
|
+
return false;
|
|
706
|
+
if (filter.language && p.language !== filter.language)
|
|
707
|
+
return false;
|
|
708
|
+
return true;
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
return patterns;
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Import patterns from another project
|
|
715
|
+
*/
|
|
716
|
+
async importPatterns(patterns) {
|
|
717
|
+
for (const pattern of patterns) {
|
|
718
|
+
await this.storePattern(pattern);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Get pattern statistics including quality metrics
|
|
723
|
+
*/
|
|
724
|
+
getStats() {
|
|
725
|
+
const patterns = Array.from(this.patterns.values());
|
|
726
|
+
const stats = {
|
|
727
|
+
totalPatterns: patterns.length,
|
|
728
|
+
byFramework: {},
|
|
729
|
+
byCategory: {},
|
|
730
|
+
averageQuality: patterns.reduce((sum, p) => sum + (p.quality ?? 0), 0) / patterns.length || 0,
|
|
731
|
+
averageSuccessRate: patterns.reduce((sum, p) => sum + p.successRate, 0) / patterns.length || 0
|
|
732
|
+
};
|
|
733
|
+
for (const pattern of patterns) {
|
|
734
|
+
stats.byFramework[pattern.framework] = (stats.byFramework[pattern.framework] || 0) + 1;
|
|
735
|
+
stats.byCategory[pattern.category] = (stats.byCategory[pattern.category] || 0) + 1;
|
|
736
|
+
}
|
|
737
|
+
return stats;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Get performance metrics (NEW)
|
|
741
|
+
* Returns timing statistics for pattern matching operations
|
|
742
|
+
*/
|
|
743
|
+
getPerformanceMetrics() {
|
|
744
|
+
const times = this.performanceMetrics.lookupTimes;
|
|
745
|
+
if (times.length === 0) {
|
|
746
|
+
return {
|
|
747
|
+
avgLookupTime: 0,
|
|
748
|
+
p95LookupTime: 0,
|
|
749
|
+
p99LookupTime: 0,
|
|
750
|
+
cacheHitRate: 0,
|
|
751
|
+
totalLookups: 0
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
const sorted = [...times].sort((a, b) => a - b);
|
|
755
|
+
const avg = times.reduce((sum, t) => sum + t, 0) / times.length;
|
|
756
|
+
const p95Index = Math.floor(sorted.length * 0.95);
|
|
757
|
+
const p99Index = Math.floor(sorted.length * 0.99);
|
|
758
|
+
const totalRequests = this.performanceMetrics.cachehits + this.performanceMetrics.cacheMisses;
|
|
759
|
+
const cacheHitRate = totalRequests > 0
|
|
760
|
+
? (this.performanceMetrics.cachehits / totalRequests) * 100
|
|
761
|
+
: 0;
|
|
762
|
+
return {
|
|
763
|
+
avgLookupTime: parseFloat(avg.toFixed(2)),
|
|
764
|
+
p95LookupTime: parseFloat(sorted[p95Index].toFixed(2)),
|
|
765
|
+
p99LookupTime: parseFloat(sorted[p99Index].toFixed(2)),
|
|
766
|
+
cacheHitRate: parseFloat(cacheHitRate.toFixed(2)),
|
|
767
|
+
totalLookups: times.length
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Reset performance metrics (for testing)
|
|
772
|
+
*/
|
|
773
|
+
resetPerformanceMetrics() {
|
|
774
|
+
this.performanceMetrics = {
|
|
775
|
+
lookupTimes: [],
|
|
776
|
+
cachehits: 0,
|
|
777
|
+
cacheMisses: 0
|
|
778
|
+
};
|
|
779
|
+
}
|
|
232
780
|
}
|
|
233
781
|
exports.QEReasoningBank = QEReasoningBank;
|
|
234
782
|
exports.default = QEReasoningBank;
|