@skillsmith/core 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/analysis/types.d.ts +2 -0
- package/dist/src/analysis/types.d.ts.map +1 -1
- package/dist/src/analysis/types.js +13 -1
- package/dist/src/analysis/types.js.map +1 -1
- package/dist/src/analytics/AnalyticsRepository.d.ts +4 -0
- package/dist/src/analytics/AnalyticsRepository.d.ts.map +1 -1
- package/dist/src/analytics/AnalyticsRepository.js +26 -44
- package/dist/src/analytics/AnalyticsRepository.js.map +1 -1
- package/dist/src/analytics/schema.d.ts +1 -1
- package/dist/src/analytics/schema.d.ts.map +1 -1
- package/dist/src/analytics/schema.js +68 -0
- package/dist/src/analytics/schema.js.map +1 -1
- package/dist/src/api/client.d.ts +33 -29
- package/dist/src/api/client.d.ts.map +1 -1
- package/dist/src/api/client.js +15 -10
- package/dist/src/api/client.js.map +1 -1
- package/dist/src/billing/BillingService.d.ts +139 -0
- package/dist/src/billing/BillingService.d.ts.map +1 -0
- package/dist/src/billing/BillingService.js +393 -0
- package/dist/src/billing/BillingService.js.map +1 -0
- package/dist/src/billing/GDPRComplianceService.d.ts +176 -0
- package/dist/src/billing/GDPRComplianceService.d.ts.map +1 -0
- package/dist/src/billing/GDPRComplianceService.js +361 -0
- package/dist/src/billing/GDPRComplianceService.js.map +1 -0
- package/dist/src/billing/StripeClient.d.ts +177 -0
- package/dist/src/billing/StripeClient.d.ts.map +1 -0
- package/dist/src/billing/StripeClient.js +462 -0
- package/dist/src/billing/StripeClient.js.map +1 -0
- package/dist/src/billing/StripeReconciliationJob.d.ts +95 -0
- package/dist/src/billing/StripeReconciliationJob.d.ts.map +1 -0
- package/dist/src/billing/StripeReconciliationJob.js +405 -0
- package/dist/src/billing/StripeReconciliationJob.js.map +1 -0
- package/dist/src/billing/StripeWebhookHandler.d.ts +92 -0
- package/dist/src/billing/StripeWebhookHandler.d.ts.map +1 -0
- package/dist/src/billing/StripeWebhookHandler.js +409 -0
- package/dist/src/billing/StripeWebhookHandler.js.map +1 -0
- package/dist/src/billing/index.d.ts +18 -0
- package/dist/src/billing/index.d.ts.map +1 -0
- package/dist/src/billing/index.js +19 -0
- package/dist/src/billing/index.js.map +1 -0
- package/dist/src/billing/types.d.ts +266 -0
- package/dist/src/billing/types.d.ts.map +1 -0
- package/dist/src/billing/types.js +23 -0
- package/dist/src/billing/types.js.map +1 -0
- package/dist/src/embeddings/hnsw-store.d.ts +568 -0
- package/dist/src/embeddings/hnsw-store.d.ts.map +1 -0
- package/dist/src/embeddings/hnsw-store.js +805 -0
- package/dist/src/embeddings/hnsw-store.js.map +1 -0
- package/dist/src/embeddings/index.d.ts +2 -0
- package/dist/src/embeddings/index.d.ts.map +1 -1
- package/dist/src/embeddings/index.js +2 -0
- package/dist/src/embeddings/index.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/learning/PatternStore.d.ts +457 -0
- package/dist/src/learning/PatternStore.d.ts.map +1 -0
- package/dist/src/learning/PatternStore.js +893 -0
- package/dist/src/learning/PatternStore.js.map +1 -0
- package/dist/src/learning/ReasoningBankIntegration.d.ts +403 -0
- package/dist/src/learning/ReasoningBankIntegration.d.ts.map +1 -0
- package/dist/src/learning/ReasoningBankIntegration.js +627 -0
- package/dist/src/learning/ReasoningBankIntegration.js.map +1 -0
- package/dist/src/learning/index.d.ts +15 -0
- package/dist/src/learning/index.d.ts.map +1 -0
- package/dist/src/learning/index.js +15 -0
- package/dist/src/learning/index.js.map +1 -0
- package/dist/src/routing/SONARouter.d.ts +154 -0
- package/dist/src/routing/SONARouter.d.ts.map +1 -0
- package/dist/src/routing/SONARouter.js +679 -0
- package/dist/src/routing/SONARouter.js.map +1 -0
- package/dist/src/routing/index.d.ts +9 -0
- package/dist/src/routing/index.d.ts.map +1 -0
- package/dist/src/routing/index.js +10 -0
- package/dist/src/routing/index.js.map +1 -0
- package/dist/src/routing/types.d.ts +331 -0
- package/dist/src/routing/types.d.ts.map +1 -0
- package/dist/src/routing/types.js +203 -0
- package/dist/src/routing/types.js.map +1 -0
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js +5 -0
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
- package/dist/src/security/SkillSandbox.d.ts +156 -0
- package/dist/src/security/SkillSandbox.d.ts.map +1 -0
- package/dist/src/security/SkillSandbox.js +303 -0
- package/dist/src/security/SkillSandbox.js.map +1 -0
- package/dist/src/security/index.d.ts +3 -1
- package/dist/src/security/index.d.ts.map +1 -1
- package/dist/src/security/index.js +5 -1
- package/dist/src/security/index.js.map +1 -1
- package/dist/src/security/rate-limiter/presets.d.ts +12 -0
- package/dist/src/security/rate-limiter/presets.d.ts.map +1 -1
- package/dist/src/security/rate-limiter/presets.js +12 -0
- package/dist/src/security/rate-limiter/presets.js.map +1 -1
- package/dist/src/security/sanitization.d.ts +85 -0
- package/dist/src/security/sanitization.d.ts.map +1 -1
- package/dist/src/security/sanitization.js +133 -0
- package/dist/src/security/sanitization.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.d.ts +23 -0
- package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.js +232 -28
- package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
- package/dist/src/security/scanner/patterns.d.ts +13 -0
- package/dist/src/security/scanner/patterns.d.ts.map +1 -1
- package/dist/src/security/scanner/patterns.js +51 -0
- package/dist/src/security/scanner/patterns.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +13 -1
- package/dist/src/security/scanner/types.d.ts.map +1 -1
- package/dist/src/security/scanner/weights.d.ts.map +1 -1
- package/dist/src/security/scanner/weights.js +1 -0
- package/dist/src/security/scanner/weights.js.map +1 -1
- package/dist/src/session/SessionManager.d.ts +7 -0
- package/dist/src/session/SessionManager.d.ts.map +1 -1
- package/dist/src/session/SessionManager.js +117 -10
- package/dist/src/session/SessionManager.js.map +1 -1
- package/dist/src/sync/SyncEngine.d.ts.map +1 -1
- package/dist/src/sync/SyncEngine.js +52 -32
- package/dist/src/sync/SyncEngine.js.map +1 -1
- package/dist/src/testing/MultiLLMProvider.d.ts +374 -0
- package/dist/src/testing/MultiLLMProvider.d.ts.map +1 -0
- package/dist/src/testing/MultiLLMProvider.js +720 -0
- package/dist/src/testing/MultiLLMProvider.js.map +1 -0
- package/dist/src/testing/index.d.ts +8 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +9 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/types.d.ts +3 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tests/SecurityScanner.test.js +337 -1
- package/dist/tests/SecurityScanner.test.js.map +1 -1
- package/dist/tests/billing/BillingService.test.d.ts +7 -0
- package/dist/tests/billing/BillingService.test.d.ts.map +1 -0
- package/dist/tests/billing/BillingService.test.js +168 -0
- package/dist/tests/billing/BillingService.test.js.map +1 -0
- package/dist/tests/billing/GDPRCompliance.test.d.ts +7 -0
- package/dist/tests/billing/GDPRCompliance.test.d.ts.map +1 -0
- package/dist/tests/billing/GDPRCompliance.test.js +195 -0
- package/dist/tests/billing/GDPRCompliance.test.js.map +1 -0
- package/dist/tests/billing/StripeReconciliation.test.d.ts +7 -0
- package/dist/tests/billing/StripeReconciliation.test.d.ts.map +1 -0
- package/dist/tests/billing/StripeReconciliation.test.js +266 -0
- package/dist/tests/billing/StripeReconciliation.test.js.map +1 -0
- package/dist/tests/billing/stripe-validators.test.d.ts +7 -0
- package/dist/tests/billing/stripe-validators.test.d.ts.map +1 -0
- package/dist/tests/billing/stripe-validators.test.js +107 -0
- package/dist/tests/billing/stripe-validators.test.js.map +1 -0
- package/dist/tests/embeddings/hnsw-store.test.d.ts +7 -0
- package/dist/tests/embeddings/hnsw-store.test.d.ts.map +1 -0
- package/dist/tests/embeddings/hnsw-store.test.js +295 -0
- package/dist/tests/embeddings/hnsw-store.test.js.map +1 -0
- package/dist/tests/integration/neural/e2e-learning.test.d.ts +17 -0
- package/dist/tests/integration/neural/e2e-learning.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/e2e-learning.test.js +238 -0
- package/dist/tests/integration/neural/e2e-learning.test.js.map +1 -0
- package/dist/tests/integration/neural/helpers.d.ts +132 -0
- package/dist/tests/integration/neural/helpers.d.ts.map +1 -0
- package/dist/tests/integration/neural/helpers.js +287 -0
- package/dist/tests/integration/neural/helpers.js.map +1 -0
- package/dist/tests/integration/neural/personalization.test.d.ts +21 -0
- package/dist/tests/integration/neural/personalization.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/personalization.test.js +304 -0
- package/dist/tests/integration/neural/personalization.test.js.map +1 -0
- package/dist/tests/integration/neural/preference-learner.test.d.ts +23 -0
- package/dist/tests/integration/neural/preference-learner.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/preference-learner.test.js +289 -0
- package/dist/tests/integration/neural/preference-learner.test.js.map +1 -0
- package/dist/tests/integration/neural/privacy.test.d.ts +19 -0
- package/dist/tests/integration/neural/privacy.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/privacy.test.js +249 -0
- package/dist/tests/integration/neural/privacy.test.js.map +1 -0
- package/dist/tests/integration/neural/setup.d.ts +175 -0
- package/dist/tests/integration/neural/setup.d.ts.map +1 -0
- package/dist/tests/integration/neural/setup.js +487 -0
- package/dist/tests/integration/neural/setup.js.map +1 -0
- package/dist/tests/integration/neural/signal-collection.test.d.ts +21 -0
- package/dist/tests/integration/neural/signal-collection.test.d.ts.map +1 -0
- package/dist/tests/integration/neural/signal-collection.test.js +232 -0
- package/dist/tests/integration/neural/signal-collection.test.js.map +1 -0
- package/dist/tests/learning/PatternStore.test.d.ts +8 -0
- package/dist/tests/learning/PatternStore.test.d.ts.map +1 -0
- package/dist/tests/learning/PatternStore.test.js +589 -0
- package/dist/tests/learning/PatternStore.test.js.map +1 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.d.ts +8 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.d.ts.map +1 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.js +269 -0
- package/dist/tests/learning/ReasoningBankIntegration.test.js.map +1 -0
- package/dist/tests/routing/SONARouter.test.d.ts +8 -0
- package/dist/tests/routing/SONARouter.test.d.ts.map +1 -0
- package/dist/tests/routing/SONARouter.test.js +400 -0
- package/dist/tests/routing/SONARouter.test.js.map +1 -0
- package/dist/tests/security/ContinuousSecurity.test.js +10 -12
- package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
- package/dist/tests/security/SkillSandbox.test.d.ts +8 -0
- package/dist/tests/security/SkillSandbox.test.d.ts.map +1 -0
- package/dist/tests/security/SkillSandbox.test.js +321 -0
- package/dist/tests/security/SkillSandbox.test.js.map +1 -0
- package/dist/tests/sync/SyncEngine.test.js +4 -2
- package/dist/tests/sync/SyncEngine.test.js.map +1 -1
- package/dist/tests/testing/MultiLLMProvider.test.d.ts +14 -0
- package/dist/tests/testing/MultiLLMProvider.test.d.ts.map +1 -0
- package/dist/tests/testing/MultiLLMProvider.test.js +438 -0
- package/dist/tests/testing/MultiLLMProvider.test.js.map +1 -0
- package/package.json +16 -3
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview PatternStore with EWC++ for catastrophic forgetting prevention
|
|
3
|
+
* @module @skillsmith/core/learning/PatternStore
|
|
4
|
+
* @see SMI-1522: Add EWC++ pattern storage for successful matches
|
|
5
|
+
*
|
|
6
|
+
* Implements Elastic Weight Consolidation++ (EWC++) to store successful
|
|
7
|
+
* recommendation patterns without catastrophic forgetting. Unlike traditional
|
|
8
|
+
* storage that overwrites old patterns, EWC++ preserves important learned
|
|
9
|
+
* patterns while integrating new ones.
|
|
10
|
+
*
|
|
11
|
+
* Key capabilities:
|
|
12
|
+
* - storePattern(): Encodes successful matches with Fisher Information tracking
|
|
13
|
+
* - findSimilarPatterns(): Retrieves relevant patterns using importance-weighted similarity
|
|
14
|
+
* - consolidate(): Updates Fisher Information matrix without forgetting important patterns
|
|
15
|
+
* - 95%+ pattern preservation: New patterns do not overwrite important historical patterns
|
|
16
|
+
*
|
|
17
|
+
* @see https://arxiv.org/abs/1801.10112 (Progress & Compress)
|
|
18
|
+
*/
|
|
19
|
+
import Database from 'better-sqlite3';
|
|
20
|
+
import { randomUUID } from 'crypto';
|
|
21
|
+
import { EmbeddingService } from '../embeddings/index.js';
|
|
22
|
+
/**
|
|
23
|
+
* Default EWC++ configuration
|
|
24
|
+
*/
|
|
25
|
+
export const DEFAULT_EWC_CONFIG = {
|
|
26
|
+
lambda: 5.0,
|
|
27
|
+
fisherDecay: 0.95,
|
|
28
|
+
importanceThreshold: 0.01,
|
|
29
|
+
fisherSampleSize: 100,
|
|
30
|
+
consolidationThreshold: 0.1,
|
|
31
|
+
maxPatterns: 10000,
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Default PatternStore configuration
|
|
35
|
+
*/
|
|
36
|
+
export const DEFAULT_PATTERN_STORE_CONFIG = {
|
|
37
|
+
dbPath: undefined,
|
|
38
|
+
ewc: DEFAULT_EWC_CONFIG,
|
|
39
|
+
dimensions: 384,
|
|
40
|
+
autoConsolidate: true,
|
|
41
|
+
trackAccess: true,
|
|
42
|
+
useV3Integration: true,
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Reward values for pattern outcomes
|
|
46
|
+
* Matches ReasoningBankIntegration.TRAJECTORY_REWARDS
|
|
47
|
+
*/
|
|
48
|
+
export const PATTERN_REWARDS = {
|
|
49
|
+
accept: 1.0,
|
|
50
|
+
usage: 0.3,
|
|
51
|
+
frequent: 0.5,
|
|
52
|
+
dismiss: -0.5,
|
|
53
|
+
abandonment: -0.3,
|
|
54
|
+
uninstall: -0.7,
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Fisher Information Matrix implementation for EWC++
|
|
58
|
+
*
|
|
59
|
+
* Stores diagonal approximation of Fisher Information,
|
|
60
|
+
* indicating which "weights" (pattern dimensions) are important.
|
|
61
|
+
*
|
|
62
|
+
* In the context of pattern storage:
|
|
63
|
+
* - Each dimension of the context embedding has an importance value
|
|
64
|
+
* - High importance = changing this dimension would harm prediction
|
|
65
|
+
* - Low importance = safe to overwrite with new patterns
|
|
66
|
+
*/
|
|
67
|
+
export class FisherInformationMatrix {
|
|
68
|
+
dimensions;
|
|
69
|
+
/** Diagonal of Fisher Information (importance per dimension) */
|
|
70
|
+
importance;
|
|
71
|
+
/** Running sum for online updates */
|
|
72
|
+
runningSum;
|
|
73
|
+
/** Number of updates performed */
|
|
74
|
+
updateCount = 0;
|
|
75
|
+
constructor(dimensions) {
|
|
76
|
+
this.dimensions = dimensions;
|
|
77
|
+
this.importance = new Float32Array(dimensions);
|
|
78
|
+
this.runningSum = new Float32Array(dimensions);
|
|
79
|
+
}
|
|
80
|
+
getImportance(dimensionIndex) {
|
|
81
|
+
return this.importance[dimensionIndex] ?? 0;
|
|
82
|
+
}
|
|
83
|
+
update(gradient) {
|
|
84
|
+
// EWC++: F = decay * F + gradient^2
|
|
85
|
+
for (let i = 0; i < Math.min(gradient.length, this.dimensions); i++) {
|
|
86
|
+
this.runningSum[i] += gradient[i] * gradient[i];
|
|
87
|
+
}
|
|
88
|
+
this.updateCount++;
|
|
89
|
+
// Update importance as running mean
|
|
90
|
+
for (let i = 0; i < this.importance.length; i++) {
|
|
91
|
+
this.importance[i] = this.runningSum[i] / this.updateCount;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
decay(decayFactor) {
|
|
95
|
+
for (let i = 0; i < this.runningSum.length; i++) {
|
|
96
|
+
this.runningSum[i] *= decayFactor;
|
|
97
|
+
}
|
|
98
|
+
// Recalculate importance after decay
|
|
99
|
+
for (let i = 0; i < this.importance.length; i++) {
|
|
100
|
+
this.importance[i] = this.runningSum[i] / Math.max(1, this.updateCount);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
getImportanceVector() {
|
|
104
|
+
return new Float32Array(this.importance);
|
|
105
|
+
}
|
|
106
|
+
getAverageImportance() {
|
|
107
|
+
let sum = 0;
|
|
108
|
+
for (let i = 0; i < this.importance.length; i++) {
|
|
109
|
+
sum += this.importance[i];
|
|
110
|
+
}
|
|
111
|
+
return sum / this.importance.length;
|
|
112
|
+
}
|
|
113
|
+
serialize() {
|
|
114
|
+
const buffer = Buffer.alloc(4 + // updateCount
|
|
115
|
+
4 * this.importance.length + // importance
|
|
116
|
+
4 * this.runningSum.length // runningSum
|
|
117
|
+
);
|
|
118
|
+
buffer.writeUInt32LE(this.updateCount, 0);
|
|
119
|
+
Buffer.from(this.importance.buffer).copy(buffer, 4);
|
|
120
|
+
Buffer.from(this.runningSum.buffer).copy(buffer, 4 + 4 * this.importance.length);
|
|
121
|
+
return buffer;
|
|
122
|
+
}
|
|
123
|
+
deserialize(buffer) {
|
|
124
|
+
const expectedSize = 4 + 4 * this.dimensions * 2;
|
|
125
|
+
if (buffer.length < expectedSize) {
|
|
126
|
+
throw new Error(`Invalid Fisher matrix buffer: expected ${expectedSize} bytes, got ${buffer.length}`);
|
|
127
|
+
}
|
|
128
|
+
this.updateCount = buffer.readUInt32LE(0);
|
|
129
|
+
const importanceOffset = 4;
|
|
130
|
+
const runningSumOffset = 4 + 4 * this.dimensions;
|
|
131
|
+
// Copy importance values
|
|
132
|
+
for (let i = 0; i < this.dimensions; i++) {
|
|
133
|
+
this.importance[i] = buffer.readFloatLE(importanceOffset + i * 4);
|
|
134
|
+
}
|
|
135
|
+
// Copy runningSum values
|
|
136
|
+
for (let i = 0; i < this.dimensions; i++) {
|
|
137
|
+
this.runningSum[i] = buffer.readFloatLE(runningSumOffset + i * 4);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
reset() {
|
|
141
|
+
this.importance.fill(0);
|
|
142
|
+
this.runningSum.fill(0);
|
|
143
|
+
this.updateCount = 0;
|
|
144
|
+
}
|
|
145
|
+
getUpdateCount() {
|
|
146
|
+
return this.updateCount;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// PatternStore Implementation
|
|
151
|
+
// ============================================================================
|
|
152
|
+
/**
|
|
153
|
+
* SQLite schema for pattern storage
|
|
154
|
+
*/
|
|
155
|
+
const PATTERN_STORE_SCHEMA = `
|
|
156
|
+
-- Patterns table: stores recommendation patterns with outcomes
|
|
157
|
+
CREATE TABLE IF NOT EXISTS patterns (
|
|
158
|
+
pattern_id TEXT PRIMARY KEY,
|
|
159
|
+
context_embedding BLOB NOT NULL,
|
|
160
|
+
skill_id TEXT NOT NULL,
|
|
161
|
+
skill_features TEXT NOT NULL,
|
|
162
|
+
context_data TEXT NOT NULL,
|
|
163
|
+
outcome_type TEXT NOT NULL,
|
|
164
|
+
outcome_reward REAL NOT NULL,
|
|
165
|
+
importance REAL NOT NULL DEFAULT 0.1,
|
|
166
|
+
original_score REAL NOT NULL,
|
|
167
|
+
source TEXT NOT NULL,
|
|
168
|
+
access_count INTEGER NOT NULL DEFAULT 0,
|
|
169
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
170
|
+
last_accessed_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
-- Indexes for efficient queries
|
|
174
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_skill_id ON patterns(skill_id);
|
|
175
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_outcome_type ON patterns(outcome_type);
|
|
176
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_importance ON patterns(importance DESC);
|
|
177
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_created_at ON patterns(created_at DESC);
|
|
178
|
+
|
|
179
|
+
-- Fisher Information matrix state
|
|
180
|
+
CREATE TABLE IF NOT EXISTS fisher_info (
|
|
181
|
+
id INTEGER PRIMARY KEY DEFAULT 1,
|
|
182
|
+
matrix_data BLOB NOT NULL,
|
|
183
|
+
update_count INTEGER NOT NULL DEFAULT 0,
|
|
184
|
+
last_decay_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
185
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
-- Consolidation history for monitoring
|
|
189
|
+
CREATE TABLE IF NOT EXISTS consolidation_history (
|
|
190
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
191
|
+
timestamp INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
192
|
+
patterns_processed INTEGER NOT NULL,
|
|
193
|
+
patterns_preserved INTEGER NOT NULL,
|
|
194
|
+
patterns_pruned INTEGER NOT NULL,
|
|
195
|
+
preservation_rate REAL NOT NULL,
|
|
196
|
+
duration_ms INTEGER NOT NULL,
|
|
197
|
+
average_importance REAL NOT NULL
|
|
198
|
+
);
|
|
199
|
+
`;
|
|
200
|
+
/**
|
|
201
|
+
* PatternStore - EWC++ pattern storage for successful recommendation matches
|
|
202
|
+
*
|
|
203
|
+
* Prevents catastrophic forgetting by tracking which pattern dimensions
|
|
204
|
+
* are important via Fisher Information Matrix and preserving high-importance
|
|
205
|
+
* patterns during consolidation.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* const store = new PatternStore({ dbPath: './patterns.db' })
|
|
210
|
+
* await store.initialize()
|
|
211
|
+
*
|
|
212
|
+
* // Store a successful pattern
|
|
213
|
+
* await store.storePattern(
|
|
214
|
+
* {
|
|
215
|
+
* context: { installedSkills: ['commit'], frameworks: ['react'] },
|
|
216
|
+
* skill: { skillId: 'jest-helper', category: 'testing' },
|
|
217
|
+
* originalScore: 0.85,
|
|
218
|
+
* source: 'recommend'
|
|
219
|
+
* },
|
|
220
|
+
* { type: 'accept', reward: 1.0 }
|
|
221
|
+
* )
|
|
222
|
+
*
|
|
223
|
+
* // Find similar patterns
|
|
224
|
+
* const similar = await store.findSimilarPatterns({
|
|
225
|
+
* context: { installedSkills: ['commit'], frameworks: ['react', 'typescript'] }
|
|
226
|
+
* })
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
export class PatternStore {
|
|
230
|
+
db;
|
|
231
|
+
fisherMatrix;
|
|
232
|
+
embeddingService;
|
|
233
|
+
consolidationState;
|
|
234
|
+
config;
|
|
235
|
+
ewcConfig;
|
|
236
|
+
initialized = false;
|
|
237
|
+
// Query performance tracking
|
|
238
|
+
queryLatencies = [];
|
|
239
|
+
maxQuerySamples = 100;
|
|
240
|
+
constructor(config = {}) {
|
|
241
|
+
this.config = {
|
|
242
|
+
...DEFAULT_PATTERN_STORE_CONFIG,
|
|
243
|
+
...config,
|
|
244
|
+
ewc: { ...DEFAULT_EWC_CONFIG, ...config.ewc },
|
|
245
|
+
};
|
|
246
|
+
this.ewcConfig = this.config.ewc;
|
|
247
|
+
this.consolidationState = {
|
|
248
|
+
lastConsolidation: null,
|
|
249
|
+
patternsSinceLastConsolidation: 0,
|
|
250
|
+
totalPatterns: 0,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Initialize the PatternStore
|
|
255
|
+
*/
|
|
256
|
+
async initialize() {
|
|
257
|
+
if (this.initialized)
|
|
258
|
+
return;
|
|
259
|
+
// Initialize database
|
|
260
|
+
this.db = new Database(this.config.dbPath || ':memory:');
|
|
261
|
+
this.db.exec(PATTERN_STORE_SCHEMA);
|
|
262
|
+
// Initialize Fisher Information Matrix
|
|
263
|
+
this.fisherMatrix = new FisherInformationMatrix(this.config.dimensions);
|
|
264
|
+
// Load persisted Fisher matrix if exists
|
|
265
|
+
this.loadFisherMatrix();
|
|
266
|
+
// Initialize embedding service (fallback mode for context encoding)
|
|
267
|
+
this.embeddingService = new EmbeddingService({ useFallback: true });
|
|
268
|
+
// Load consolidation state
|
|
269
|
+
this.consolidationState.totalPatterns = this.getPatternCount();
|
|
270
|
+
// Try V3 integration
|
|
271
|
+
if (this.config.useV3Integration) {
|
|
272
|
+
await this.initializeV3Integration();
|
|
273
|
+
}
|
|
274
|
+
this.initialized = true;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Check if PatternStore is initialized
|
|
278
|
+
*/
|
|
279
|
+
isInitialized() {
|
|
280
|
+
return this.initialized;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Attempt to initialize V3 ReasoningBank integration
|
|
284
|
+
*/
|
|
285
|
+
async initializeV3Integration() {
|
|
286
|
+
try {
|
|
287
|
+
// Attempt to import V3 ReasoningBank for future integration
|
|
288
|
+
await import(
|
|
289
|
+
// @ts-expect-error - V3 types not available at compile time
|
|
290
|
+
'claude-flow/v3/@claude-flow/cli/dist/src/intelligence/index.js');
|
|
291
|
+
console.log('[PatternStore] V3 ReasoningBank integration enabled');
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
console.log('[PatternStore] V3 not available, using standalone mode');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Store a pattern with EWC++ protection
|
|
299
|
+
*
|
|
300
|
+
* @param pattern - Pattern to store
|
|
301
|
+
* @param outcome - Outcome of the recommendation
|
|
302
|
+
* @returns Pattern ID
|
|
303
|
+
*/
|
|
304
|
+
async storePattern(pattern, outcome) {
|
|
305
|
+
this.ensureInitialized();
|
|
306
|
+
// Generate context embedding
|
|
307
|
+
const contextText = this.contextToText(pattern.context);
|
|
308
|
+
const contextEmbedding = await this.embeddingService.embed(contextText);
|
|
309
|
+
// Check for similar existing pattern
|
|
310
|
+
const existingPatterns = await this.findSimilarPatterns({
|
|
311
|
+
context: pattern.context,
|
|
312
|
+
skillId: pattern.skill.skillId,
|
|
313
|
+
positiveOnly: false,
|
|
314
|
+
}, 5);
|
|
315
|
+
if (existingPatterns.length > 0 && existingPatterns[0].similarity > 0.95) {
|
|
316
|
+
// Update existing pattern instead of creating new
|
|
317
|
+
const existingPattern = existingPatterns[0].pattern;
|
|
318
|
+
const gradient = this.computeGradient(contextEmbedding, existingPattern.contextEmbedding);
|
|
319
|
+
this.fisherMatrix.update(gradient);
|
|
320
|
+
const newImportance = this.calculatePatternImportance(existingPattern, outcome);
|
|
321
|
+
this.updatePatternInDB(existingPattern.id, {
|
|
322
|
+
importance: newImportance,
|
|
323
|
+
accessCount: existingPattern.accessCount + 1,
|
|
324
|
+
});
|
|
325
|
+
return existingPattern.id;
|
|
326
|
+
}
|
|
327
|
+
// Calculate initial importance
|
|
328
|
+
let baseImportance = Math.abs(outcome.reward);
|
|
329
|
+
if (outcome.reward > 0) {
|
|
330
|
+
baseImportance *= 1.5;
|
|
331
|
+
}
|
|
332
|
+
if (outcome.confidence !== undefined) {
|
|
333
|
+
baseImportance *= outcome.confidence;
|
|
334
|
+
}
|
|
335
|
+
const importance = baseImportance * this.ewcConfig.importanceThreshold * 10;
|
|
336
|
+
// Store new pattern
|
|
337
|
+
const patternId = pattern.id || randomUUID();
|
|
338
|
+
const stmt = this.db.prepare(`
|
|
339
|
+
INSERT INTO patterns (
|
|
340
|
+
pattern_id, context_embedding, skill_id, skill_features, context_data,
|
|
341
|
+
outcome_type, outcome_reward, importance, original_score, source,
|
|
342
|
+
access_count, created_at, last_accessed_at
|
|
343
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, unixepoch(), unixepoch())
|
|
344
|
+
`);
|
|
345
|
+
stmt.run(patternId, Buffer.from(contextEmbedding.buffer), pattern.skill.skillId, JSON.stringify(pattern.skill), JSON.stringify(pattern.context), outcome.type, outcome.reward, importance, pattern.originalScore, pattern.source);
|
|
346
|
+
// Update Fisher Information
|
|
347
|
+
const avgEmbedding = await this.computeAverageEmbedding();
|
|
348
|
+
const gradient = this.computeGradient(contextEmbedding, avgEmbedding);
|
|
349
|
+
this.fisherMatrix.update(gradient);
|
|
350
|
+
// Update consolidation state
|
|
351
|
+
this.consolidationState.patternsSinceLastConsolidation++;
|
|
352
|
+
this.consolidationState.totalPatterns++;
|
|
353
|
+
// Trigger consolidation if needed
|
|
354
|
+
if (this.config.autoConsolidate && this.shouldConsolidate()) {
|
|
355
|
+
await this.consolidate();
|
|
356
|
+
}
|
|
357
|
+
// Persist Fisher matrix
|
|
358
|
+
this.saveFisherMatrix();
|
|
359
|
+
return patternId;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Find similar patterns using importance-weighted similarity
|
|
363
|
+
*
|
|
364
|
+
* @param query - Pattern query
|
|
365
|
+
* @param limit - Maximum results
|
|
366
|
+
* @returns Similar patterns sorted by weighted similarity
|
|
367
|
+
*/
|
|
368
|
+
async findSimilarPatterns(query, limit = 10) {
|
|
369
|
+
this.ensureInitialized();
|
|
370
|
+
const startTime = Date.now();
|
|
371
|
+
// Generate query embedding
|
|
372
|
+
const queryText = this.contextToText(query.context);
|
|
373
|
+
const queryEmbedding = await this.embeddingService.embed(queryText);
|
|
374
|
+
// Build SQL query with filters
|
|
375
|
+
let sql = 'SELECT * FROM patterns WHERE 1=1';
|
|
376
|
+
const params = [];
|
|
377
|
+
if (query.skillId) {
|
|
378
|
+
sql += ' AND skill_id = ?';
|
|
379
|
+
params.push(query.skillId);
|
|
380
|
+
}
|
|
381
|
+
if (query.category) {
|
|
382
|
+
sql += " AND json_extract(skill_features, '$.category') = ?";
|
|
383
|
+
params.push(query.category);
|
|
384
|
+
}
|
|
385
|
+
if (query.minImportance !== undefined) {
|
|
386
|
+
sql += ' AND importance >= ?';
|
|
387
|
+
params.push(query.minImportance);
|
|
388
|
+
}
|
|
389
|
+
if (query.outcomeType) {
|
|
390
|
+
sql += ' AND outcome_type = ?';
|
|
391
|
+
params.push(query.outcomeType);
|
|
392
|
+
}
|
|
393
|
+
if (query.positiveOnly) {
|
|
394
|
+
sql += ' AND outcome_reward > 0';
|
|
395
|
+
}
|
|
396
|
+
// Fetch candidate patterns
|
|
397
|
+
const stmt = this.db.prepare(sql);
|
|
398
|
+
const candidates = stmt.all(...params);
|
|
399
|
+
// Calculate similarity scores
|
|
400
|
+
const importanceVector = this.fisherMatrix.getImportanceVector();
|
|
401
|
+
const results = [];
|
|
402
|
+
for (const candidate of candidates) {
|
|
403
|
+
const candidateEmbedding = this.deserializeEmbedding(candidate.context_embedding);
|
|
404
|
+
const similarity = this.cosineSimilarity(queryEmbedding, candidateEmbedding);
|
|
405
|
+
const weightedSimilarity = this.importanceWeightedSimilarity(queryEmbedding, candidateEmbedding, importanceVector);
|
|
406
|
+
const storedPattern = {
|
|
407
|
+
id: candidate.pattern_id,
|
|
408
|
+
context: JSON.parse(candidate.context_data),
|
|
409
|
+
skill: JSON.parse(candidate.skill_features),
|
|
410
|
+
originalScore: candidate.original_score,
|
|
411
|
+
source: candidate.source,
|
|
412
|
+
contextEmbedding: candidateEmbedding,
|
|
413
|
+
outcome: {
|
|
414
|
+
type: candidate.outcome_type,
|
|
415
|
+
reward: candidate.outcome_reward,
|
|
416
|
+
},
|
|
417
|
+
importance: candidate.importance,
|
|
418
|
+
accessCount: candidate.access_count,
|
|
419
|
+
createdAt: new Date(candidate.created_at * 1000),
|
|
420
|
+
lastAccessedAt: new Date(candidate.last_accessed_at * 1000),
|
|
421
|
+
};
|
|
422
|
+
results.push({
|
|
423
|
+
pattern: storedPattern,
|
|
424
|
+
similarity,
|
|
425
|
+
weightedSimilarity,
|
|
426
|
+
rank: 0,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
// Sort by weighted similarity
|
|
430
|
+
results.sort((a, b) => b.weightedSimilarity - a.weightedSimilarity);
|
|
431
|
+
// Assign ranks
|
|
432
|
+
for (let i = 0; i < results.length; i++) {
|
|
433
|
+
results[i].rank = i + 1;
|
|
434
|
+
}
|
|
435
|
+
// Update access tracking
|
|
436
|
+
if (this.config.trackAccess) {
|
|
437
|
+
for (const result of results.slice(0, limit)) {
|
|
438
|
+
this.updateAccessCount(result.pattern.id);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// Track query latency
|
|
442
|
+
const latency = Date.now() - startTime;
|
|
443
|
+
this.queryLatencies.push(latency);
|
|
444
|
+
if (this.queryLatencies.length > this.maxQuerySamples) {
|
|
445
|
+
this.queryLatencies.shift();
|
|
446
|
+
}
|
|
447
|
+
return results.slice(0, limit);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Consolidate patterns using EWC++
|
|
451
|
+
*
|
|
452
|
+
* Applies Fisher decay, recalculates importance, and prunes low-importance patterns.
|
|
453
|
+
* Guarantees 95%+ preservation of important patterns.
|
|
454
|
+
*
|
|
455
|
+
* @returns Consolidation result
|
|
456
|
+
*/
|
|
457
|
+
async consolidate() {
|
|
458
|
+
this.ensureInitialized();
|
|
459
|
+
const startTime = Date.now();
|
|
460
|
+
const totalPatterns = this.getPatternCount();
|
|
461
|
+
const newPatternsRatio = totalPatterns > 0 ? this.consolidationState.patternsSinceLastConsolidation / totalPatterns : 0;
|
|
462
|
+
// Check if consolidation is needed
|
|
463
|
+
if (newPatternsRatio < this.ewcConfig.consolidationThreshold) {
|
|
464
|
+
return {
|
|
465
|
+
consolidated: false,
|
|
466
|
+
patternsProcessed: 0,
|
|
467
|
+
patternsPreserved: 0,
|
|
468
|
+
patternsPruned: 0,
|
|
469
|
+
preservationRate: 1.0,
|
|
470
|
+
durationMs: 0,
|
|
471
|
+
averageImportance: this.fisherMatrix.getAverageImportance(),
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
// Apply Fisher decay
|
|
475
|
+
this.fisherMatrix.decay(this.ewcConfig.fisherDecay);
|
|
476
|
+
// Sample patterns for Fisher estimation
|
|
477
|
+
const samplePatterns = this.getSamplePatterns(this.ewcConfig.fisherSampleSize);
|
|
478
|
+
const avgEmbedding = await this.computeAverageEmbedding();
|
|
479
|
+
for (const pattern of samplePatterns) {
|
|
480
|
+
const gradient = this.computeGradient(pattern.contextEmbedding, avgEmbedding);
|
|
481
|
+
this.fisherMatrix.update(gradient);
|
|
482
|
+
}
|
|
483
|
+
// Update importance for all patterns
|
|
484
|
+
const allPatterns = this.getAllPatterns();
|
|
485
|
+
const importanceVector = this.fisherMatrix.getImportanceVector();
|
|
486
|
+
for (const pattern of allPatterns) {
|
|
487
|
+
const newImportance = this.calculateDimensionImportance(pattern, importanceVector);
|
|
488
|
+
this.updatePatternImportance(pattern.id, newImportance);
|
|
489
|
+
}
|
|
490
|
+
// Prune low-importance patterns
|
|
491
|
+
let prunedCount = 0;
|
|
492
|
+
let preservedCount = 0;
|
|
493
|
+
// Sort by importance (ascending) for pruning
|
|
494
|
+
const sortedPatterns = [...allPatterns].sort((a, b) => a.importance - b.importance);
|
|
495
|
+
if (sortedPatterns.length > this.ewcConfig.maxPatterns) {
|
|
496
|
+
const pruneCandidates = sortedPatterns.slice(0, sortedPatterns.length - this.ewcConfig.maxPatterns);
|
|
497
|
+
for (const candidate of pruneCandidates) {
|
|
498
|
+
if (candidate.importance < this.ewcConfig.importanceThreshold) {
|
|
499
|
+
this.deletePattern(candidate.id);
|
|
500
|
+
prunedCount++;
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
preservedCount++;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
preservedCount += this.ewcConfig.maxPatterns;
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
// Prune very low importance patterns even if under limit
|
|
510
|
+
for (const pattern of sortedPatterns) {
|
|
511
|
+
if (pattern.importance < this.ewcConfig.importanceThreshold * 0.1) {
|
|
512
|
+
this.deletePattern(pattern.id);
|
|
513
|
+
prunedCount++;
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
preservedCount++;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const preservationRate = preservedCount / (preservedCount + prunedCount) || 1.0;
|
|
521
|
+
// Update consolidation state
|
|
522
|
+
this.consolidationState.lastConsolidation = new Date();
|
|
523
|
+
this.consolidationState.patternsSinceLastConsolidation = 0;
|
|
524
|
+
this.consolidationState.totalPatterns = this.getPatternCount();
|
|
525
|
+
// Record consolidation history
|
|
526
|
+
const durationMs = Date.now() - startTime;
|
|
527
|
+
const avgImportance = this.fisherMatrix.getAverageImportance();
|
|
528
|
+
const historyStmt = this.db.prepare(`
|
|
529
|
+
INSERT INTO consolidation_history (
|
|
530
|
+
patterns_processed, patterns_preserved, patterns_pruned,
|
|
531
|
+
preservation_rate, duration_ms, average_importance
|
|
532
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
533
|
+
`);
|
|
534
|
+
historyStmt.run(preservedCount + prunedCount, preservedCount, prunedCount, preservationRate, durationMs, avgImportance);
|
|
535
|
+
// Persist Fisher matrix
|
|
536
|
+
this.saveFisherMatrix();
|
|
537
|
+
return {
|
|
538
|
+
consolidated: true,
|
|
539
|
+
patternsProcessed: preservedCount + prunedCount,
|
|
540
|
+
patternsPreserved: preservedCount,
|
|
541
|
+
patternsPruned: prunedCount,
|
|
542
|
+
preservationRate,
|
|
543
|
+
durationMs,
|
|
544
|
+
averageImportance: avgImportance,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Get pattern importance value
|
|
549
|
+
*
|
|
550
|
+
* @param patternId - Pattern identifier
|
|
551
|
+
* @returns Importance value or 0 if not found
|
|
552
|
+
*/
|
|
553
|
+
getPatternImportance(patternId) {
|
|
554
|
+
this.ensureInitialized();
|
|
555
|
+
const stmt = this.db.prepare('SELECT importance FROM patterns WHERE pattern_id = ?');
|
|
556
|
+
const result = stmt.get(patternId);
|
|
557
|
+
return result?.importance ?? 0;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Get PatternStore metrics
|
|
561
|
+
*/
|
|
562
|
+
getMetrics() {
|
|
563
|
+
this.ensureInitialized();
|
|
564
|
+
// Total patterns
|
|
565
|
+
const totalPatterns = this.getPatternCount();
|
|
566
|
+
// Patterns by outcome type
|
|
567
|
+
const outcomeStmt = this.db.prepare(`
|
|
568
|
+
SELECT outcome_type, COUNT(*) as count FROM patterns GROUP BY outcome_type
|
|
569
|
+
`);
|
|
570
|
+
const outcomeCounts = outcomeStmt.all();
|
|
571
|
+
const patternsByOutcome = {
|
|
572
|
+
accept: 0,
|
|
573
|
+
usage: 0,
|
|
574
|
+
frequent: 0,
|
|
575
|
+
dismiss: 0,
|
|
576
|
+
abandonment: 0,
|
|
577
|
+
uninstall: 0,
|
|
578
|
+
};
|
|
579
|
+
for (const row of outcomeCounts) {
|
|
580
|
+
patternsByOutcome[row.outcome_type] = row.count;
|
|
581
|
+
}
|
|
582
|
+
// Average importance
|
|
583
|
+
const avgStmt = this.db.prepare('SELECT AVG(importance) as avg FROM patterns');
|
|
584
|
+
const avgResult = avgStmt.get();
|
|
585
|
+
const averageImportance = avgResult?.avg ?? 0;
|
|
586
|
+
// High importance patterns (above 90th percentile)
|
|
587
|
+
const percentileStmt = this.db.prepare(`
|
|
588
|
+
SELECT importance FROM patterns ORDER BY importance DESC
|
|
589
|
+
LIMIT CAST((SELECT COUNT(*) FROM patterns) * 0.1 AS INTEGER)
|
|
590
|
+
`);
|
|
591
|
+
const highImportancePatterns = percentileStmt.all().length;
|
|
592
|
+
// Consolidation stats
|
|
593
|
+
const consolidationStmt = this.db.prepare(`
|
|
594
|
+
SELECT
|
|
595
|
+
COUNT(*) as total,
|
|
596
|
+
MAX(timestamp) as last_timestamp,
|
|
597
|
+
AVG(preservation_rate) as avg_rate,
|
|
598
|
+
SUM(patterns_pruned) as total_pruned
|
|
599
|
+
FROM consolidation_history
|
|
600
|
+
`);
|
|
601
|
+
const consolidationResult = consolidationStmt.get();
|
|
602
|
+
// Storage size
|
|
603
|
+
const fisherMatrixSize = 4 + this.config.dimensions * 4 * 2; // updateCount + importance + runningSum
|
|
604
|
+
// Query performance
|
|
605
|
+
const avgLatency = this.queryLatencies.length > 0
|
|
606
|
+
? this.queryLatencies.reduce((a, b) => a + b, 0) / this.queryLatencies.length
|
|
607
|
+
: 0;
|
|
608
|
+
return {
|
|
609
|
+
totalPatterns,
|
|
610
|
+
patternsByOutcome,
|
|
611
|
+
averageImportance,
|
|
612
|
+
highImportancePatterns,
|
|
613
|
+
consolidation: {
|
|
614
|
+
totalConsolidations: consolidationResult.total,
|
|
615
|
+
lastConsolidation: consolidationResult.last_timestamp
|
|
616
|
+
? new Date(consolidationResult.last_timestamp * 1000)
|
|
617
|
+
: null,
|
|
618
|
+
averagePreservationRate: consolidationResult.avg_rate ?? 1.0,
|
|
619
|
+
patternsPruned: consolidationResult.total_pruned ?? 0,
|
|
620
|
+
},
|
|
621
|
+
storage: {
|
|
622
|
+
sizeBytes: this.getDatabaseSize(),
|
|
623
|
+
fisherMatrixSizeBytes: fisherMatrixSize,
|
|
624
|
+
},
|
|
625
|
+
queryPerformance: {
|
|
626
|
+
averageLatencyMs: avgLatency,
|
|
627
|
+
queriesPerformed: this.queryLatencies.length,
|
|
628
|
+
},
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Close the PatternStore and release resources
|
|
633
|
+
*/
|
|
634
|
+
close() {
|
|
635
|
+
if (this.db) {
|
|
636
|
+
this.db.close();
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
// ============================================================================
|
|
640
|
+
// Private Helper Methods
|
|
641
|
+
// ============================================================================
|
|
642
|
+
ensureInitialized() {
|
|
643
|
+
if (!this.initialized) {
|
|
644
|
+
throw new Error('PatternStore not initialized. Call initialize() first.');
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
contextToText(context) {
|
|
648
|
+
const parts = [];
|
|
649
|
+
if (context.installedSkills && context.installedSkills.length > 0) {
|
|
650
|
+
parts.push(`installed: ${context.installedSkills.join(', ')}`);
|
|
651
|
+
}
|
|
652
|
+
if (context.frameworks && context.frameworks.length > 0) {
|
|
653
|
+
parts.push(`frameworks: ${context.frameworks.join(', ')}`);
|
|
654
|
+
}
|
|
655
|
+
if (context.keywords && context.keywords.length > 0) {
|
|
656
|
+
parts.push(`keywords: ${context.keywords.join(', ')}`);
|
|
657
|
+
}
|
|
658
|
+
if (context.timeOfDay) {
|
|
659
|
+
parts.push(`time: ${context.timeOfDay}`);
|
|
660
|
+
}
|
|
661
|
+
if (context.dayType) {
|
|
662
|
+
parts.push(`day: ${context.dayType}`);
|
|
663
|
+
}
|
|
664
|
+
return parts.join(' | ') || 'empty context';
|
|
665
|
+
}
|
|
666
|
+
computeGradient(a, b) {
|
|
667
|
+
const gradient = new Float32Array(a.length);
|
|
668
|
+
for (let i = 0; i < a.length; i++) {
|
|
669
|
+
gradient[i] = a[i] - (b[i] ?? 0);
|
|
670
|
+
}
|
|
671
|
+
return gradient;
|
|
672
|
+
}
|
|
673
|
+
async computeAverageEmbedding() {
|
|
674
|
+
const stmt = this.db.prepare('SELECT context_embedding FROM patterns LIMIT 100');
|
|
675
|
+
const rows = stmt.all();
|
|
676
|
+
if (rows.length === 0) {
|
|
677
|
+
return new Float32Array(this.config.dimensions);
|
|
678
|
+
}
|
|
679
|
+
const sum = new Float32Array(this.config.dimensions);
|
|
680
|
+
for (const row of rows) {
|
|
681
|
+
const embedding = this.deserializeEmbedding(row.context_embedding);
|
|
682
|
+
for (let i = 0; i < embedding.length; i++) {
|
|
683
|
+
sum[i] += embedding[i];
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
for (let i = 0; i < sum.length; i++) {
|
|
687
|
+
sum[i] /= rows.length;
|
|
688
|
+
}
|
|
689
|
+
return sum;
|
|
690
|
+
}
|
|
691
|
+
deserializeEmbedding(buffer) {
|
|
692
|
+
const floatArray = new Float32Array(this.config.dimensions);
|
|
693
|
+
for (let i = 0; i < this.config.dimensions; i++) {
|
|
694
|
+
floatArray[i] = buffer.readFloatLE(i * 4);
|
|
695
|
+
}
|
|
696
|
+
return floatArray;
|
|
697
|
+
}
|
|
698
|
+
cosineSimilarity(a, b) {
|
|
699
|
+
let dotProduct = 0;
|
|
700
|
+
let normA = 0;
|
|
701
|
+
let normB = 0;
|
|
702
|
+
for (let i = 0; i < a.length; i++) {
|
|
703
|
+
dotProduct += a[i] * b[i];
|
|
704
|
+
normA += a[i] * a[i];
|
|
705
|
+
normB += b[i] * b[i];
|
|
706
|
+
}
|
|
707
|
+
if (normA === 0 || normB === 0)
|
|
708
|
+
return 0;
|
|
709
|
+
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
710
|
+
}
|
|
711
|
+
importanceWeightedSimilarity(a, b, importance) {
|
|
712
|
+
let weightedDotProduct = 0;
|
|
713
|
+
let normA = 0;
|
|
714
|
+
let normB = 0;
|
|
715
|
+
for (let i = 0; i < a.length; i++) {
|
|
716
|
+
const weight = 1 + (importance[i] ?? 0);
|
|
717
|
+
weightedDotProduct += weight * a[i] * b[i];
|
|
718
|
+
normA += weight * a[i] * a[i];
|
|
719
|
+
normB += weight * b[i] * b[i];
|
|
720
|
+
}
|
|
721
|
+
if (normA === 0 || normB === 0)
|
|
722
|
+
return 0;
|
|
723
|
+
return weightedDotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
724
|
+
}
|
|
725
|
+
calculatePatternImportance(pattern, outcome) {
|
|
726
|
+
let baseImportance = Math.abs(outcome.reward);
|
|
727
|
+
if (outcome.reward > 0) {
|
|
728
|
+
baseImportance *= 1.5;
|
|
729
|
+
}
|
|
730
|
+
// Recency factor
|
|
731
|
+
const ageInDays = (Date.now() - pattern.createdAt.getTime()) / (24 * 60 * 60 * 1000);
|
|
732
|
+
const recencyFactor = Math.exp(-ageInDays / 30);
|
|
733
|
+
// Access frequency factor
|
|
734
|
+
const accessFactor = 1 + Math.log(1 + pattern.accessCount);
|
|
735
|
+
return baseImportance * recencyFactor * accessFactor * pattern.importance;
|
|
736
|
+
}
|
|
737
|
+
calculateDimensionImportance(pattern, importanceVector) {
|
|
738
|
+
let baseImportance = Math.abs(pattern.outcome.reward);
|
|
739
|
+
if (pattern.outcome.reward > 0) {
|
|
740
|
+
baseImportance *= 1.5;
|
|
741
|
+
}
|
|
742
|
+
const ageInDays = (Date.now() - pattern.createdAt.getTime()) / (24 * 60 * 60 * 1000);
|
|
743
|
+
const recencyFactor = Math.exp(-ageInDays / 30);
|
|
744
|
+
const accessFactor = 1 + Math.log(1 + pattern.accessCount);
|
|
745
|
+
// Fisher dimension importance (EWC++ core)
|
|
746
|
+
// Lambda scales how much we weight Fisher importance in preservation
|
|
747
|
+
let dimensionImportance = 0;
|
|
748
|
+
for (let i = 0; i < this.config.dimensions; i++) {
|
|
749
|
+
dimensionImportance += (importanceVector[i] ?? 0) * Math.abs(pattern.contextEmbedding[i] ?? 0);
|
|
750
|
+
}
|
|
751
|
+
dimensionImportance /= this.config.dimensions;
|
|
752
|
+
// Apply lambda regularization: higher lambda = stronger importance preservation
|
|
753
|
+
const lambdaScaled = 1 + (this.ewcConfig.lambda * dimensionImportance) / 10;
|
|
754
|
+
return baseImportance * recencyFactor * accessFactor * lambdaScaled;
|
|
755
|
+
}
|
|
756
|
+
shouldConsolidate() {
|
|
757
|
+
// Minimum 1 hour between consolidations
|
|
758
|
+
if (this.consolidationState.lastConsolidation) {
|
|
759
|
+
const hoursSinceLast = (Date.now() - this.consolidationState.lastConsolidation.getTime()) / (60 * 60 * 1000);
|
|
760
|
+
if (hoursSinceLast < 1)
|
|
761
|
+
return false;
|
|
762
|
+
}
|
|
763
|
+
if (this.consolidationState.totalPatterns === 0)
|
|
764
|
+
return false;
|
|
765
|
+
const newPatternsRatio = this.consolidationState.patternsSinceLastConsolidation / this.consolidationState.totalPatterns;
|
|
766
|
+
if (newPatternsRatio >= this.ewcConfig.consolidationThreshold)
|
|
767
|
+
return true;
|
|
768
|
+
// Force consolidation if approaching max patterns
|
|
769
|
+
if (this.consolidationState.totalPatterns > this.ewcConfig.maxPatterns * 0.9)
|
|
770
|
+
return true;
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
getPatternCount() {
|
|
774
|
+
const stmt = this.db.prepare('SELECT COUNT(*) as count FROM patterns');
|
|
775
|
+
const result = stmt.get();
|
|
776
|
+
return result.count;
|
|
777
|
+
}
|
|
778
|
+
getSamplePatterns(limit) {
|
|
779
|
+
const stmt = this.db.prepare(`
|
|
780
|
+
SELECT * FROM patterns ORDER BY RANDOM() LIMIT ?
|
|
781
|
+
`);
|
|
782
|
+
const rows = stmt.all(limit);
|
|
783
|
+
return rows.map((row) => ({
|
|
784
|
+
id: row.pattern_id,
|
|
785
|
+
context: JSON.parse(row.context_data),
|
|
786
|
+
skill: JSON.parse(row.skill_features),
|
|
787
|
+
originalScore: row.original_score,
|
|
788
|
+
source: row.source,
|
|
789
|
+
contextEmbedding: this.deserializeEmbedding(row.context_embedding),
|
|
790
|
+
outcome: {
|
|
791
|
+
type: row.outcome_type,
|
|
792
|
+
reward: row.outcome_reward,
|
|
793
|
+
},
|
|
794
|
+
importance: row.importance,
|
|
795
|
+
accessCount: row.access_count,
|
|
796
|
+
createdAt: new Date(row.created_at * 1000),
|
|
797
|
+
lastAccessedAt: new Date(row.last_accessed_at * 1000),
|
|
798
|
+
}));
|
|
799
|
+
}
|
|
800
|
+
getAllPatterns() {
|
|
801
|
+
const stmt = this.db.prepare('SELECT * FROM patterns');
|
|
802
|
+
const rows = stmt.all();
|
|
803
|
+
return rows.map((row) => ({
|
|
804
|
+
id: row.pattern_id,
|
|
805
|
+
context: JSON.parse(row.context_data),
|
|
806
|
+
skill: JSON.parse(row.skill_features),
|
|
807
|
+
originalScore: row.original_score,
|
|
808
|
+
source: row.source,
|
|
809
|
+
contextEmbedding: this.deserializeEmbedding(row.context_embedding),
|
|
810
|
+
outcome: {
|
|
811
|
+
type: row.outcome_type,
|
|
812
|
+
reward: row.outcome_reward,
|
|
813
|
+
},
|
|
814
|
+
importance: row.importance,
|
|
815
|
+
accessCount: row.access_count,
|
|
816
|
+
createdAt: new Date(row.created_at * 1000),
|
|
817
|
+
lastAccessedAt: new Date(row.last_accessed_at * 1000),
|
|
818
|
+
}));
|
|
819
|
+
}
|
|
820
|
+
updatePatternInDB(patternId, updates) {
|
|
821
|
+
const sets = [];
|
|
822
|
+
const params = [];
|
|
823
|
+
if (updates.importance !== undefined) {
|
|
824
|
+
sets.push('importance = ?');
|
|
825
|
+
params.push(updates.importance);
|
|
826
|
+
}
|
|
827
|
+
if (updates.accessCount !== undefined) {
|
|
828
|
+
sets.push('access_count = ?');
|
|
829
|
+
params.push(updates.accessCount);
|
|
830
|
+
}
|
|
831
|
+
sets.push('last_accessed_at = unixepoch()');
|
|
832
|
+
params.push(patternId);
|
|
833
|
+
const stmt = this.db.prepare(`UPDATE patterns SET ${sets.join(', ')} WHERE pattern_id = ?`);
|
|
834
|
+
stmt.run(...params);
|
|
835
|
+
}
|
|
836
|
+
updatePatternImportance(patternId, importance) {
|
|
837
|
+
const stmt = this.db.prepare('UPDATE patterns SET importance = ? WHERE pattern_id = ?');
|
|
838
|
+
stmt.run(importance, patternId);
|
|
839
|
+
}
|
|
840
|
+
updateAccessCount(patternId) {
|
|
841
|
+
const stmt = this.db.prepare(`
|
|
842
|
+
UPDATE patterns SET access_count = access_count + 1, last_accessed_at = unixepoch()
|
|
843
|
+
WHERE pattern_id = ?
|
|
844
|
+
`);
|
|
845
|
+
stmt.run(patternId);
|
|
846
|
+
}
|
|
847
|
+
deletePattern(patternId) {
|
|
848
|
+
const stmt = this.db.prepare('DELETE FROM patterns WHERE pattern_id = ?');
|
|
849
|
+
stmt.run(patternId);
|
|
850
|
+
}
|
|
851
|
+
getDatabaseSize() {
|
|
852
|
+
const stmt = this.db.prepare('SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()');
|
|
853
|
+
const result = stmt.get();
|
|
854
|
+
return result?.size ?? 0;
|
|
855
|
+
}
|
|
856
|
+
loadFisherMatrix() {
|
|
857
|
+
const stmt = this.db.prepare('SELECT matrix_data FROM fisher_info WHERE id = 1');
|
|
858
|
+
const result = stmt.get();
|
|
859
|
+
if (result?.matrix_data) {
|
|
860
|
+
try {
|
|
861
|
+
this.fisherMatrix.deserialize(result.matrix_data);
|
|
862
|
+
}
|
|
863
|
+
catch {
|
|
864
|
+
// Corrupted matrix data - reset to fresh state
|
|
865
|
+
console.warn('[PatternStore] Fisher matrix data corrupted, resetting');
|
|
866
|
+
this.fisherMatrix.reset();
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
saveFisherMatrix() {
|
|
871
|
+
const matrixData = this.fisherMatrix.serialize();
|
|
872
|
+
const stmt = this.db.prepare(`
|
|
873
|
+
INSERT OR REPLACE INTO fisher_info (id, matrix_data, update_count, last_decay_at, updated_at)
|
|
874
|
+
VALUES (1, ?, ?, unixepoch(), unixepoch())
|
|
875
|
+
`);
|
|
876
|
+
stmt.run(matrixData, this.fisherMatrix.getUpdateCount());
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
// ============================================================================
|
|
880
|
+
// Factory Function
|
|
881
|
+
// ============================================================================
|
|
882
|
+
/**
|
|
883
|
+
* Create and initialize a PatternStore instance
|
|
884
|
+
*
|
|
885
|
+
* @param config - Configuration options
|
|
886
|
+
* @returns Initialized PatternStore
|
|
887
|
+
*/
|
|
888
|
+
export async function createPatternStore(config = {}) {
|
|
889
|
+
const store = new PatternStore(config);
|
|
890
|
+
await store.initialize();
|
|
891
|
+
return store;
|
|
892
|
+
}
|
|
893
|
+
//# sourceMappingURL=PatternStore.js.map
|