vesper-memory 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/.env.example +28 -0
  2. package/CHANGELOG.md +137 -0
  3. package/LICENSE +21 -0
  4. package/README.md +665 -0
  5. package/config/sqlite-schema.sql +413 -0
  6. package/dist/benchmark/index.d.ts +18 -0
  7. package/dist/benchmark/index.d.ts.map +1 -0
  8. package/dist/benchmark/index.js +19 -0
  9. package/dist/benchmark/index.js.map +1 -0
  10. package/dist/benchmark/metrics-collector.d.ts +90 -0
  11. package/dist/benchmark/metrics-collector.d.ts.map +1 -0
  12. package/dist/benchmark/metrics-collector.js +196 -0
  13. package/dist/benchmark/metrics-collector.js.map +1 -0
  14. package/dist/benchmark/report-generator.d.ts +35 -0
  15. package/dist/benchmark/report-generator.d.ts.map +1 -0
  16. package/dist/benchmark/report-generator.js +221 -0
  17. package/dist/benchmark/report-generator.js.map +1 -0
  18. package/dist/benchmark/runner.d.ts +30 -0
  19. package/dist/benchmark/runner.d.ts.map +1 -0
  20. package/dist/benchmark/runner.js +308 -0
  21. package/dist/benchmark/runner.js.map +1 -0
  22. package/dist/benchmark/scenarios/context-recall.d.ts +65 -0
  23. package/dist/benchmark/scenarios/context-recall.d.ts.map +1 -0
  24. package/dist/benchmark/scenarios/context-recall.js +180 -0
  25. package/dist/benchmark/scenarios/context-recall.js.map +1 -0
  26. package/dist/benchmark/scenarios/index.d.ts +34 -0
  27. package/dist/benchmark/scenarios/index.d.ts.map +1 -0
  28. package/dist/benchmark/scenarios/index.js +230 -0
  29. package/dist/benchmark/scenarios/index.js.map +1 -0
  30. package/dist/benchmark/statistics.d.ts +125 -0
  31. package/dist/benchmark/statistics.d.ts.map +1 -0
  32. package/dist/benchmark/statistics.js +520 -0
  33. package/dist/benchmark/statistics.js.map +1 -0
  34. package/dist/benchmark/types.d.ts +458 -0
  35. package/dist/benchmark/types.d.ts.map +1 -0
  36. package/dist/benchmark/types.js +297 -0
  37. package/dist/benchmark/types.js.map +1 -0
  38. package/dist/cli 2.js +583 -0
  39. package/dist/cli.d 2.ts +8 -0
  40. package/dist/cli.d.ts +8 -0
  41. package/dist/cli.d.ts 2.map +1 -0
  42. package/dist/cli.d.ts.map +1 -0
  43. package/dist/cli.js +583 -0
  44. package/dist/cli.js 2.map +1 -0
  45. package/dist/cli.js.map +1 -0
  46. package/dist/config/rate-limits.d.ts +76 -0
  47. package/dist/config/rate-limits.d.ts.map +1 -0
  48. package/dist/config/rate-limits.js +161 -0
  49. package/dist/config/rate-limits.js.map +1 -0
  50. package/dist/config 2/rate-limits.d.ts +76 -0
  51. package/dist/config 2/rate-limits.d.ts.map +1 -0
  52. package/dist/config 2/rate-limits.js +161 -0
  53. package/dist/config 2/rate-limits.js.map +1 -0
  54. package/dist/consolidation/pipeline.d.ts +52 -0
  55. package/dist/consolidation/pipeline.d.ts.map +1 -0
  56. package/dist/consolidation/pipeline.js +193 -0
  57. package/dist/consolidation/pipeline.js.map +1 -0
  58. package/dist/consolidation 2/pipeline.d.ts +52 -0
  59. package/dist/consolidation 2/pipeline.d.ts.map +1 -0
  60. package/dist/consolidation 2/pipeline.js +193 -0
  61. package/dist/consolidation 2/pipeline.js.map +1 -0
  62. package/dist/embeddings/client.d.ts +95 -0
  63. package/dist/embeddings/client.d.ts.map +1 -0
  64. package/dist/embeddings/client.js +162 -0
  65. package/dist/embeddings/client.js.map +1 -0
  66. package/dist/embeddings 2/client.d.ts +94 -0
  67. package/dist/embeddings 2/client.d.ts.map +1 -0
  68. package/dist/embeddings 2/client.js +144 -0
  69. package/dist/embeddings 2/client.js.map +1 -0
  70. package/dist/memory-layers/semantic-memory.d.ts +76 -0
  71. package/dist/memory-layers/semantic-memory.d.ts.map +1 -0
  72. package/dist/memory-layers/semantic-memory.js +231 -0
  73. package/dist/memory-layers/semantic-memory.js.map +1 -0
  74. package/dist/memory-layers/skill-library.d.ts +25 -0
  75. package/dist/memory-layers/skill-library.d.ts.map +1 -0
  76. package/dist/memory-layers/skill-library.js +54 -0
  77. package/dist/memory-layers/skill-library.js.map +1 -0
  78. package/dist/memory-layers/working-memory.d.ts +57 -0
  79. package/dist/memory-layers/working-memory.d.ts.map +1 -0
  80. package/dist/memory-layers/working-memory.js +153 -0
  81. package/dist/memory-layers/working-memory.js.map +1 -0
  82. package/dist/memory-layers 2/semantic-memory.d.ts +33 -0
  83. package/dist/memory-layers 2/semantic-memory.d.ts.map +1 -0
  84. package/dist/memory-layers 2/semantic-memory.js +114 -0
  85. package/dist/memory-layers 2/semantic-memory.js.map +1 -0
  86. package/dist/memory-layers 2/skill-library.d.ts +25 -0
  87. package/dist/memory-layers 2/skill-library.d.ts.map +1 -0
  88. package/dist/memory-layers 2/skill-library.js +54 -0
  89. package/dist/memory-layers 2/skill-library.js.map +1 -0
  90. package/dist/memory-layers 2/working-memory.d.ts +57 -0
  91. package/dist/memory-layers 2/working-memory.d.ts.map +1 -0
  92. package/dist/memory-layers 2/working-memory.js +153 -0
  93. package/dist/memory-layers 2/working-memory.js.map +1 -0
  94. package/dist/middleware/auth.d.ts +287 -0
  95. package/dist/middleware/auth.d.ts.map +1 -0
  96. package/dist/middleware/auth.js +610 -0
  97. package/dist/middleware/auth.js.map +1 -0
  98. package/dist/middleware 2/auth.d.ts +287 -0
  99. package/dist/middleware 2/auth.d.ts.map +1 -0
  100. package/dist/middleware 2/auth.js +610 -0
  101. package/dist/middleware 2/auth.js.map +1 -0
  102. package/dist/monitoring/health.d.ts +241 -0
  103. package/dist/monitoring/health.d.ts.map +1 -0
  104. package/dist/monitoring/health.js +649 -0
  105. package/dist/monitoring/health.js.map +1 -0
  106. package/dist/monitoring/metrics.d.ts +153 -0
  107. package/dist/monitoring/metrics.d.ts.map +1 -0
  108. package/dist/monitoring/metrics.js +349 -0
  109. package/dist/monitoring/metrics.js.map +1 -0
  110. package/dist/monitoring 2/health.d.ts +241 -0
  111. package/dist/monitoring 2/health.d.ts.map +1 -0
  112. package/dist/monitoring 2/health.js +649 -0
  113. package/dist/monitoring 2/health.js.map +1 -0
  114. package/dist/monitoring 2/metrics.d.ts +153 -0
  115. package/dist/monitoring 2/metrics.d.ts.map +1 -0
  116. package/dist/monitoring 2/metrics.js +349 -0
  117. package/dist/monitoring 2/metrics.js.map +1 -0
  118. package/dist/retrieval/hybrid-search.d.ts +186 -0
  119. package/dist/retrieval/hybrid-search.d.ts.map +1 -0
  120. package/dist/retrieval/hybrid-search.js +330 -0
  121. package/dist/retrieval/hybrid-search.js.map +1 -0
  122. package/dist/retrieval 2/hybrid-search.d.ts +186 -0
  123. package/dist/retrieval 2/hybrid-search.d.ts.map +1 -0
  124. package/dist/retrieval 2/hybrid-search.js +327 -0
  125. package/dist/retrieval 2/hybrid-search.js.map +1 -0
  126. package/dist/router/smart-router.d.ts +157 -0
  127. package/dist/router/smart-router.d.ts.map +1 -0
  128. package/dist/router/smart-router.js +524 -0
  129. package/dist/router/smart-router.js.map +1 -0
  130. package/dist/router 2/smart-router.d.ts +111 -0
  131. package/dist/router 2/smart-router.d.ts.map +1 -0
  132. package/dist/router 2/smart-router.js +383 -0
  133. package/dist/router 2/smart-router.js.map +1 -0
  134. package/dist/security/rate-limit-middleware.d.ts +119 -0
  135. package/dist/security/rate-limit-middleware.d.ts.map +1 -0
  136. package/dist/security/rate-limit-middleware.js +193 -0
  137. package/dist/security/rate-limit-middleware.js.map +1 -0
  138. package/dist/security 2/rate-limit-middleware.d.ts +119 -0
  139. package/dist/security 2/rate-limit-middleware.d.ts.map +1 -0
  140. package/dist/security 2/rate-limit-middleware.js +193 -0
  141. package/dist/security 2/rate-limit-middleware.js.map +1 -0
  142. package/dist/server 2.js +861 -0
  143. package/dist/server-wrapper 2.js +170 -0
  144. package/dist/server-wrapper.d 2.ts +12 -0
  145. package/dist/server-wrapper.d.ts +12 -0
  146. package/dist/server-wrapper.d.ts 2.map +1 -0
  147. package/dist/server-wrapper.d.ts.map +1 -0
  148. package/dist/server-wrapper.js +170 -0
  149. package/dist/server-wrapper.js 2.map +1 -0
  150. package/dist/server-wrapper.js.map +1 -0
  151. package/dist/server.d 2.ts +14 -0
  152. package/dist/server.d.ts +14 -0
  153. package/dist/server.d.ts 2.map +1 -0
  154. package/dist/server.d.ts.map +1 -0
  155. package/dist/server.js +656 -0
  156. package/dist/server.js 2.map +1 -0
  157. package/dist/server.js.map +1 -0
  158. package/dist/synthesis/conflict-detector.d.ts +43 -0
  159. package/dist/synthesis/conflict-detector.d.ts.map +1 -0
  160. package/dist/synthesis/conflict-detector.js +125 -0
  161. package/dist/synthesis/conflict-detector.js.map +1 -0
  162. package/dist/synthesis 2/conflict-detector.d.ts +43 -0
  163. package/dist/synthesis 2/conflict-detector.d.ts.map +1 -0
  164. package/dist/synthesis 2/conflict-detector.js +125 -0
  165. package/dist/synthesis 2/conflict-detector.js.map +1 -0
  166. package/dist/utils/monitoring.d.ts +25 -0
  167. package/dist/utils/monitoring.d.ts.map +1 -0
  168. package/dist/utils/monitoring.js +41 -0
  169. package/dist/utils/monitoring.js.map +1 -0
  170. package/dist/utils/rate-limiter.d.ts +97 -0
  171. package/dist/utils/rate-limiter.d.ts.map +1 -0
  172. package/dist/utils/rate-limiter.js +166 -0
  173. package/dist/utils/rate-limiter.js.map +1 -0
  174. package/dist/utils/validation.d.ts +139 -0
  175. package/dist/utils/validation.d.ts.map +1 -0
  176. package/dist/utils/validation.js +195 -0
  177. package/dist/utils/validation.js.map +1 -0
  178. package/dist/utils 2/monitoring.d.ts +25 -0
  179. package/dist/utils 2/monitoring.d.ts.map +1 -0
  180. package/dist/utils 2/monitoring.js +41 -0
  181. package/dist/utils 2/monitoring.js.map +1 -0
  182. package/dist/utils 2/rate-limiter.d.ts +97 -0
  183. package/dist/utils 2/rate-limiter.d.ts.map +1 -0
  184. package/dist/utils 2/rate-limiter.js +166 -0
  185. package/dist/utils 2/rate-limiter.js.map +1 -0
  186. package/dist/utils 2/validation.d.ts +139 -0
  187. package/dist/utils 2/validation.d.ts.map +1 -0
  188. package/dist/utils 2/validation.js +195 -0
  189. package/dist/utils 2/validation.js.map +1 -0
  190. package/docker-compose.yml +65 -0
  191. package/embedding-service/Dockerfile +29 -0
  192. package/embedding-service/README.md +68 -0
  193. package/embedding-service/requirements.txt +6 -0
  194. package/embedding-service/server.py +110 -0
  195. package/package.json +92 -0
  196. package/scripts/FRESH_INSTALL.md +280 -0
  197. package/scripts/README.md +101 -0
  198. package/scripts/ensure-infrastructure.sh +38 -0
  199. package/scripts/fresh-install-auto.sh +96 -0
  200. package/scripts/fresh-install.sh +167 -0
  201. package/scripts/health-check.sh +72 -0
  202. package/scripts/init-qdrant.ts +201 -0
  203. package/scripts/postinstall.js +94 -0
  204. package/scripts/run-accuracy-benchmark.ts +1237 -0
  205. package/scripts/run-real-benchmark.ts +581 -0
  206. package/scripts/test-mcp-server.sh +38 -0
