@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.
Files changed (204) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/src/analysis/types.d.ts +2 -0
  3. package/dist/src/analysis/types.d.ts.map +1 -1
  4. package/dist/src/analysis/types.js +13 -1
  5. package/dist/src/analysis/types.js.map +1 -1
  6. package/dist/src/analytics/AnalyticsRepository.d.ts +4 -0
  7. package/dist/src/analytics/AnalyticsRepository.d.ts.map +1 -1
  8. package/dist/src/analytics/AnalyticsRepository.js +26 -44
  9. package/dist/src/analytics/AnalyticsRepository.js.map +1 -1
  10. package/dist/src/analytics/schema.d.ts +1 -1
  11. package/dist/src/analytics/schema.d.ts.map +1 -1
  12. package/dist/src/analytics/schema.js +68 -0
  13. package/dist/src/analytics/schema.js.map +1 -1
  14. package/dist/src/api/client.d.ts +33 -29
  15. package/dist/src/api/client.d.ts.map +1 -1
  16. package/dist/src/api/client.js +15 -10
  17. package/dist/src/api/client.js.map +1 -1
  18. package/dist/src/billing/BillingService.d.ts +139 -0
  19. package/dist/src/billing/BillingService.d.ts.map +1 -0
  20. package/dist/src/billing/BillingService.js +393 -0
  21. package/dist/src/billing/BillingService.js.map +1 -0
  22. package/dist/src/billing/GDPRComplianceService.d.ts +176 -0
  23. package/dist/src/billing/GDPRComplianceService.d.ts.map +1 -0
  24. package/dist/src/billing/GDPRComplianceService.js +361 -0
  25. package/dist/src/billing/GDPRComplianceService.js.map +1 -0
  26. package/dist/src/billing/StripeClient.d.ts +177 -0
  27. package/dist/src/billing/StripeClient.d.ts.map +1 -0
  28. package/dist/src/billing/StripeClient.js +462 -0
  29. package/dist/src/billing/StripeClient.js.map +1 -0
  30. package/dist/src/billing/StripeReconciliationJob.d.ts +95 -0
  31. package/dist/src/billing/StripeReconciliationJob.d.ts.map +1 -0
  32. package/dist/src/billing/StripeReconciliationJob.js +405 -0
  33. package/dist/src/billing/StripeReconciliationJob.js.map +1 -0
  34. package/dist/src/billing/StripeWebhookHandler.d.ts +92 -0
  35. package/dist/src/billing/StripeWebhookHandler.d.ts.map +1 -0
  36. package/dist/src/billing/StripeWebhookHandler.js +409 -0
  37. package/dist/src/billing/StripeWebhookHandler.js.map +1 -0
  38. package/dist/src/billing/index.d.ts +18 -0
  39. package/dist/src/billing/index.d.ts.map +1 -0
  40. package/dist/src/billing/index.js +19 -0
  41. package/dist/src/billing/index.js.map +1 -0
  42. package/dist/src/billing/types.d.ts +266 -0
  43. package/dist/src/billing/types.d.ts.map +1 -0
  44. package/dist/src/billing/types.js +23 -0
  45. package/dist/src/billing/types.js.map +1 -0
  46. package/dist/src/embeddings/hnsw-store.d.ts +568 -0
  47. package/dist/src/embeddings/hnsw-store.d.ts.map +1 -0
  48. package/dist/src/embeddings/hnsw-store.js +805 -0
  49. package/dist/src/embeddings/hnsw-store.js.map +1 -0
  50. package/dist/src/embeddings/index.d.ts +2 -0
  51. package/dist/src/embeddings/index.d.ts.map +1 -1
  52. package/dist/src/embeddings/index.js +2 -0
  53. package/dist/src/embeddings/index.js.map +1 -1
  54. package/dist/src/index.d.ts +1 -0
  55. package/dist/src/index.d.ts.map +1 -1
  56. package/dist/src/index.js +2 -0
  57. package/dist/src/index.js.map +1 -1
  58. package/dist/src/learning/PatternStore.d.ts +457 -0
  59. package/dist/src/learning/PatternStore.d.ts.map +1 -0
  60. package/dist/src/learning/PatternStore.js +893 -0
  61. package/dist/src/learning/PatternStore.js.map +1 -0
  62. package/dist/src/learning/ReasoningBankIntegration.d.ts +403 -0
  63. package/dist/src/learning/ReasoningBankIntegration.d.ts.map +1 -0
  64. package/dist/src/learning/ReasoningBankIntegration.js +627 -0
  65. package/dist/src/learning/ReasoningBankIntegration.js.map +1 -0
  66. package/dist/src/learning/index.d.ts +15 -0
  67. package/dist/src/learning/index.d.ts.map +1 -0
  68. package/dist/src/learning/index.js +15 -0
  69. package/dist/src/learning/index.js.map +1 -0
  70. package/dist/src/routing/SONARouter.d.ts +154 -0
  71. package/dist/src/routing/SONARouter.d.ts.map +1 -0
  72. package/dist/src/routing/SONARouter.js +679 -0
  73. package/dist/src/routing/SONARouter.js.map +1 -0
  74. package/dist/src/routing/index.d.ts +9 -0
  75. package/dist/src/routing/index.d.ts.map +1 -0
  76. package/dist/src/routing/index.js +10 -0
  77. package/dist/src/routing/index.js.map +1 -0
  78. package/dist/src/routing/types.d.ts +331 -0
  79. package/dist/src/routing/types.d.ts.map +1 -0
  80. package/dist/src/routing/types.js +203 -0
  81. package/dist/src/routing/types.js.map +1 -0
  82. package/dist/src/scripts/__tests__/scan-imported-skills.test.js +5 -0
  83. package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
  84. package/dist/src/security/SkillSandbox.d.ts +156 -0
  85. package/dist/src/security/SkillSandbox.d.ts.map +1 -0
  86. package/dist/src/security/SkillSandbox.js +303 -0
  87. package/dist/src/security/SkillSandbox.js.map +1 -0
  88. package/dist/src/security/index.d.ts +3 -1
  89. package/dist/src/security/index.d.ts.map +1 -1
  90. package/dist/src/security/index.js +5 -1
  91. package/dist/src/security/index.js.map +1 -1
  92. package/dist/src/security/rate-limiter/presets.d.ts +12 -0
  93. package/dist/src/security/rate-limiter/presets.d.ts.map +1 -1
  94. package/dist/src/security/rate-limiter/presets.js +12 -0
  95. package/dist/src/security/rate-limiter/presets.js.map +1 -1
  96. package/dist/src/security/sanitization.d.ts +85 -0
  97. package/dist/src/security/sanitization.d.ts.map +1 -1
  98. package/dist/src/security/sanitization.js +133 -0
  99. package/dist/src/security/sanitization.js.map +1 -1
  100. package/dist/src/security/scanner/SecurityScanner.d.ts +23 -0
  101. package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
  102. package/dist/src/security/scanner/SecurityScanner.js +232 -28
  103. package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
  104. package/dist/src/security/scanner/patterns.d.ts +13 -0
  105. package/dist/src/security/scanner/patterns.d.ts.map +1 -1
  106. package/dist/src/security/scanner/patterns.js +51 -0
  107. package/dist/src/security/scanner/patterns.js.map +1 -1
  108. package/dist/src/security/scanner/types.d.ts +13 -1
  109. package/dist/src/security/scanner/types.d.ts.map +1 -1
  110. package/dist/src/security/scanner/weights.d.ts.map +1 -1
  111. package/dist/src/security/scanner/weights.js +1 -0
  112. package/dist/src/security/scanner/weights.js.map +1 -1
  113. package/dist/src/session/SessionManager.d.ts +7 -0
  114. package/dist/src/session/SessionManager.d.ts.map +1 -1
  115. package/dist/src/session/SessionManager.js +117 -10
  116. package/dist/src/session/SessionManager.js.map +1 -1
  117. package/dist/src/sync/SyncEngine.d.ts.map +1 -1
  118. package/dist/src/sync/SyncEngine.js +52 -32
  119. package/dist/src/sync/SyncEngine.js.map +1 -1
  120. package/dist/src/testing/MultiLLMProvider.d.ts +374 -0
  121. package/dist/src/testing/MultiLLMProvider.d.ts.map +1 -0
  122. package/dist/src/testing/MultiLLMProvider.js +720 -0
  123. package/dist/src/testing/MultiLLMProvider.js.map +1 -0
  124. package/dist/src/testing/index.d.ts +8 -0
  125. package/dist/src/testing/index.d.ts.map +1 -0
  126. package/dist/src/testing/index.js +9 -0
  127. package/dist/src/testing/index.js.map +1 -0
  128. package/dist/src/types.d.ts +3 -0
  129. package/dist/src/types.d.ts.map +1 -1
  130. package/dist/tests/SecurityScanner.test.js +337 -1
  131. package/dist/tests/SecurityScanner.test.js.map +1 -1
  132. package/dist/tests/billing/BillingService.test.d.ts +7 -0
  133. package/dist/tests/billing/BillingService.test.d.ts.map +1 -0
  134. package/dist/tests/billing/BillingService.test.js +168 -0
  135. package/dist/tests/billing/BillingService.test.js.map +1 -0
  136. package/dist/tests/billing/GDPRCompliance.test.d.ts +7 -0
  137. package/dist/tests/billing/GDPRCompliance.test.d.ts.map +1 -0
  138. package/dist/tests/billing/GDPRCompliance.test.js +195 -0
  139. package/dist/tests/billing/GDPRCompliance.test.js.map +1 -0
  140. package/dist/tests/billing/StripeReconciliation.test.d.ts +7 -0
  141. package/dist/tests/billing/StripeReconciliation.test.d.ts.map +1 -0
  142. package/dist/tests/billing/StripeReconciliation.test.js +266 -0
  143. package/dist/tests/billing/StripeReconciliation.test.js.map +1 -0
  144. package/dist/tests/billing/stripe-validators.test.d.ts +7 -0
  145. package/dist/tests/billing/stripe-validators.test.d.ts.map +1 -0
  146. package/dist/tests/billing/stripe-validators.test.js +107 -0
  147. package/dist/tests/billing/stripe-validators.test.js.map +1 -0
  148. package/dist/tests/embeddings/hnsw-store.test.d.ts +7 -0
  149. package/dist/tests/embeddings/hnsw-store.test.d.ts.map +1 -0
  150. package/dist/tests/embeddings/hnsw-store.test.js +295 -0
  151. package/dist/tests/embeddings/hnsw-store.test.js.map +1 -0
  152. package/dist/tests/integration/neural/e2e-learning.test.d.ts +17 -0
  153. package/dist/tests/integration/neural/e2e-learning.test.d.ts.map +1 -0
  154. package/dist/tests/integration/neural/e2e-learning.test.js +238 -0
  155. package/dist/tests/integration/neural/e2e-learning.test.js.map +1 -0
  156. package/dist/tests/integration/neural/helpers.d.ts +132 -0
  157. package/dist/tests/integration/neural/helpers.d.ts.map +1 -0
  158. package/dist/tests/integration/neural/helpers.js +287 -0
  159. package/dist/tests/integration/neural/helpers.js.map +1 -0
  160. package/dist/tests/integration/neural/personalization.test.d.ts +21 -0
  161. package/dist/tests/integration/neural/personalization.test.d.ts.map +1 -0
  162. package/dist/tests/integration/neural/personalization.test.js +304 -0
  163. package/dist/tests/integration/neural/personalization.test.js.map +1 -0
  164. package/dist/tests/integration/neural/preference-learner.test.d.ts +23 -0
  165. package/dist/tests/integration/neural/preference-learner.test.d.ts.map +1 -0
  166. package/dist/tests/integration/neural/preference-learner.test.js +289 -0
  167. package/dist/tests/integration/neural/preference-learner.test.js.map +1 -0
  168. package/dist/tests/integration/neural/privacy.test.d.ts +19 -0
  169. package/dist/tests/integration/neural/privacy.test.d.ts.map +1 -0
  170. package/dist/tests/integration/neural/privacy.test.js +249 -0
  171. package/dist/tests/integration/neural/privacy.test.js.map +1 -0
  172. package/dist/tests/integration/neural/setup.d.ts +175 -0
  173. package/dist/tests/integration/neural/setup.d.ts.map +1 -0
  174. package/dist/tests/integration/neural/setup.js +487 -0
  175. package/dist/tests/integration/neural/setup.js.map +1 -0
  176. package/dist/tests/integration/neural/signal-collection.test.d.ts +21 -0
  177. package/dist/tests/integration/neural/signal-collection.test.d.ts.map +1 -0
  178. package/dist/tests/integration/neural/signal-collection.test.js +232 -0
  179. package/dist/tests/integration/neural/signal-collection.test.js.map +1 -0
  180. package/dist/tests/learning/PatternStore.test.d.ts +8 -0
  181. package/dist/tests/learning/PatternStore.test.d.ts.map +1 -0
  182. package/dist/tests/learning/PatternStore.test.js +589 -0
  183. package/dist/tests/learning/PatternStore.test.js.map +1 -0
  184. package/dist/tests/learning/ReasoningBankIntegration.test.d.ts +8 -0
  185. package/dist/tests/learning/ReasoningBankIntegration.test.d.ts.map +1 -0
  186. package/dist/tests/learning/ReasoningBankIntegration.test.js +269 -0
  187. package/dist/tests/learning/ReasoningBankIntegration.test.js.map +1 -0
  188. package/dist/tests/routing/SONARouter.test.d.ts +8 -0
  189. package/dist/tests/routing/SONARouter.test.d.ts.map +1 -0
  190. package/dist/tests/routing/SONARouter.test.js +400 -0
  191. package/dist/tests/routing/SONARouter.test.js.map +1 -0
  192. package/dist/tests/security/ContinuousSecurity.test.js +10 -12
  193. package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
  194. package/dist/tests/security/SkillSandbox.test.d.ts +8 -0
  195. package/dist/tests/security/SkillSandbox.test.d.ts.map +1 -0
  196. package/dist/tests/security/SkillSandbox.test.js +321 -0
  197. package/dist/tests/security/SkillSandbox.test.js.map +1 -0
  198. package/dist/tests/sync/SyncEngine.test.js +4 -2
  199. package/dist/tests/sync/SyncEngine.test.js.map +1 -1
  200. package/dist/tests/testing/MultiLLMProvider.test.d.ts +14 -0
  201. package/dist/tests/testing/MultiLLMProvider.test.d.ts.map +1 -0
  202. package/dist/tests/testing/MultiLLMProvider.test.js +438 -0
  203. package/dist/tests/testing/MultiLLMProvider.test.js.map +1 -0
  204. package/package.json +16 -3
