agentic-qe 2.5.8 → 2.5.10
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/CHANGELOG.md +122 -0
- package/README.md +2 -2
- package/dist/agents/BaseAgent.d.ts +211 -7
- package/dist/agents/BaseAgent.d.ts.map +1 -1
- package/dist/agents/BaseAgent.js +455 -7
- package/dist/agents/BaseAgent.js.map +1 -1
- package/dist/agents/CoverageAnalyzerAgent.d.ts +20 -23
- package/dist/agents/CoverageAnalyzerAgent.d.ts.map +1 -1
- package/dist/agents/CoverageAnalyzerAgent.js +95 -145
- package/dist/agents/CoverageAnalyzerAgent.js.map +1 -1
- package/dist/agents/FlakyTestHunterAgent.d.ts +15 -0
- package/dist/agents/FlakyTestHunterAgent.d.ts.map +1 -1
- package/dist/agents/FlakyTestHunterAgent.js +93 -0
- package/dist/agents/FlakyTestHunterAgent.js.map +1 -1
- package/dist/agents/QualityAnalyzerAgent.d.ts +2 -2
- package/dist/agents/QualityAnalyzerAgent.d.ts.map +1 -1
- package/dist/agents/QualityAnalyzerAgent.js.map +1 -1
- package/dist/agents/QualityGateAgent.d.ts +35 -18
- package/dist/agents/QualityGateAgent.d.ts.map +1 -1
- package/dist/agents/QualityGateAgent.js +72 -97
- package/dist/agents/QualityGateAgent.js.map +1 -1
- package/dist/agents/SecurityScannerAgent.d.ts +19 -0
- package/dist/agents/SecurityScannerAgent.d.ts.map +1 -1
- package/dist/agents/SecurityScannerAgent.js +137 -0
- package/dist/agents/SecurityScannerAgent.js.map +1 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +17 -4
- package/dist/agents/index.js.map +1 -1
- package/dist/cli/commands/ruvector/index.d.ts +13 -0
- package/dist/cli/commands/ruvector/index.d.ts.map +1 -0
- package/dist/cli/commands/ruvector/index.js +308 -0
- package/dist/cli/commands/ruvector/index.js.map +1 -0
- package/dist/cli/index.js +5 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/index.d.ts.map +1 -1
- package/dist/cli/init/index.js +11 -0
- package/dist/cli/init/index.js.map +1 -1
- package/dist/core/memory/HNSWVectorMemory.js +1 -1
- package/dist/core/memory/HNSWVectorMemory.js.map +1 -1
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +1 -1
- package/dist/mcp/server-instructions.js.map +1 -1
- package/dist/mcp/tools.d.ts +6 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +121 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/memory/RuVectorPatternStore.d.ts +258 -0
- package/dist/memory/RuVectorPatternStore.d.ts.map +1 -0
- package/dist/memory/RuVectorPatternStore.js +525 -0
- package/dist/memory/RuVectorPatternStore.js.map +1 -0
- package/dist/providers/RuVectorPostgresAdapter.d.ts +134 -0
- package/dist/providers/RuVectorPostgresAdapter.d.ts.map +1 -0
- package/dist/providers/RuVectorPostgresAdapter.js +504 -0
- package/dist/providers/RuVectorPostgresAdapter.js.map +1 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +7 -1
- package/dist/providers/index.js.map +1 -1
- package/package.json +4 -2
package/dist/agents/BaseAgent.js
CHANGED
|
@@ -20,6 +20,8 @@ const AgentCoordinator_1 = require("./coordination/AgentCoordinator");
|
|
|
20
20
|
const AgentMemoryService_1 = require("./memory/AgentMemoryService");
|
|
21
21
|
const RuvllmProvider_1 = require("../providers/RuvllmProvider");
|
|
22
22
|
const LLMProviderFactory_1 = require("../providers/LLMProviderFactory");
|
|
23
|
+
// HybridRouter with RuVector cache (Phase 0.5 - GNN Self-Learning)
|
|
24
|
+
const HybridRouter_1 = require("../providers/HybridRouter");
|
|
23
25
|
const adapters_1 = require("./adapters");
|
|
24
26
|
// Extracted utilities (B1.2)
|
|
25
27
|
const utils_1 = require("./utils");
|
|
@@ -30,15 +32,32 @@ class BaseAgent extends events_1.EventEmitter {
|
|
|
30
32
|
super();
|
|
31
33
|
this.performanceMetrics = { tasksCompleted: 0, averageExecutionTime: 0, errorCount: 0, lastActivity: new Date() };
|
|
32
34
|
this.federatedInitialized = false;
|
|
35
|
+
this.patternStoreInitialized = false;
|
|
33
36
|
this.agentId = { id: config.id || (0, utils_1.generateAgentId)(config.type), type: config.type, created: new Date() };
|
|
34
|
-
this.capabilities = new Map(config.capabilities.map(cap => [cap.name, cap]));
|
|
37
|
+
this.capabilities = new Map((config.capabilities || []).map(cap => [cap.name, cap]));
|
|
35
38
|
this.context = config.context;
|
|
36
39
|
this.memoryStore = config.memoryStore;
|
|
37
|
-
this.eventBus = config.eventBus;
|
|
40
|
+
this.eventBus = config.eventBus || new events_1.EventEmitter();
|
|
38
41
|
this.enableLearning = config.enableLearning ?? true;
|
|
39
42
|
this.learningConfig = config.learningConfig;
|
|
40
43
|
// LLM configuration (Phase 0 - default enabled with RuvLLM)
|
|
41
44
|
this.llmConfig = config.llm ?? { enabled: true, preferredProvider: 'ruvllm' };
|
|
45
|
+
// QE Pattern Store configuration (Phase 0.5 - RuVector Integration)
|
|
46
|
+
// Environment variables take precedence, then config, then defaults
|
|
47
|
+
// RuVector is OPT-IN to maintain backward compatibility with existing users
|
|
48
|
+
const envRuVectorEnabled = process.env.AQE_RUVECTOR_ENABLED?.toLowerCase() === 'true';
|
|
49
|
+
const envRuVectorUrl = process.env.AQE_RUVECTOR_URL || process.env.RUVECTOR_URL;
|
|
50
|
+
const envPatternStoreEnabled = process.env.AQE_PATTERN_STORE_ENABLED?.toLowerCase() !== 'false';
|
|
51
|
+
this.qePatternConfig = {
|
|
52
|
+
enabled: config.patternStore?.enabled ?? envPatternStoreEnabled,
|
|
53
|
+
// RuVector defaults to FALSE - opt-in for advanced self-learning
|
|
54
|
+
useRuVector: config.patternStore?.useRuVector ?? envRuVectorEnabled,
|
|
55
|
+
useHNSW: config.patternStore?.useHNSW ?? true,
|
|
56
|
+
dualWrite: config.patternStore?.dualWrite ?? (process.env.AQE_PATTERN_DUAL_WRITE === 'true'),
|
|
57
|
+
ruvectorUrl: config.patternStore?.ruvectorUrl ?? envRuVectorUrl ?? 'postgresql://ruvector:ruvector@localhost:5432/ruvector_db',
|
|
58
|
+
storagePath: config.patternStore?.storagePath ?? process.env.AQE_PATTERN_STORE_PATH ?? './data/qe-patterns.ruvector',
|
|
59
|
+
autoSync: config.patternStore?.autoSync ?? (process.env.AQE_PATTERN_AUTO_SYNC === 'true'),
|
|
60
|
+
};
|
|
42
61
|
// Early validation (Issue #137)
|
|
43
62
|
const validation = (0, utils_1.validateLearningConfig)(config);
|
|
44
63
|
if (!validation.valid && validation.warning) {
|
|
@@ -103,6 +122,8 @@ class BaseAgent extends events_1.EventEmitter {
|
|
|
103
122
|
await this.initializeLLMProvider();
|
|
104
123
|
// Initialize Federated Learning (Phase 0 M0.5)
|
|
105
124
|
await this.initializeFederatedLearning();
|
|
125
|
+
// Initialize QE Pattern Store (Phase 0.5)
|
|
126
|
+
await this.initializePatternStore();
|
|
106
127
|
await this.initializeComponents();
|
|
107
128
|
await this.executeHook('post-initialization');
|
|
108
129
|
this.coordinator.emitEvent('agent.initialized', { agentId: this.agentId });
|
|
@@ -158,6 +179,7 @@ class BaseAgent extends events_1.EventEmitter {
|
|
|
158
179
|
await this.cleanup();
|
|
159
180
|
await this.cleanupLLM(); // Phase 0: Cleanup LLM resources
|
|
160
181
|
await this.cleanupFederated(); // Phase 0 M0.5: Cleanup federated learning
|
|
182
|
+
await this.cleanupPatternStore(); // Phase 0.5: Cleanup pattern store
|
|
161
183
|
this.coordinator.clearAllHandlers();
|
|
162
184
|
},
|
|
163
185
|
onPostTermination: async () => {
|
|
@@ -413,7 +435,8 @@ class BaseAgent extends events_1.EventEmitter {
|
|
|
413
435
|
await this[method](data);
|
|
414
436
|
}
|
|
415
437
|
catch (error) {
|
|
416
|
-
|
|
438
|
+
// Use warn - hooks are optional and failures shouldn't break agent operation
|
|
439
|
+
console.warn(`Hook ${hookName} failed:`, error);
|
|
417
440
|
}
|
|
418
441
|
}
|
|
419
442
|
setupEventHandlers() {
|
|
@@ -472,7 +495,7 @@ class BaseAgent extends events_1.EventEmitter {
|
|
|
472
495
|
// ============================================
|
|
473
496
|
/**
|
|
474
497
|
* Initialize LLM provider for agent use
|
|
475
|
-
* Supports RuvLLM (local), Claude, and
|
|
498
|
+
* Supports RuvLLM (local), Claude, OpenRouter, and HybridRouter with RuVector cache
|
|
476
499
|
*/
|
|
477
500
|
async initializeLLMProvider() {
|
|
478
501
|
if (!this.llmConfig.enabled) {
|
|
@@ -486,6 +509,37 @@ class BaseAgent extends events_1.EventEmitter {
|
|
|
486
509
|
console.log(`[${this.agentId.id}] Using injected LLM provider`);
|
|
487
510
|
return;
|
|
488
511
|
}
|
|
512
|
+
// Phase 0.5: Create HybridRouter with RuVector GNN cache for intelligent routing
|
|
513
|
+
if (this.llmConfig.enableHybridRouter || this.llmConfig.preferredProvider === 'hybrid') {
|
|
514
|
+
const hybridConfig = {
|
|
515
|
+
// RuVector cache configuration (GNN self-learning)
|
|
516
|
+
ruvector: {
|
|
517
|
+
enabled: true,
|
|
518
|
+
baseUrl: this.llmConfig.ruvectorCache?.baseUrl || 'http://localhost:8080',
|
|
519
|
+
cacheThreshold: this.llmConfig.ruvectorCache?.cacheThreshold ?? 0.85,
|
|
520
|
+
learningEnabled: this.llmConfig.ruvectorCache?.learningEnabled ?? true,
|
|
521
|
+
loraRank: this.llmConfig.ruvectorCache?.loraRank ?? 8,
|
|
522
|
+
ewcEnabled: this.llmConfig.ruvectorCache?.ewcEnabled ?? true,
|
|
523
|
+
...this.llmConfig.ruvectorCache,
|
|
524
|
+
},
|
|
525
|
+
// Local LLM via ruvllm
|
|
526
|
+
ruvllm: {
|
|
527
|
+
name: `${this.agentId.id}-ruvllm`,
|
|
528
|
+
enableSessions: this.llmConfig.enableSessions ?? true,
|
|
529
|
+
enableTRM: true,
|
|
530
|
+
enableSONA: true,
|
|
531
|
+
...this.llmConfig.ruvllm,
|
|
532
|
+
},
|
|
533
|
+
// Routing strategy
|
|
534
|
+
defaultStrategy: this.llmConfig.hybridRouterConfig?.defaultStrategy || HybridRouter_1.RoutingStrategy.BALANCED,
|
|
535
|
+
...this.llmConfig.hybridRouterConfig,
|
|
536
|
+
};
|
|
537
|
+
this.hybridRouter = new HybridRouter_1.HybridRouter(hybridConfig);
|
|
538
|
+
await this.hybridRouter.initialize();
|
|
539
|
+
this.llmProvider = this.hybridRouter;
|
|
540
|
+
console.log(`[${this.agentId.id}] HybridRouter initialized with RuVector GNN cache`);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
489
543
|
// Create RuvLLM provider directly (preferred for local inference)
|
|
490
544
|
if (this.llmConfig.preferredProvider === 'ruvllm' || !this.llmConfig.preferredProvider) {
|
|
491
545
|
const ruvllmConfig = {
|
|
@@ -524,7 +578,8 @@ class BaseAgent extends events_1.EventEmitter {
|
|
|
524
578
|
}
|
|
525
579
|
}
|
|
526
580
|
catch (error) {
|
|
527
|
-
|
|
581
|
+
// Use warn instead of error - this is expected fallback behavior, not a failure
|
|
582
|
+
console.warn(`[${this.agentId.id}] LLM initialization failed:`, error.message);
|
|
528
583
|
// Don't throw - agent can still work without LLM (algorithmic fallback)
|
|
529
584
|
}
|
|
530
585
|
}
|
|
@@ -559,7 +614,8 @@ class BaseAgent extends events_1.EventEmitter {
|
|
|
559
614
|
}
|
|
560
615
|
}
|
|
561
616
|
catch (error) {
|
|
562
|
-
|
|
617
|
+
// Use warn instead of error - this is expected fallback behavior
|
|
618
|
+
console.warn(`[${this.agentId.id}] Federated learning initialization failed:`, error.message);
|
|
563
619
|
// Don't throw - agent can work without federated learning
|
|
564
620
|
}
|
|
565
621
|
}
|
|
@@ -760,9 +816,401 @@ class BaseAgent extends events_1.EventEmitter {
|
|
|
760
816
|
return {
|
|
761
817
|
available: this.hasLLM(),
|
|
762
818
|
sessionId: this.llmSessionId,
|
|
763
|
-
provider: this.llmProvider ? 'ruvllm' : undefined
|
|
819
|
+
provider: this.hybridRouter ? 'hybrid' : (this.llmProvider ? 'ruvllm' : undefined),
|
|
820
|
+
hasRuVectorCache: this.hasRuVectorCache(),
|
|
821
|
+
hasPatternStore: this.hasPatternStore(),
|
|
822
|
+
patternStoreType: this.hasPatternStore() ?
|
|
823
|
+
this.qePatternStore?.getImplementationInfo?.()?.type :
|
|
824
|
+
undefined,
|
|
764
825
|
};
|
|
765
826
|
}
|
|
827
|
+
// ============================================
|
|
828
|
+
// RuVector Cache Methods (Phase 0.5 - GNN Self-Learning)
|
|
829
|
+
// ============================================
|
|
830
|
+
/**
|
|
831
|
+
* Check if RuVector GNN cache is available
|
|
832
|
+
*/
|
|
833
|
+
hasRuVectorCache() {
|
|
834
|
+
return this.hybridRouter !== undefined;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Get RuVector cache metrics
|
|
838
|
+
* Returns cache hit rate, pattern count, and learning metrics
|
|
839
|
+
*/
|
|
840
|
+
async getRuVectorMetrics() {
|
|
841
|
+
if (!this.hybridRouter) {
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
try {
|
|
845
|
+
return await this.hybridRouter.getRuVectorMetrics();
|
|
846
|
+
}
|
|
847
|
+
catch {
|
|
848
|
+
return null;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Get cache hit rate for this agent's requests
|
|
853
|
+
*/
|
|
854
|
+
getCacheHitRate() {
|
|
855
|
+
if (!this.hybridRouter) {
|
|
856
|
+
return 0;
|
|
857
|
+
}
|
|
858
|
+
return this.hybridRouter.getCacheHitRate();
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Get routing statistics including cache savings
|
|
862
|
+
*/
|
|
863
|
+
getRoutingStats() {
|
|
864
|
+
if (!this.hybridRouter) {
|
|
865
|
+
return {
|
|
866
|
+
totalDecisions: 0,
|
|
867
|
+
localDecisions: 0,
|
|
868
|
+
cloudDecisions: 0,
|
|
869
|
+
cacheHits: 0,
|
|
870
|
+
cacheMisses: 0,
|
|
871
|
+
cacheHitRate: 0,
|
|
872
|
+
averageLocalLatency: 0,
|
|
873
|
+
averageCloudLatency: 0,
|
|
874
|
+
successRate: 0,
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
return this.hybridRouter.getRoutingStats();
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Force learning consolidation in the RuVector cache
|
|
881
|
+
* Triggers LoRA adaptation and EWC++ protection
|
|
882
|
+
*/
|
|
883
|
+
async forceRuVectorLearn() {
|
|
884
|
+
if (!this.hybridRouter) {
|
|
885
|
+
return { success: false, error: 'RuVector not enabled' };
|
|
886
|
+
}
|
|
887
|
+
return await this.hybridRouter.forceRuVectorLearn();
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Get cost savings report from using RuVector cache
|
|
891
|
+
*/
|
|
892
|
+
getCostSavingsReport() {
|
|
893
|
+
if (!this.hybridRouter) {
|
|
894
|
+
return {
|
|
895
|
+
totalRequests: 0,
|
|
896
|
+
localRequests: 0,
|
|
897
|
+
cloudRequests: 0,
|
|
898
|
+
totalCost: 0,
|
|
899
|
+
estimatedCloudCost: 0,
|
|
900
|
+
savings: 0,
|
|
901
|
+
savingsPercentage: 0,
|
|
902
|
+
cacheHits: 0,
|
|
903
|
+
cacheSavings: 0,
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
return this.hybridRouter.getCostSavingsReport();
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Make an LLM call with automatic caching and learning
|
|
910
|
+
* Uses RuVector GNN cache when available for sub-ms pattern matching
|
|
911
|
+
*
|
|
912
|
+
* @param prompt - The prompt to process
|
|
913
|
+
* @param options - Additional options
|
|
914
|
+
* @returns The response text
|
|
915
|
+
*/
|
|
916
|
+
async llmCompleteWithLearning(prompt, options) {
|
|
917
|
+
if (!this.llmProvider) {
|
|
918
|
+
throw new Error(`[${this.agentId.id}] LLM not available - initialize agent first`);
|
|
919
|
+
}
|
|
920
|
+
// If using HybridRouter, it automatically handles caching and learning
|
|
921
|
+
if (this.hybridRouter) {
|
|
922
|
+
const completionOptions = {
|
|
923
|
+
model: options?.model || 'auto',
|
|
924
|
+
messages: [{ role: 'user', content: prompt }],
|
|
925
|
+
maxTokens: options?.maxTokens,
|
|
926
|
+
temperature: options?.temperature,
|
|
927
|
+
metadata: {
|
|
928
|
+
...options?.metadata,
|
|
929
|
+
agentId: this.agentId.id,
|
|
930
|
+
agentType: this.agentId.type,
|
|
931
|
+
complexity: options?.complexity,
|
|
932
|
+
},
|
|
933
|
+
};
|
|
934
|
+
const response = await this.hybridRouter.complete(completionOptions);
|
|
935
|
+
// Extract text from response
|
|
936
|
+
const text = response.content
|
|
937
|
+
.filter(block => block.type === 'text')
|
|
938
|
+
.map(block => block.text)
|
|
939
|
+
.join('\n');
|
|
940
|
+
return {
|
|
941
|
+
response: text,
|
|
942
|
+
source: response.metadata?.source || 'local',
|
|
943
|
+
confidence: response.metadata?.confidence,
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
// Fallback to regular completion
|
|
947
|
+
const text = await this.llmComplete(prompt, options);
|
|
948
|
+
return { response: text, source: 'local' };
|
|
949
|
+
}
|
|
950
|
+
// ============================================
|
|
951
|
+
// QE Pattern Store Methods (Phase 0.5 - RuVector Integration)
|
|
952
|
+
// ============================================
|
|
953
|
+
/**
|
|
954
|
+
* Initialize QE Pattern Store for learned pattern storage
|
|
955
|
+
* Supports both RuVector Docker (150x faster) and local HNSW fallback
|
|
956
|
+
*/
|
|
957
|
+
async initializePatternStore() {
|
|
958
|
+
if (!this.qePatternConfig.enabled) {
|
|
959
|
+
console.log(`[${this.agentId.id}] QE Pattern Store disabled by configuration`);
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
try {
|
|
963
|
+
// Phase 0.5: Use PostgreSQL adapter when RuVector is enabled
|
|
964
|
+
if (this.qePatternConfig.useRuVector) {
|
|
965
|
+
try {
|
|
966
|
+
const { createDockerRuVectorAdapter } = require('../providers/RuVectorPostgresAdapter');
|
|
967
|
+
// Parse PostgreSQL connection URL or use defaults
|
|
968
|
+
const url = this.qePatternConfig.ruvectorUrl || '';
|
|
969
|
+
const isPostgres = url.startsWith('postgresql://') || url.startsWith('postgres://');
|
|
970
|
+
if (isPostgres) {
|
|
971
|
+
// Parse connection URL
|
|
972
|
+
const parsed = new URL(url);
|
|
973
|
+
this.qePatternStore = createDockerRuVectorAdapter({
|
|
974
|
+
host: parsed.hostname,
|
|
975
|
+
port: parseInt(parsed.port) || 5432,
|
|
976
|
+
database: parsed.pathname.slice(1) || 'ruvector_db',
|
|
977
|
+
user: parsed.username || 'ruvector',
|
|
978
|
+
password: parsed.password || 'ruvector',
|
|
979
|
+
learningEnabled: true,
|
|
980
|
+
cacheThreshold: 0.85,
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
else {
|
|
984
|
+
// Use default Docker configuration
|
|
985
|
+
this.qePatternStore = createDockerRuVectorAdapter({
|
|
986
|
+
host: process.env.RUVECTOR_HOST || 'localhost',
|
|
987
|
+
port: parseInt(process.env.RUVECTOR_PORT || '5432'),
|
|
988
|
+
database: process.env.RUVECTOR_DATABASE || 'ruvector_db',
|
|
989
|
+
user: process.env.RUVECTOR_USER || 'ruvector',
|
|
990
|
+
password: process.env.RUVECTOR_PASSWORD || 'ruvector',
|
|
991
|
+
learningEnabled: true,
|
|
992
|
+
cacheThreshold: 0.85,
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
await this.qePatternStore.initialize();
|
|
996
|
+
this.patternStoreInitialized = true;
|
|
997
|
+
console.log(`[${this.agentId.id}] QE Pattern Store initialized (RuVector PostgreSQL: enabled)`);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
catch (postgresError) {
|
|
1001
|
+
console.warn(`[${this.agentId.id}] RuVector PostgreSQL unavailable, falling back to HNSW:`, postgresError.message);
|
|
1002
|
+
// Fall through to HNSW fallback
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
// Fallback: Use local RuVectorPatternStore (HNSW-based)
|
|
1006
|
+
const { RuVectorPatternStore } = require('../memory/RuVectorPatternStore');
|
|
1007
|
+
// Configure pattern store with local HNSW
|
|
1008
|
+
const storeConfig = {
|
|
1009
|
+
dimension: 384,
|
|
1010
|
+
metric: 'cosine',
|
|
1011
|
+
storagePath: this.qePatternConfig.storagePath,
|
|
1012
|
+
autoPersist: true,
|
|
1013
|
+
enableMetrics: true,
|
|
1014
|
+
hnsw: {
|
|
1015
|
+
m: 32,
|
|
1016
|
+
efConstruction: 200,
|
|
1017
|
+
efSearch: 100,
|
|
1018
|
+
},
|
|
1019
|
+
};
|
|
1020
|
+
this.qePatternStore = new RuVectorPatternStore(storeConfig);
|
|
1021
|
+
await this.qePatternStore.initialize();
|
|
1022
|
+
this.patternStoreInitialized = true;
|
|
1023
|
+
console.log(`[${this.agentId.id}] QE Pattern Store initialized (HNSW fallback)`);
|
|
1024
|
+
}
|
|
1025
|
+
catch (error) {
|
|
1026
|
+
console.warn(`[${this.agentId.id}] Pattern Store initialization failed:`, error.message);
|
|
1027
|
+
// Don't throw - agent can work without pattern store
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Check if QE pattern store is available
|
|
1032
|
+
*/
|
|
1033
|
+
hasPatternStore() {
|
|
1034
|
+
return this.patternStoreInitialized && this.qePatternStore !== undefined;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* Store a QE learned pattern
|
|
1038
|
+
* Automatically uses RuVector when available, falls back to HNSW
|
|
1039
|
+
*
|
|
1040
|
+
* @param pattern - The QE pattern to store
|
|
1041
|
+
* @returns Success status
|
|
1042
|
+
*/
|
|
1043
|
+
async storeQEPattern(pattern) {
|
|
1044
|
+
if (!this.hasPatternStore()) {
|
|
1045
|
+
return { success: false };
|
|
1046
|
+
}
|
|
1047
|
+
try {
|
|
1048
|
+
const testPattern = {
|
|
1049
|
+
id: pattern.id,
|
|
1050
|
+
type: pattern.type,
|
|
1051
|
+
domain: pattern.domain,
|
|
1052
|
+
content: pattern.content,
|
|
1053
|
+
embedding: pattern.embedding,
|
|
1054
|
+
framework: pattern.framework,
|
|
1055
|
+
coverage: pattern.coverage,
|
|
1056
|
+
createdAt: Date.now(),
|
|
1057
|
+
lastUsed: Date.now(),
|
|
1058
|
+
usageCount: 0,
|
|
1059
|
+
metadata: {
|
|
1060
|
+
agentId: this.agentId.id,
|
|
1061
|
+
agentType: this.agentId.type,
|
|
1062
|
+
...pattern.metadata,
|
|
1063
|
+
},
|
|
1064
|
+
};
|
|
1065
|
+
// Use auto-sync variant if enabled
|
|
1066
|
+
if (this.qePatternConfig.autoSync && this.qePatternStore.storeWithSync) {
|
|
1067
|
+
const result = await this.qePatternStore.storeWithSync(testPattern);
|
|
1068
|
+
return { success: result.stored, synced: result.synced };
|
|
1069
|
+
}
|
|
1070
|
+
await this.qePatternStore.storePattern(testPattern);
|
|
1071
|
+
return { success: true };
|
|
1072
|
+
}
|
|
1073
|
+
catch (error) {
|
|
1074
|
+
console.warn(`[${this.agentId.id}] Failed to store QE pattern:`, error.message);
|
|
1075
|
+
return { success: false };
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Search for similar QE patterns
|
|
1080
|
+
* Uses RuVector's 150x faster search when available
|
|
1081
|
+
*
|
|
1082
|
+
* @param embedding - Query embedding vector
|
|
1083
|
+
* @param k - Number of results to return (default: 10)
|
|
1084
|
+
* @param options - Additional search options
|
|
1085
|
+
* @returns Array of similar patterns with scores
|
|
1086
|
+
*/
|
|
1087
|
+
async searchQEPatterns(embedding, k = 10, options) {
|
|
1088
|
+
if (!this.hasPatternStore()) {
|
|
1089
|
+
return [];
|
|
1090
|
+
}
|
|
1091
|
+
try {
|
|
1092
|
+
const results = await this.qePatternStore.searchSimilar(embedding, {
|
|
1093
|
+
k,
|
|
1094
|
+
domain: options?.domain,
|
|
1095
|
+
type: options?.type,
|
|
1096
|
+
framework: options?.framework,
|
|
1097
|
+
threshold: options?.threshold ?? 0.7,
|
|
1098
|
+
useMMR: options?.useMMR ?? false,
|
|
1099
|
+
});
|
|
1100
|
+
return results;
|
|
1101
|
+
}
|
|
1102
|
+
catch (error) {
|
|
1103
|
+
console.warn(`[${this.agentId.id}] Pattern search failed:`, error.message);
|
|
1104
|
+
return [];
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Get QE pattern store metrics
|
|
1109
|
+
* Returns performance and usage statistics
|
|
1110
|
+
*/
|
|
1111
|
+
getQEPatternMetrics() {
|
|
1112
|
+
if (!this.hasPatternStore()) {
|
|
1113
|
+
return null;
|
|
1114
|
+
}
|
|
1115
|
+
try {
|
|
1116
|
+
const stats = this.qePatternStore.getStats?.();
|
|
1117
|
+
const perfMetrics = this.qePatternStore.getPerformanceMetrics?.();
|
|
1118
|
+
const metrics = {
|
|
1119
|
+
enabled: true,
|
|
1120
|
+
patternCount: stats?.count ?? 0,
|
|
1121
|
+
implementation: stats?.implementation ?? 'fallback',
|
|
1122
|
+
};
|
|
1123
|
+
if (perfMetrics) {
|
|
1124
|
+
metrics.performance = {
|
|
1125
|
+
avgSearchTime: perfMetrics.avgSearchTime,
|
|
1126
|
+
p50SearchTime: perfMetrics.p50SearchTime,
|
|
1127
|
+
p99SearchTime: perfMetrics.p99SearchTime,
|
|
1128
|
+
estimatedQPS: perfMetrics.estimatedQPS,
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
// GNN learning metrics if available
|
|
1132
|
+
if (this.qePatternStore.isGNNEnabled?.()) {
|
|
1133
|
+
const gnnMetrics = this.qePatternStore.getGNNMetrics?.();
|
|
1134
|
+
if (gnnMetrics) {
|
|
1135
|
+
metrics.gnnLearning = {
|
|
1136
|
+
enabled: gnnMetrics.enabled,
|
|
1137
|
+
cacheHitRate: gnnMetrics.cacheHitRate,
|
|
1138
|
+
patternsLearned: gnnMetrics.patternsLearned,
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
return metrics;
|
|
1143
|
+
}
|
|
1144
|
+
catch (error) {
|
|
1145
|
+
console.warn(`[${this.agentId.id}] Failed to get pattern metrics:`, error.message);
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
/**
|
|
1150
|
+
* Sync local patterns to remote RuVector service
|
|
1151
|
+
* Only applicable when RuVector Docker is enabled
|
|
1152
|
+
*/
|
|
1153
|
+
async syncPatternsToRemote(options) {
|
|
1154
|
+
if (!this.hasPatternStore() || !this.qePatternStore.syncToRemote) {
|
|
1155
|
+
return { synced: 0, failed: 0, duration: 0 };
|
|
1156
|
+
}
|
|
1157
|
+
try {
|
|
1158
|
+
const result = await this.qePatternStore.syncToRemote(options);
|
|
1159
|
+
console.log(`[${this.agentId.id}] Synced ${result.synced} patterns to remote (${result.duration}ms)`);
|
|
1160
|
+
return result;
|
|
1161
|
+
}
|
|
1162
|
+
catch (error) {
|
|
1163
|
+
console.warn(`[${this.agentId.id}] Pattern sync failed:`, error.message);
|
|
1164
|
+
return { synced: 0, failed: 0, duration: 0 };
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Force GNN learning consolidation on remote service
|
|
1169
|
+
* Triggers LoRA parameter updates with EWC protection
|
|
1170
|
+
*/
|
|
1171
|
+
async forcePatternLearning() {
|
|
1172
|
+
if (!this.hasPatternStore() || !this.qePatternStore.forceGNNLearn) {
|
|
1173
|
+
return { success: false, patternsConsolidated: 0, duration: 0 };
|
|
1174
|
+
}
|
|
1175
|
+
try {
|
|
1176
|
+
const result = await this.qePatternStore.forceGNNLearn({
|
|
1177
|
+
domain: this.agentId.type,
|
|
1178
|
+
});
|
|
1179
|
+
return {
|
|
1180
|
+
success: result.success,
|
|
1181
|
+
patternsConsolidated: result.patternsConsolidated,
|
|
1182
|
+
duration: result.duration,
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
catch (error) {
|
|
1186
|
+
console.warn(`[${this.agentId.id}] Force learning failed:`, error.message);
|
|
1187
|
+
return { success: false, patternsConsolidated: 0, duration: 0 };
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Cleanup pattern store resources on agent termination
|
|
1192
|
+
*/
|
|
1193
|
+
async cleanupPatternStore() {
|
|
1194
|
+
if (!this.patternStoreInitialized || !this.qePatternStore) {
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
try {
|
|
1198
|
+
// Sync pending patterns before shutdown
|
|
1199
|
+
if (this.qePatternConfig.autoSync && this.qePatternStore.syncToRemote) {
|
|
1200
|
+
await this.qePatternStore.syncToRemote({ force: true });
|
|
1201
|
+
}
|
|
1202
|
+
// Shutdown and release resources
|
|
1203
|
+
if (this.qePatternStore.shutdown) {
|
|
1204
|
+
await this.qePatternStore.shutdown();
|
|
1205
|
+
}
|
|
1206
|
+
console.log(`[${this.agentId.id}] Pattern Store cleanup complete`);
|
|
1207
|
+
}
|
|
1208
|
+
catch (error) {
|
|
1209
|
+
console.warn(`[${this.agentId.id}] Pattern Store cleanup error:`, error.message);
|
|
1210
|
+
}
|
|
1211
|
+
this.patternStoreInitialized = false;
|
|
1212
|
+
this.qePatternStore = undefined;
|
|
1213
|
+
}
|
|
766
1214
|
/**
|
|
767
1215
|
* Cleanup LLM resources on agent termination
|
|
768
1216
|
*/
|