@@ -0,0 +1,581 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Real-World Vesper Benchmark Script
4
+ *
5
+ * Runs actual performance benchmarks against live Vesper infrastructure:
6
+ * - Redis (working memory)
7
+ * - Qdrant (vector search)
8
+ * - BGE-large embeddings
9
+ * - SQLite (knowledge graph)
10
+ *
11
+ * Scientific methodology:
12
+ * - Warmup runs to prime caches and eliminate cold-start effects
13
+ * - Multiple measurement runs for statistical power
14
+ * - Welch's t-test for hypothesis testing (handles unequal variances)
15
+ * - Cohen's d for effect size interpretation
16
+ * - Proper isolation between enabled/disabled conditions
17
+ *
18
+ * Usage: npm run benchmark:real
19
+ */
20
+
21
+ import { createBenchmarkRunner, BenchmarkConfig } from "../src/benchmark/runner.js";
22
+ import { generateMarkdownReport } from "../src/benchmark/report-generator.js";
23
+ import { MCPClient } from "../src/benchmark/scenarios/context-recall.js";
24
+ import { WorkingMemoryLayer, WorkingMemory } from "../src/memory-layers/working-memory.js";
25
+ import { SemanticMemoryLayer } from "../src/memory-layers/semantic-memory.js";
26
+ import { HybridSearchEngine } from "../src/retrieval/hybrid-search.js";
27
+ import { EmbeddingClient } from "../src/embeddings/client.js";
28
+ import Redis from "ioredis";
29
+ import Database from "better-sqlite3";
30
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
31
+ import { join } from "path";
32
+ import { randomUUID } from "crypto";
33
+
34
+ // Configuration
35
+ const CONFIG: BenchmarkConfig = {
36
+ warmupRuns: 3,
37
+ measurementRuns: 10,
38
+ timeoutMs: 60000,
39
+ significanceLevel: 0.05,
40
+ scenarios: [
41
+ "context-recall",
42
+ "cross-session",
43
+ "skill-retrieval",
44
+ "token-efficiency",
45
+ "semantic-accuracy",
46
+ ],
47
+ outputDirectory: "./benchmarks",
48
+ };
49
+
50
+ /**
51
+ * Real Vesper MCP Client
52
+ *
53
+ * Implements the MCPClient interface expected by benchmark scenarios.
54
+ * Uses actual Vesper infrastructure for memory operations.
55
+ *
56
+ * Key design principles:
57
+ * - When enabled: full memory storage and retrieval via Vesper
58
+ * - When disabled: pass-through with minimal latency (baseline comparison)
59
+ * - Proper cleanup between test runs to avoid carryover effects
60
+ */
61
+ class RealVesperClient implements MCPClient {
62
+ private workingMemory: WorkingMemoryLayer;
63
+ private semanticMemory: SemanticMemoryLayer;
64
+ private hybridSearch: HybridSearchEngine;
65
+ private embeddingClient: EmbeddingClient;
66
+ private redis: Redis;
67
+ private enabled: boolean = true;
68
+ private sessionId: string;
69
+
70
+ constructor(
71
+ redis: Redis,
72
+ db: Database.Database,
73
+ embeddingClient: EmbeddingClient,
74
+ hybridSearch: HybridSearchEngine
75
+ ) {
76
+ this.redis = redis;
77
+ this.workingMemory = new WorkingMemoryLayer(redis);
78
+ this.semanticMemory = new SemanticMemoryLayer(db);
79
+ this.hybridSearch = hybridSearch;
80
+ this.embeddingClient = embeddingClient;
81
+ this.sessionId = `bench-${randomUUID().slice(0, 8)}`;
82
+ }
83
+
84
+ enable(): void {
85
+ this.enabled = true;
86
+ }
87
+
88
+ disable(): void {
89
+ this.enabled = false;
90
+ }
91
+
92
+ isEnabled(): boolean {
93
+ return this.enabled;
94
+ }
95
+
96
+ /**
97
+ * Store memory implementation
98
+ *
99
+ * When enabled:
100
+ * 1. Generate embedding via BGE-large service
101
+ * 2. Store in working memory (Redis) for fast recall
102
+ * 3. Extract entities and store in semantic memory (SQLite)
103
+ * 4. Index in Qdrant for vector search
104
+ *
105
+ * When disabled:
106
+ * Return immediately with synthetic ID (baseline)
107
+ */
108
+ async storeMemory(
109
+ content: string,
110
+ memoryType: string,
111
+ metadata?: Record<string, unknown>
112
+ ): Promise<{ success: boolean; memory_id: string }> {
113
+ if (!this.enabled) {
114
+ // Pass-through mode - no storage, just return synthetic ID
115
+ return {
116
+ success: true,
117
+ memory_id: `disabled-${Date.now()}`,
118
+ };
119
+ }
120
+
121
+ try {
122
+ // Qdrant requires UUID format for string IDs
123
+ const memoryId = randomUUID();
124
+ const conversationId = `conv-${this.sessionId}-${Date.now()}`;
125
+
126
+ // 1. Generate embedding
127
+ const embedding = await this.embeddingClient.embed(content);
128
+
129
+ // 2. Store in working memory
130
+ const workingMemoryEntry: WorkingMemory = {
131
+ conversationId,
132
+ timestamp: new Date(),
133
+ fullText: content,
134
+ embedding,
135
+ keyEntities: this.extractEntities(content),
136
+ topics: this.extractTopics(content),
137
+ userIntent: memoryType,
138
+ };
139
+ await this.workingMemory.store(workingMemoryEntry);
140
+
141
+ // 3. Store in semantic memory (entity extraction)
142
+ const entities = this.extractEntities(content);
143
+ for (const entityName of entities) {
144
+ this.semanticMemory.upsertEntity({
145
+ name: entityName,
146
+ type: this.inferEntityType(entityName, content),
147
+ description: content,
148
+ confidence: 0.9,
149
+ });
150
+ }
151
+
152
+ // 4. Index in Qdrant for vector search
153
+ await this.hybridSearch.upsertMemory(memoryId, embedding, {
154
+ content,
155
+ memoryType,
156
+ timestamp: Date.now(),
157
+ sessionId: this.sessionId,
158
+ ...metadata,
159
+ });
160
+
161
+ return {
162
+ success: true,
163
+ memory_id: memoryId,
164
+ };
165
+ } catch (error) {
166
+ console.error("Store error:", error);
167
+ return {
168
+ success: false,
169
+ memory_id: "",
170
+ };
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Retrieve memory implementation
176
+ *
177
+ * When enabled:
178
+ * 1. Check working memory first (fast path, ~5ms)
179
+ * 2. If not found, generate query embedding
180
+ * 3. Perform hybrid search in Qdrant
181
+ * 4. Return top-K results with similarity scores
182
+ *
183
+ * When disabled:
184
+ * Return empty results (baseline for comparison)
185
+ */
186
+ async retrieveMemory(
187
+ query: string,
188
+ options?: { max_results?: number }
189
+ ): Promise<{
190
+ success: boolean;
191
+ results: Array<{ content: string; similarity_score: number }>;
192
+ }> {
193
+ if (!this.enabled) {
194
+ // Pass-through mode - return empty results
195
+ return {
196
+ success: true,
197
+ results: [],
198
+ };
199
+ }
200
+
201
+ const maxResults = options?.max_results ?? 3;
202
+
203
+ try {
204
+ // 1. Check working memory first (fast path)
205
+ const workingResults = await this.workingMemory.search(query, maxResults);
206
+
207
+ if (workingResults.length > 0 && workingResults[0].similarity > 0.7) {
208
+ // Working memory hit - return immediately
209
+ return {
210
+ success: true,
211
+ results: workingResults.map((r) => ({
212
+ content: r.memory.fullText,
213
+ similarity_score: r.similarity,
214
+ })),
215
+ };
216
+ }
217
+
218
+ // 2. Generate query embedding
219
+ const queryEmbedding = await this.embeddingClient.embed(query);
220
+
221
+ // 3. Perform hybrid search in Qdrant
222
+ const searchResults = await this.hybridSearch.hybridSearch(
223
+ queryEmbedding,
224
+ maxResults
225
+ );
226
+
227
+ // 4. Format and return results
228
+ return {
229
+ success: true,
230
+ results: searchResults.map((r) => ({
231
+ content: (r.payload?.content as string) || "",
232
+ similarity_score: r.fusedScore,
233
+ })),
234
+ };
235
+ } catch (error) {
236
+ console.error("Retrieve error:", error);
237
+ return {
238
+ success: false,
239
+ results: [],
240
+ };
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Clear all memories for clean slate between test runs
246
+ *
247
+ * Important for scientific validity:
248
+ * - Prevents carryover effects between scenarios
249
+ * - Ensures each scenario starts from identical baseline
250
+ */
251
+ async clearMemories(): Promise<void> {
252
+ try {
253
+ // Clear working memory
254
+ const keys = await this.redis.keys("working:*");
255
+ if (keys.length > 0) {
256
+ await this.redis.del(...keys);
257
+ }
258
+
259
+ // Note: For benchmarks, we don't clear Qdrant vectors between runs
260
+ // as each scenario operates on its own stored facts
261
+ } catch (error) {
262
+ console.error("Clear memories error:", error);
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Generate a new session ID for isolation
268
+ */
269
+ newSession(): void {
270
+ this.sessionId = `bench-${randomUUID().slice(0, 8)}`;
271
+ }
272
+
273
+ // Helper methods for entity extraction
274
+
275
+ private extractEntities(content: string): string[] {
276
+ const entities: string[] = [];
277
+ const lowerContent = content.toLowerCase();
278
+
279
+ // Technical terms
280
+ const techTerms = [
281
+ "typescript",
282
+ "javascript",
283
+ "python",
284
+ "rust",
285
+ "redis",
286
+ "qdrant",
287
+ "sqlite",
288
+ "postgresql",
289
+ "docker",
290
+ "kubernetes",
291
+ "vesper",
292
+ "claude",
293
+ "mcp",
294
+ "api",
295
+ "rest",
296
+ "http",
297
+ ];
298
+
299
+ for (const term of techTerms) {
300
+ if (lowerContent.includes(term)) {
301
+ entities.push(term);
302
+ }
303
+ }
304
+
305
+ // Proper nouns (simplified heuristic)
306
+ const words = content.split(/\s+/);
307
+ for (const word of words) {
308
+ if (/^[A-Z][a-z]+$/.test(word) && word.length > 2) {
309
+ entities.push(word.toLowerCase());
310
+ }
311
+ }
312
+
313
+ return [...new Set(entities)].slice(0, 5);
314
+ }
315
+
316
+ private extractTopics(content: string): string[] {
317
+ const topics: string[] = [];
318
+ const lowerContent = content.toLowerCase();
319
+
320
+ const topicPatterns = [
321
+ { pattern: /coding|programming|development/i, topic: "development" },
322
+ { pattern: /test|testing|coverage/i, topic: "testing" },
323
+ { pattern: /memory|cache|storage/i, topic: "storage" },
324
+ { pattern: /prefer|like|want/i, topic: "preferences" },
325
+ { pattern: /project|building|working on/i, topic: "projects" },
326
+ { pattern: /deploy|production|staging/i, topic: "deployment" },
327
+ ];
328
+
329
+ for (const { pattern, topic } of topicPatterns) {
330
+ if (pattern.test(lowerContent)) {
331
+ topics.push(topic);
332
+ }
333
+ }
334
+
335
+ return topics;
336
+ }
337
+
338
+ private inferEntityType(
339
+ entityName: string,
340
+ context: string
341
+ ): "person" | "project" | "concept" | "preference" {
342
+ const lowerContext = context.toLowerCase();
343
+ const lowerName = entityName.toLowerCase();
344
+
345
+ if (lowerContext.includes("prefer") || lowerContext.includes("like")) {
346
+ return "preference";
347
+ }
348
+ if (
349
+ lowerContext.includes("project") ||
350
+ lowerContext.includes("building")
351
+ ) {
352
+ return "project";
353
+ }
354
+ if (
355
+ ["david", "user", "claude", "assistant"].some((n) => lowerName.includes(n))
356
+ ) {
357
+ return "person";
358
+ }
359
+ return "concept";
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Ensure Qdrant collection exists with proper configuration
365
+ */
366
+ async function ensureQdrantCollection(hybridSearch: HybridSearchEngine): Promise<void> {
367
+ try {
368
+ await hybridSearch.initializeCollection();
369
+ } catch (error) {
370
+ // Collection may already exist, which is fine
371
+ console.log(" Qdrant collection ready (may already exist)");
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Initialize SQLite database with required schema
377
+ */
378
+ function initializeDatabase(dbPath: string): Database.Database {
379
+ // Ensure data directory exists
380
+ const dataDir = dbPath.substring(0, dbPath.lastIndexOf("/"));
381
+ if (dataDir && !existsSync(dataDir)) {
382
+ mkdirSync(dataDir, { recursive: true });
383
+ }
384
+
385
+ const db = new Database(dbPath);
386
+
387
+ // Create tables if they don't exist
388
+ db.exec(`
389
+ CREATE TABLE IF NOT EXISTS entities (
390
+ id TEXT PRIMARY KEY,
391
+ name TEXT NOT NULL,
392
+ type TEXT NOT NULL,
393
+ description TEXT,
394
+ confidence REAL DEFAULT 1.0,
395
+ created_at TEXT NOT NULL,
396
+ last_accessed TEXT NOT NULL,
397
+ access_count INTEGER DEFAULT 1
398
+ );
399
+
400
+ CREATE TABLE IF NOT EXISTS relationships (
401
+ id TEXT PRIMARY KEY,
402
+ source_id TEXT NOT NULL,
403
+ target_id TEXT NOT NULL,
404
+ relation_type TEXT NOT NULL,
405
+ strength REAL DEFAULT 0.8,
406
+ evidence TEXT,
407
+ created_at TEXT NOT NULL,
408
+ last_reinforced TEXT NOT NULL,
409
+ FOREIGN KEY (source_id) REFERENCES entities(id),
410
+ FOREIGN KEY (target_id) REFERENCES entities(id)
411
+ );
412
+
413
+ CREATE INDEX IF NOT EXISTS idx_entities_name ON entities(name);
414
+ CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(type);
415
+ CREATE INDEX IF NOT EXISTS idx_relationships_source ON relationships(source_id);
416
+ CREATE INDEX IF NOT EXISTS idx_relationships_target ON relationships(target_id);
417
+ `);
418
+
419
+ return db;
420
+ }
421
+
422
+ async function main() {
423
+ console.log("=".repeat(60));
424
+ console.log(" Real-World Vesper Benchmark");
425
+ console.log("=".repeat(60));
426
+ console.log("");
427
+ console.log("Scientific Methodology:");
428
+ console.log(" - A/B testing: Vesper Enabled vs Disabled");
429
+ console.log(" - Statistical tests: Welch's t-test, Cohen's d");
430
+ console.log(" - Significance level: alpha = 0.05");
431
+ console.log("");
432
+ console.log("Configuration:");
433
+ console.log(` Warmup Runs: ${CONFIG.warmupRuns}`);
434
+ console.log(` Measurement Runs: ${CONFIG.measurementRuns}`);
435
+ console.log(` Scenarios: ${CONFIG.scenarios.join(", ")}`);
436
+ console.log(` Timeout: ${CONFIG.timeoutMs}ms`);
437
+ console.log("");
438
+
439
+ // Initialize infrastructure
440
+ console.log("Connecting to infrastructure...");
441
+
442
+ const redis = new Redis({
443
+ host: process.env.REDIS_HOST || "localhost",
444
+ port: parseInt(process.env.REDIS_PORT || "6379"),
445
+ password: process.env.REDIS_PASSWORD,
446
+ });
447
+
448
+ const dbPath = process.env.SQLITE_DB || "./data/memory.db";
449
+ const db = initializeDatabase(dbPath);
450
+
451
+ const embeddingClient = new EmbeddingClient({
452
+ serviceUrl: process.env.EMBEDDING_SERVICE_URL || "http://localhost:8000",
453
+ });
454
+
455
+ const hybridSearch = new HybridSearchEngine(
456
+ process.env.QDRANT_URL || "http://localhost:6333",
457
+ "memory-vectors",
458
+ 1024,
459
+ process.env.QDRANT_API_KEY
460
+ );
461
+
462
+ // Test connections
463
+ try {
464
+ await redis.ping();
465
+ console.log(" [OK] Redis connected");
466
+ } catch (error) {
467
+ console.error(" [FAIL] Redis connection failed:", error);
468
+ process.exit(1);
469
+ }
470
+
471
+ try {
472
+ await embeddingClient.health();
473
+ console.log(" [OK] Embedding service connected");
474
+ } catch (error) {
475
+ console.error(" [FAIL] Embedding service connection failed:", error);
476
+ process.exit(1);
477
+ }
478
+
479
+ try {
480
+ await ensureQdrantCollection(hybridSearch);
481
+ console.log(" [OK] Qdrant connected");
482
+ } catch (error) {
483
+ console.error(" [FAIL] Qdrant connection failed:", error);
484
+ process.exit(1);
485
+ }
486
+
487
+ console.log(" [OK] SQLite connected");
488
+ console.log("");
489
+
490
+ // Create real Vesper client
491
+ const client = new RealVesperClient(redis, db, embeddingClient, hybridSearch);
492
+
493
+ // Create benchmark runner
494
+ const runner = createBenchmarkRunner(client as MCPClient, CONFIG);
495
+
496
+ // Run benchmarks
497
+ console.log("Running benchmarks...");
498
+ console.log("(This may take several minutes)");
499
+ console.log("");
500
+
501
+ const startTime = Date.now();
502
+ const result = await runner.run();
503
+ const duration = Date.now() - startTime;
504
+
505
+ console.log("");
506
+ console.log(`Benchmarks completed in ${(duration / 1000).toFixed(1)}s`);
507
+ console.log("");
508
+
509
+ // Generate report
510
+ console.log("Generating report...");
511
+
512
+ const report = generateMarkdownReport(result);
513
+
514
+ // Save report
515
+ const outputDir = CONFIG.outputDirectory;
516
+ mkdirSync(outputDir, { recursive: true });
517
+
518
+ const reportPath = join(outputDir, "real-world-results.md");
519
+ writeFileSync(reportPath, report);
520
+
521
+ console.log(` Report saved to ${reportPath}`);
522
+ console.log("");
523
+
524
+ // Print summary to console
525
+ console.log("=".repeat(60));
526
+ console.log(" Summary");
527
+ console.log("=".repeat(60));
528
+
529
+ for (const comparison of result.comparisons) {
530
+ const winner =
531
+ comparison.winner === "vesper-enabled"
532
+ ? "[WIN] Vesper"
533
+ : comparison.winner === "vesper-disabled"
534
+ ? "[LOSS] Baseline"
535
+ : "[TIE]";
536
+
537
+ const improvement = comparison.statistics.latencyImprovement.toFixed(1);
538
+ const sign = comparison.statistics.latencyImprovement > 0 ? "+" : "";
539
+ const pValue = comparison.statistics.tTestResult.pValue.toFixed(4);
540
+ const significant = comparison.statistics.tTestResult.significant ? "Yes" : "No";
541
+ const effectSize = comparison.statistics.effectSize.interpretation;
542
+
543
+ console.log("");
544
+ console.log(`${comparison.scenarioType}:`);
545
+ console.log(` Winner: ${winner}`);
546
+ console.log(` Latency Change: ${sign}${improvement}%`);
547
+ console.log(` p-value: ${pValue}`);
548
+ console.log(` Significant: ${significant}`);
549
+ console.log(` Effect Size: ${effectSize}`);
550
+ }
551
+
552
+ console.log("");
553
+ console.log("=".repeat(60));
554
+ console.log(" Overall Results");
555
+ console.log("=".repeat(60));
556
+ console.log(` Total Scenarios: ${result.summary.totalScenarios}`);
557
+ console.log(` Vesper Wins: ${result.summary.vesperWins}`);
558
+ console.log(` Baseline Wins: ${result.summary.disabledWins}`);
559
+ console.log(` Ties: ${result.summary.ties}`);
560
+ console.log(
561
+ ` Overall Improvement: ${result.summary.overallImprovement > 0 ? "+" : ""}${result.summary.overallImprovement.toFixed(1)}%`
562
+ );
563
+ console.log(
564
+ ` Statistically Significant: ${result.summary.statisticallySignificant ? "Yes" : "No"}`
565
+ );
566
+ console.log("");
567
+ console.log(`Full report: ${reportPath}`);
568
+ console.log("");
569
+
570
+ // Cleanup
571
+ await redis.quit();
572
+ db.close();
573
+
574
+ console.log("Benchmark complete!");
575
+ }
576
+
577
+ // Run benchmark
578
+ main().catch((error) => {
579
+ console.error("Benchmark failed:", error);
580
+ process.exit(1);
581
+ });
@@ -0,0 +1,38 @@
1
+ #!/bin/bash
2
+ # Quick test to verify MCP server can start and connect to infrastructure
3
+
4
+ set -e
5
+
6
+ PROJECT_DIR="/Users/fitzy/Documents/MemoryProject"
7
+ cd "$PROJECT_DIR"
8
+
9
+ echo "๐Ÿงช Testing MCP Server Startup..."
10
+ echo "================================"
11
+
12
+ # Source environment variables
13
+ export REDIS_HOST=localhost
14
+ export REDIS_PORT=6379
15
+ export REDIS_PASSWORD="MHot0MIuDfST4QUY6g3WVbLzcDEzJ14B"
16
+ export QDRANT_URL="http://localhost:6333"
17
+ export QDRANT_API_KEY="0mTJEZVwmIXceZM7hYwd2SgZo8sOT21p"
18
+ export SQLITE_DB="$PROJECT_DIR/data/memory.db"
19
+ export NODE_ENV=production
20
+
21
+ # Test server can start (send EOF immediately to exit gracefully)
22
+ echo "Starting MCP server with test input..."
23
+ timeout 5s node dist/server.js < /dev/null 2>&1 | head -20 || {
24
+ EXIT_CODE=$?
25
+ if [ $EXIT_CODE -eq 124 ]; then
26
+ echo "โœ… MCP Server: Started successfully (timeout = expected for stdio transport)"
27
+ elif [ $EXIT_CODE -eq 141 ]; then
28
+ echo "โœ… MCP Server: Started successfully (SIGPIPE = expected for stdio transport)"
29
+ else
30
+ echo "โŒ MCP Server: Failed to start (exit code: $EXIT_CODE)"
31
+ exit 1
32
+ fi
33
+ }
34
+
35
+ echo ""
36
+ echo "โœ… MCP Server test passed!"
37
+ echo ""
38
+ echo "Next step: Restart Claude Code to connect to the memory system"