@@ -0,0 +1,295 @@
1
+ /**
2
+ * SMI-1519: HNSW Embedding Store Tests
3
+ *
4
+ * Tests for the hybrid HNSW + SQLite embedding storage.
5
+ */
6
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
7
+ import { existsSync, unlinkSync, mkdirSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { tmpdir } from 'node:os';
10
+ import { HNSWEmbeddingStore, DEFAULT_HNSW_CONFIG, HNSW_PRESETS, createHNSWStore, isHNSWAvailable, } from '../../src/embeddings/hnsw-store.js';
11
+ describe('HNSWEmbeddingStore', () => {
12
+ let store;
13
+ let testDbPath;
14
+ beforeEach(() => {
15
+ // Create unique test database path
16
+ const testDir = join(tmpdir(), 'skillsmith-hnsw-tests');
17
+ if (!existsSync(testDir)) {
18
+ mkdirSync(testDir, { recursive: true });
19
+ }
20
+ testDbPath = join(testDir, `test-${Date.now()}.db`);
21
+ });
22
+ afterEach(() => {
23
+ if (store) {
24
+ store.close();
25
+ }
26
+ // Clean up test database
27
+ if (existsSync(testDbPath)) {
28
+ try {
29
+ unlinkSync(testDbPath);
30
+ }
31
+ catch {
32
+ // Ignore cleanup errors
33
+ }
34
+ }
35
+ });
36
+ describe('constructor', () => {
37
+ it('should create store with default config', () => {
38
+ store = new HNSWEmbeddingStore({ dbPath: testDbPath });
39
+ expect(store).toBeInstanceOf(HNSWEmbeddingStore);
40
+ });
41
+ it('should create store with custom config', () => {
42
+ store = new HNSWEmbeddingStore({
43
+ dbPath: testDbPath,
44
+ hnswConfig: {
45
+ m: 32,
46
+ efConstruction: 400,
47
+ efSearch: 150,
48
+ dimensions: 384,
49
+ },
50
+ maxElements: 50000,
51
+ });
52
+ expect(store).toBeInstanceOf(HNSWEmbeddingStore);
53
+ });
54
+ it('should use brute-force fallback when useHNSW is false', () => {
55
+ store = new HNSWEmbeddingStore({
56
+ dbPath: testDbPath,
57
+ useHNSW: false,
58
+ });
59
+ expect(store.isUsingFallback()).toBe(true);
60
+ });
61
+ });
62
+ describe('storeEmbedding', () => {
63
+ beforeEach(() => {
64
+ store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
65
+ });
66
+ it('should store embedding successfully', () => {
67
+ const embedding = new Float32Array(384).fill(0.1);
68
+ store.storeEmbedding('skill-1', embedding, 'Test skill description');
69
+ const retrieved = store.getEmbedding('skill-1');
70
+ expect(retrieved).not.toBeNull();
71
+ expect(retrieved.length).toBe(384);
72
+ });
73
+ it('should reject embedding with wrong dimensions', () => {
74
+ const wrongDimEmbedding = new Float32Array(768).fill(0.1);
75
+ expect(() => {
76
+ store.storeEmbedding('skill-1', wrongDimEmbedding, 'Test');
77
+ }).toThrow('Embedding dimension mismatch');
78
+ });
79
+ it('should update existing embedding on re-store', () => {
80
+ const embedding1 = new Float32Array(384).fill(0.1);
81
+ const embedding2 = new Float32Array(384).fill(0.2);
82
+ store.storeEmbedding('skill-1', embedding1, 'Original text');
83
+ store.storeEmbedding('skill-1', embedding2, 'Updated text');
84
+ const retrieved = store.getEmbedding('skill-1');
85
+ expect(retrieved).not.toBeNull();
86
+ expect(retrieved[0]).toBeCloseTo(0.2);
87
+ });
88
+ });
89
+ describe('getEmbedding', () => {
90
+ beforeEach(() => {
91
+ store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
92
+ });
93
+ it('should return null for non-existent skill', () => {
94
+ const result = store.getEmbedding('non-existent');
95
+ expect(result).toBeNull();
96
+ });
97
+ it('should return stored embedding', () => {
98
+ const embedding = new Float32Array(384);
99
+ for (let i = 0; i < 384; i++) {
100
+ embedding[i] = i / 384;
101
+ }
102
+ store.storeEmbedding('skill-1', embedding, 'Test');
103
+ const retrieved = store.getEmbedding('skill-1');
104
+ expect(retrieved).not.toBeNull();
105
+ expect(retrieved[0]).toBeCloseTo(0 / 384);
106
+ expect(retrieved[100]).toBeCloseTo(100 / 384);
107
+ });
108
+ });
109
+ describe('getAllEmbeddings', () => {
110
+ beforeEach(() => {
111
+ store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
112
+ });
113
+ it('should return empty map for empty store', () => {
114
+ const all = store.getAllEmbeddings();
115
+ expect(all.size).toBe(0);
116
+ });
117
+ it('should return all stored embeddings', () => {
118
+ store.storeEmbedding('skill-1', new Float32Array(384).fill(0.1), 'Skill 1');
119
+ store.storeEmbedding('skill-2', new Float32Array(384).fill(0.2), 'Skill 2');
120
+ store.storeEmbedding('skill-3', new Float32Array(384).fill(0.3), 'Skill 3');
121
+ const all = store.getAllEmbeddings();
122
+ expect(all.size).toBe(3);
123
+ expect(all.has('skill-1')).toBe(true);
124
+ expect(all.has('skill-2')).toBe(true);
125
+ expect(all.has('skill-3')).toBe(true);
126
+ });
127
+ });
128
+ describe('findSimilar', () => {
129
+ beforeEach(() => {
130
+ store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
131
+ });
132
+ it('should return empty array for empty store', () => {
133
+ const query = new Float32Array(384).fill(0.1);
134
+ const results = store.findSimilar(query, 10);
135
+ expect(results).toEqual([]);
136
+ });
137
+ it('should find similar embeddings (brute-force fallback)', () => {
138
+ // Create embeddings with known similarities
139
+ const embedding1 = new Float32Array(384).fill(0.1);
140
+ const embedding2 = new Float32Array(384).fill(0.2);
141
+ const embedding3 = new Float32Array(384).fill(0.9);
142
+ store.storeEmbedding('similar-high', embedding3, 'High similarity');
143
+ store.storeEmbedding('similar-low', embedding1, 'Low similarity');
144
+ store.storeEmbedding('similar-mid', embedding2, 'Mid similarity');
145
+ // Query with vector close to embedding3
146
+ const query = new Float32Array(384).fill(0.85);
147
+ const results = store.findSimilar(query, 3);
148
+ expect(results.length).toBe(3);
149
+ // Highest similarity should be first
150
+ expect(results[0].skillId).toBe('similar-high');
151
+ expect(results[0].score).toBeGreaterThan(results[1].score);
152
+ });
153
+ it('should respect topK limit', () => {
154
+ for (let i = 0; i < 20; i++) {
155
+ store.storeEmbedding(`skill-${i}`, new Float32Array(384).fill(i / 20), `Skill ${i}`);
156
+ }
157
+ const query = new Float32Array(384).fill(0.5);
158
+ const results = store.findSimilar(query, 5);
159
+ expect(results.length).toBe(5);
160
+ });
161
+ it('should reject query with wrong dimensions', () => {
162
+ const wrongDimQuery = new Float32Array(768).fill(0.1);
163
+ expect(() => {
164
+ store.findSimilar(wrongDimQuery, 10);
165
+ }).toThrow('Query dimension mismatch');
166
+ });
167
+ });
168
+ describe('cosineSimilarity', () => {
169
+ beforeEach(() => {
170
+ store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
171
+ });
172
+ it('should return 1 for identical normalized vectors', () => {
173
+ const v = new Float32Array(384);
174
+ for (let i = 0; i < 384; i++) {
175
+ v[i] = 1 / Math.sqrt(384);
176
+ }
177
+ const similarity = store.cosineSimilarity(v, v);
178
+ expect(similarity).toBeCloseTo(1.0);
179
+ });
180
+ it('should return 0 for orthogonal vectors', () => {
181
+ const v1 = new Float32Array(4).fill(0);
182
+ const v2 = new Float32Array(4).fill(0);
183
+ v1[0] = 1;
184
+ v2[1] = 1;
185
+ const similarity = store.cosineSimilarity(v1, v2);
186
+ expect(similarity).toBeCloseTo(0);
187
+ });
188
+ it('should throw for dimension mismatch', () => {
189
+ const v1 = new Float32Array(384).fill(0.1);
190
+ const v2 = new Float32Array(768).fill(0.1);
191
+ expect(() => store.cosineSimilarity(v1, v2)).toThrow('Embedding dimension mismatch');
192
+ });
193
+ });
194
+ describe('batchInsert', () => {
195
+ beforeEach(() => {
196
+ store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
197
+ });
198
+ it('should batch insert multiple embeddings', () => {
199
+ const embeddings = [
200
+ { skillId: 'batch-1', embedding: new Float32Array(384).fill(0.1), text: 'Batch 1' },
201
+ { skillId: 'batch-2', embedding: new Float32Array(384).fill(0.2), text: 'Batch 2' },
202
+ { skillId: 'batch-3', embedding: new Float32Array(384).fill(0.3), text: 'Batch 3' },
203
+ ];
204
+ const result = store.batchInsert(embeddings);
205
+ expect(result.inserted).toBe(3);
206
+ expect(result.updated).toBe(0);
207
+ expect(result.failed).toBe(0);
208
+ expect(result.durationMs).toBeGreaterThanOrEqual(0);
209
+ // Verify all were stored
210
+ const all = store.getAllEmbeddings();
211
+ expect(all.size).toBe(3);
212
+ });
213
+ it('should report failed insertions', () => {
214
+ const embeddings = [
215
+ { skillId: 'valid', embedding: new Float32Array(384).fill(0.1), text: 'Valid' },
216
+ { skillId: 'invalid', embedding: new Float32Array(768).fill(0.1), text: 'Invalid dim' },
217
+ ];
218
+ const result = store.batchInsert(embeddings);
219
+ expect(result.inserted).toBe(1);
220
+ expect(result.failed).toBe(1);
221
+ expect(result.errors.length).toBe(1);
222
+ expect(result.errors[0].skillId).toBe('invalid');
223
+ });
224
+ });
225
+ describe('removeEmbedding', () => {
226
+ beforeEach(() => {
227
+ store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
228
+ });
229
+ it('should remove existing embedding', () => {
230
+ store.storeEmbedding('to-remove', new Float32Array(384).fill(0.1), 'To remove');
231
+ expect(store.getEmbedding('to-remove')).not.toBeNull();
232
+ const removed = store.removeEmbedding('to-remove');
233
+ expect(removed).toBe(true);
234
+ expect(store.getEmbedding('to-remove')).toBeNull();
235
+ });
236
+ it('should return false for non-existent embedding', () => {
237
+ const removed = store.removeEmbedding('non-existent');
238
+ expect(removed).toBe(false);
239
+ });
240
+ });
241
+ describe('getStats', () => {
242
+ beforeEach(() => {
243
+ store = new HNSWEmbeddingStore({ dbPath: testDbPath, useHNSW: false });
244
+ });
245
+ it('should return correct stats for empty store', () => {
246
+ const stats = store.getStats();
247
+ expect(stats.vectorCount).toBe(0);
248
+ expect(stats.utilizationPercent).toBe(0);
249
+ expect(stats.dimensions).toBe(384);
250
+ });
251
+ it('should return correct stats after insertions', () => {
252
+ store.storeEmbedding('s1', new Float32Array(384).fill(0.1), 'S1');
253
+ store.storeEmbedding('s2', new Float32Array(384).fill(0.2), 'S2');
254
+ const stats = store.getStats();
255
+ expect(stats.vectorCount).toBe(2);
256
+ expect(stats.isHNSWEnabled).toBe(false); // useHNSW: false
257
+ });
258
+ });
259
+ describe('factory functions', () => {
260
+ it('createHNSWStore should create with preset config', () => {
261
+ store = createHNSWStore('large', { dbPath: testDbPath });
262
+ const stats = store.getStats();
263
+ expect(stats.m).toBe(HNSW_PRESETS.large.m);
264
+ expect(stats.efConstruction).toBe(HNSW_PRESETS.large.efConstruction);
265
+ });
266
+ });
267
+ describe('isHNSWAvailable', () => {
268
+ it('should return boolean indicating availability', async () => {
269
+ const available = await isHNSWAvailable();
270
+ expect(typeof available).toBe('boolean');
271
+ });
272
+ });
273
+ describe('DEFAULT_HNSW_CONFIG', () => {
274
+ it('should have expected default values', () => {
275
+ expect(DEFAULT_HNSW_CONFIG.m).toBe(16);
276
+ expect(DEFAULT_HNSW_CONFIG.efConstruction).toBe(200);
277
+ expect(DEFAULT_HNSW_CONFIG.efSearch).toBe(100);
278
+ expect(DEFAULT_HNSW_CONFIG.dimensions).toBe(384);
279
+ });
280
+ });
281
+ describe('HNSW_PRESETS', () => {
282
+ it('should have all expected presets', () => {
283
+ expect(HNSW_PRESETS).toHaveProperty('small');
284
+ expect(HNSW_PRESETS).toHaveProperty('medium');
285
+ expect(HNSW_PRESETS).toHaveProperty('large');
286
+ expect(HNSW_PRESETS).toHaveProperty('xlarge');
287
+ });
288
+ it('should have increasing M values across presets', () => {
289
+ expect(HNSW_PRESETS.small.m).toBeLessThan(HNSW_PRESETS.medium.m);
290
+ expect(HNSW_PRESETS.medium.m).toBeLessThan(HNSW_PRESETS.large.m);
291
+ expect(HNSW_PRESETS.large.m).toBeLessThan(HNSW_PRESETS.xlarge.m);
292
+ });
293
+ });
294
+ });
295
+ //# sourceMappingURL=hnsw-store.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hnsw-store.test.js","sourceRoot":"","sources":["../../../tests/embeddings/hnsw-store.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * SMI-1536: End-to-End Learning Loop Integration Tests
3
+ *
4
+ * Tests the complete learning loop from signal collection through
5
+ * personalized recommendations, verifying measurable improvements.
6
+ *
7
+ * Test Cases:
8
+ * 1. Learning improves recommendations over 10 interactions
9
+ * 2. Dismiss patterns reduce category scores measurably
10
+ * 3. Combined signals (accept + usage) boost scores higher
11
+ * 4. Learning persists across session restart
12
+ *
13
+ * @see packages/core/src/learning/interfaces.ts
14
+ * @see docs/execution/phase5-testing-execution.md
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=e2e-learning.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-learning.test.d.ts","sourceRoot":"","sources":["../../../../tests/integration/neural/e2e-learning.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
@@ -0,0 +1,238 @@
1
+ /**
2
+ * SMI-1536: End-to-End Learning Loop Integration Tests
3
+ *
4
+ * Tests the complete learning loop from signal collection through
5
+ * personalized recommendations, verifying measurable improvements.
6
+ *
7
+ * Test Cases:
8
+ * 1. Learning improves recommendations over 10 interactions
9
+ * 2. Dismiss patterns reduce category scores measurably
10
+ * 3. Combined signals (accept + usage) boost scores higher
11
+ * 4. Learning persists across session restart
12
+ *
13
+ * @see packages/core/src/learning/interfaces.ts
14
+ * @see docs/execution/phase5-testing-execution.md
15
+ */
16
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
17
+ import { createNeuralTestContext, cleanupNeuralTestContext, createDefaultProfile, } from './setup.js';
18
+ import { generateContext, generateUserJourney, generateSkillSet } from './helpers.js';
19
+ import { SignalType, SkillCategory, COLD_START_WEIGHTS } from '../../../src/learning/types.js';
20
+ describe('E2E Learning Loop Integration', () => {
21
+ let ctx;
22
+ beforeEach(() => {
23
+ ctx = createNeuralTestContext();
24
+ });
25
+ afterEach(async () => {
26
+ await cleanupNeuralTestContext(ctx);
27
+ });
28
+ describe('Learning Effectiveness', () => {
29
+ it('should improve recommendations over 10 interactions', async () => {
30
+ // Generate initial recommendations (cold start)
31
+ const skills = generateSkillSet(5);
32
+ const initialRecs = await ctx.personalizationEngine.personalizeRecommendations(skills);
33
+ // At cold start, personalization should not be applied
34
+ expect(initialRecs[0].personalization_applied).toBe(false);
35
+ // Simulate 10 interactions with testing skills
36
+ for (let i = 0; i < 10; i++) {
37
+ await ctx.signalCollector.recordAccept(`testing-skill-${i}`, generateContext({ category: SkillCategory.TESTING }));
38
+ await ctx.signalCollector.recordUsage(`testing-skill-${i}`, 'daily');
39
+ }
40
+ // Train the profile
41
+ let profile = createDefaultProfile();
42
+ const signals = await ctx.signalCollector.getSignals({});
43
+ profile = await ctx.preferenceLearner.batchUpdateProfile(profile, signals);
44
+ await ctx.profileRepository.saveProfile(profile);
45
+ // Create a new skill set with same base scores
46
+ const uniformSkills = [
47
+ {
48
+ skill_id: 'testing-new',
49
+ base_score: 0.5,
50
+ skill_data: { category: SkillCategory.TESTING, trustTier: 'community' },
51
+ },
52
+ {
53
+ skill_id: 'devops-new',
54
+ base_score: 0.5,
55
+ skill_data: { category: SkillCategory.DEVOPS, trustTier: 'community' },
56
+ },
57
+ {
58
+ skill_id: 'frontend-new',
59
+ base_score: 0.5,
60
+ skill_data: { category: SkillCategory.FRONTEND, trustTier: 'community' },
61
+ },
62
+ ];
63
+ const learnedRecs = await ctx.personalizationEngine.personalizeRecommendations(uniformSkills);
64
+ // Personalization should now be applied
65
+ expect(learnedRecs[0].personalization_applied).toBe(true);
66
+ // Testing skill should rank higher after learning
67
+ const testingRec = learnedRecs.find((r) => r.skill_id === 'testing-new');
68
+ // Testing should have higher personalized score due to positive signals
69
+ expect(testingRec.personalized_score).toBeGreaterThan(testingRec.base_score);
70
+ expect(testingRec.score_breakdown.category_boost).toBeGreaterThan(0);
71
+ // First position should be testing (highest learned preference)
72
+ expect(learnedRecs[0].skill_id).toBe('testing-new');
73
+ });
74
+ it('should reduce category scores measurably with dismiss patterns', async () => {
75
+ // Simulate consistent dismissal of SECURITY category
76
+ for (let i = 0; i < 10; i++) {
77
+ await ctx.signalCollector.recordDismiss(`security-skill-${i}`, generateContext({ category: SkillCategory.SECURITY }));
78
+ }
79
+ // Also add accepts for comparison (need threshold)
80
+ for (let i = 0; i < 5; i++) {
81
+ await ctx.signalCollector.recordAccept(`git-skill-${i}`, generateContext({ category: SkillCategory.GIT }));
82
+ }
83
+ // Train profile
84
+ let profile = createDefaultProfile();
85
+ const signals = await ctx.signalCollector.getSignals({});
86
+ profile = await ctx.preferenceLearner.batchUpdateProfile(profile, signals);
87
+ await ctx.profileRepository.saveProfile(profile);
88
+ // Security category weight should be negative
89
+ const securityWeight = profile.category_weights[SkillCategory.SECURITY] ?? 0;
90
+ const coldStartWeight = COLD_START_WEIGHTS.category_weights[SkillCategory.SECURITY] ?? 0;
91
+ expect(securityWeight).toBeLessThan(coldStartWeight);
92
+ // Git category weight should be positive (or at least higher than security)
93
+ const gitWeight = profile.category_weights[SkillCategory.GIT] ?? 0;
94
+ expect(gitWeight).toBeGreaterThan(securityWeight);
95
+ // Test personalization with uniform base scores
96
+ const skills = [
97
+ {
98
+ skill_id: 'security-new',
99
+ base_score: 0.7,
100
+ skill_data: { category: SkillCategory.SECURITY },
101
+ },
102
+ {
103
+ skill_id: 'git-new',
104
+ base_score: 0.7,
105
+ skill_data: { category: SkillCategory.GIT },
106
+ },
107
+ ];
108
+ const personalized = await ctx.personalizationEngine.personalizeRecommendations(skills);
109
+ const securityRec = personalized.find((r) => r.skill_id === 'security-new');
110
+ const gitRec = personalized.find((r) => r.skill_id === 'git-new');
111
+ // Security should have negative category boost
112
+ expect(securityRec.score_breakdown.category_boost).toBeLessThan(0);
113
+ // Git should rank higher than security despite same base score
114
+ expect(gitRec.personalized_score).toBeGreaterThan(securityRec.personalized_score);
115
+ });
116
+ it('should boost scores higher with combined accept + usage signals', async () => {
117
+ // Add signals for two skills with same accept count but different usage
118
+ const heavilyUsedSkill = 'heavily-used';
119
+ const leastUsedSkill = 'least-used';
120
+ // Both get accepted
121
+ await ctx.signalCollector.recordAccept(heavilyUsedSkill, generateContext({
122
+ category: SkillCategory.DOCUMENTATION,
123
+ }));
124
+ await ctx.signalCollector.recordAccept(leastUsedSkill, generateContext({
125
+ category: SkillCategory.ANALYSIS,
126
+ }));
127
+ // Add more accepts for threshold
128
+ for (let i = 0; i < 5; i++) {
129
+ await ctx.signalCollector.recordAccept(`other-${i}`, generateContext());
130
+ }
131
+ // Heavily used skill gets daily usage signals
132
+ for (let i = 0; i < 10; i++) {
133
+ await ctx.signalCollector.recordUsage(heavilyUsedSkill, 'daily');
134
+ }
135
+ // Train profile
136
+ let profile = createDefaultProfile();
137
+ const signals = await ctx.signalCollector.getSignals({});
138
+ profile = await ctx.preferenceLearner.batchUpdateProfile(profile, signals);
139
+ await ctx.profileRepository.saveProfile(profile);
140
+ // Documentation category (heavily used) should have higher weight
141
+ const docWeight = profile.category_weights[SkillCategory.DOCUMENTATION] ?? 0;
142
+ const analysisWeight = profile.category_weights[SkillCategory.ANALYSIS] ?? 0;
143
+ // Daily usage signals have weight 1.0 (highest), so documentation should be higher
144
+ expect(docWeight).toBeGreaterThan(analysisWeight);
145
+ });
146
+ it('should persist learning across session restart', async () => {
147
+ // Session 1: Build preferences
148
+ for (let i = 0; i < 10; i++) {
149
+ await ctx.signalCollector.recordAccept(`backend-skill-${i}`, generateContext({ category: SkillCategory.BACKEND }));
150
+ }
151
+ let profile = createDefaultProfile();
152
+ const signals1 = await ctx.signalCollector.getSignals({});
153
+ profile = await ctx.preferenceLearner.batchUpdateProfile(profile, signals1);
154
+ await ctx.profileRepository.saveProfile(profile, 'persistent-user');
155
+ const savedWeight = profile.category_weights[SkillCategory.BACKEND];
156
+ const savedSignalCount = profile.signal_count;
157
+ // Simulate session restart by creating new context
158
+ // (but keeping same profile repository state - in real implementation this would be persisted)
159
+ const savedProfile = await ctx.profileRepository.getProfile('persistent-user');
160
+ // Session 2: Load profile and verify learning persisted
161
+ expect(savedProfile).not.toBeNull();
162
+ expect(savedProfile.signal_count).toBe(savedSignalCount);
163
+ expect(savedProfile.category_weights[SkillCategory.BACKEND]).toBe(savedWeight);
164
+ // Continue learning in session 2
165
+ await ctx.signalCollector.recordAccept('backend-skill-new', generateContext({ category: SkillCategory.BACKEND }));
166
+ const signals2 = await ctx.signalCollector.getSignals({
167
+ type: SignalType.ACCEPT,
168
+ });
169
+ const latestSignal = signals2[signals2.length - 1];
170
+ const updatedProfile = await ctx.preferenceLearner.updateProfile(savedProfile, latestSignal);
171
+ // Signal count should increment
172
+ expect(updatedProfile.signal_count).toBe(savedSignalCount + 1);
173
+ // Weight should continue to accumulate
174
+ expect(updatedProfile.category_weights[SkillCategory.BACKEND]).toBeGreaterThanOrEqual(savedWeight);
175
+ });
176
+ });
177
+ describe('User Journey Simulation', () => {
178
+ it('should handle complete successful user journey', async () => {
179
+ const skillId = 'journey-skill-success';
180
+ const signals = generateUserJourney(skillId, 'successful', SkillCategory.TESTING);
181
+ // Simulate the journey by adding signals
182
+ for (const signal of signals) {
183
+ ctx.signalCollector.addSignal(signal);
184
+ }
185
+ // Add more signals to meet threshold
186
+ for (let i = 0; i < 5; i++) {
187
+ await ctx.signalCollector.recordAccept(`filler-${i}`, generateContext());
188
+ }
189
+ // Train profile
190
+ let profile = createDefaultProfile();
191
+ const allSignals = await ctx.signalCollector.getSignals({});
192
+ profile = await ctx.preferenceLearner.batchUpdateProfile(profile, allSignals);
193
+ // Successful journey (accept + multiple usage) should strongly boost category
194
+ expect(profile.category_weights[SkillCategory.TESTING]).toBeGreaterThan(COLD_START_WEIGHTS.category_weights[SkillCategory.TESTING] ?? 0);
195
+ });
196
+ it('should handle abandoned user journey', async () => {
197
+ const skillId = 'journey-skill-abandoned';
198
+ const signals = generateUserJourney(skillId, 'abandoned', SkillCategory.DEVOPS);
199
+ // Simulate the journey
200
+ for (const signal of signals) {
201
+ ctx.signalCollector.addSignal(signal);
202
+ }
203
+ // Add more signals to meet threshold
204
+ for (let i = 0; i < 5; i++) {
205
+ await ctx.signalCollector.recordAccept(`filler-${i}`, generateContext());
206
+ }
207
+ // Train profile
208
+ let profile = createDefaultProfile();
209
+ const allSignals = await ctx.signalCollector.getSignals({});
210
+ profile = await ctx.preferenceLearner.batchUpdateProfile(profile, allSignals);
211
+ // Abandoned journey (accept + abandoned) has net negative effect
212
+ // ACCEPT weight (0.5) + ABANDONED weight (-0.7) = -0.2 net
213
+ // The net effect should be slightly negative due to ABANDONED signal
214
+ // But we also added 5 filler accepts, so the overall effect depends on implementation
215
+ // Verify profile was updated
216
+ expect(profile.signal_count).toBeGreaterThan(0);
217
+ });
218
+ it('should handle uninstalled user journey', async () => {
219
+ const skillId = 'journey-skill-uninstalled';
220
+ const signals = generateUserJourney(skillId, 'uninstalled', SkillCategory.SECURITY);
221
+ // Simulate the journey
222
+ for (const signal of signals) {
223
+ ctx.signalCollector.addSignal(signal);
224
+ }
225
+ // Add minimal fillers for threshold
226
+ for (let i = 0; i < 3; i++) {
227
+ await ctx.signalCollector.recordAccept(`filler-${i}`, generateContext());
228
+ }
229
+ // Train profile
230
+ let profile = createDefaultProfile();
231
+ const allSignals = await ctx.signalCollector.getSignals({});
232
+ profile = await ctx.preferenceLearner.batchUpdateProfile(profile, allSignals);
233
+ // Uninstalled skill should be in negative patterns
234
+ expect(profile.negative_patterns.skill_ids).toContain(skillId);
235
+ });
236
+ });
237
+ });
238
+ //# sourceMappingURL=e2e-learning.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"e2e-learning.test.js","sourceRoot":"","sources":["../../../../tests/integration/neural/e2e-learning.test.ts"],"names":[],"mappings":"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"}