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.
- package/.env.example +28 -0
- package/CHANGELOG.md +137 -0
- package/LICENSE +21 -0
- package/README.md +665 -0
- package/config/sqlite-schema.sql +413 -0
- package/dist/benchmark/index.d.ts +18 -0
- package/dist/benchmark/index.d.ts.map +1 -0
- package/dist/benchmark/index.js +19 -0
- package/dist/benchmark/index.js.map +1 -0
- package/dist/benchmark/metrics-collector.d.ts +90 -0
- package/dist/benchmark/metrics-collector.d.ts.map +1 -0
- package/dist/benchmark/metrics-collector.js +196 -0
- package/dist/benchmark/metrics-collector.js.map +1 -0
- package/dist/benchmark/report-generator.d.ts +35 -0
- package/dist/benchmark/report-generator.d.ts.map +1 -0
- package/dist/benchmark/report-generator.js +221 -0
- package/dist/benchmark/report-generator.js.map +1 -0
- package/dist/benchmark/runner.d.ts +30 -0
- package/dist/benchmark/runner.d.ts.map +1 -0
- package/dist/benchmark/runner.js +308 -0
- package/dist/benchmark/runner.js.map +1 -0
- package/dist/benchmark/scenarios/context-recall.d.ts +65 -0
- package/dist/benchmark/scenarios/context-recall.d.ts.map +1 -0
- package/dist/benchmark/scenarios/context-recall.js +180 -0
- package/dist/benchmark/scenarios/context-recall.js.map +1 -0
- package/dist/benchmark/scenarios/index.d.ts +34 -0
- package/dist/benchmark/scenarios/index.d.ts.map +1 -0
- package/dist/benchmark/scenarios/index.js +230 -0
- package/dist/benchmark/scenarios/index.js.map +1 -0
- package/dist/benchmark/statistics.d.ts +125 -0
- package/dist/benchmark/statistics.d.ts.map +1 -0
- package/dist/benchmark/statistics.js +520 -0
- package/dist/benchmark/statistics.js.map +1 -0
- package/dist/benchmark/types.d.ts +458 -0
- package/dist/benchmark/types.d.ts.map +1 -0
- package/dist/benchmark/types.js +297 -0
- package/dist/benchmark/types.js.map +1 -0
- package/dist/cli 2.js +583 -0
- package/dist/cli.d 2.ts +8 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.d.ts 2.map +1 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +583 -0
- package/dist/cli.js 2.map +1 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/rate-limits.d.ts +76 -0
- package/dist/config/rate-limits.d.ts.map +1 -0
- package/dist/config/rate-limits.js +161 -0
- package/dist/config/rate-limits.js.map +1 -0
- package/dist/config 2/rate-limits.d.ts +76 -0
- package/dist/config 2/rate-limits.d.ts.map +1 -0
- package/dist/config 2/rate-limits.js +161 -0
- package/dist/config 2/rate-limits.js.map +1 -0
- package/dist/consolidation/pipeline.d.ts +52 -0
- package/dist/consolidation/pipeline.d.ts.map +1 -0
- package/dist/consolidation/pipeline.js +193 -0
- package/dist/consolidation/pipeline.js.map +1 -0
- package/dist/consolidation 2/pipeline.d.ts +52 -0
- package/dist/consolidation 2/pipeline.d.ts.map +1 -0
- package/dist/consolidation 2/pipeline.js +193 -0
- package/dist/consolidation 2/pipeline.js.map +1 -0
- package/dist/embeddings/client.d.ts +95 -0
- package/dist/embeddings/client.d.ts.map +1 -0
- package/dist/embeddings/client.js +162 -0
- package/dist/embeddings/client.js.map +1 -0
- package/dist/embeddings 2/client.d.ts +94 -0
- package/dist/embeddings 2/client.d.ts.map +1 -0
- package/dist/embeddings 2/client.js +144 -0
- package/dist/embeddings 2/client.js.map +1 -0
- package/dist/memory-layers/semantic-memory.d.ts +76 -0
- package/dist/memory-layers/semantic-memory.d.ts.map +1 -0
- package/dist/memory-layers/semantic-memory.js +231 -0
- package/dist/memory-layers/semantic-memory.js.map +1 -0
- package/dist/memory-layers/skill-library.d.ts +25 -0
- package/dist/memory-layers/skill-library.d.ts.map +1 -0
- package/dist/memory-layers/skill-library.js +54 -0
- package/dist/memory-layers/skill-library.js.map +1 -0
- package/dist/memory-layers/working-memory.d.ts +57 -0
- package/dist/memory-layers/working-memory.d.ts.map +1 -0
- package/dist/memory-layers/working-memory.js +153 -0
- package/dist/memory-layers/working-memory.js.map +1 -0
- package/dist/memory-layers 2/semantic-memory.d.ts +33 -0
- package/dist/memory-layers 2/semantic-memory.d.ts.map +1 -0
- package/dist/memory-layers 2/semantic-memory.js +114 -0
- package/dist/memory-layers 2/semantic-memory.js.map +1 -0
- package/dist/memory-layers 2/skill-library.d.ts +25 -0
- package/dist/memory-layers 2/skill-library.d.ts.map +1 -0
- package/dist/memory-layers 2/skill-library.js +54 -0
- package/dist/memory-layers 2/skill-library.js.map +1 -0
- package/dist/memory-layers 2/working-memory.d.ts +57 -0
- package/dist/memory-layers 2/working-memory.d.ts.map +1 -0
- package/dist/memory-layers 2/working-memory.js +153 -0
- package/dist/memory-layers 2/working-memory.js.map +1 -0
- package/dist/middleware/auth.d.ts +287 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +610 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware 2/auth.d.ts +287 -0
- package/dist/middleware 2/auth.d.ts.map +1 -0
- package/dist/middleware 2/auth.js +610 -0
- package/dist/middleware 2/auth.js.map +1 -0
- package/dist/monitoring/health.d.ts +241 -0
- package/dist/monitoring/health.d.ts.map +1 -0
- package/dist/monitoring/health.js +649 -0
- package/dist/monitoring/health.js.map +1 -0
- package/dist/monitoring/metrics.d.ts +153 -0
- package/dist/monitoring/metrics.d.ts.map +1 -0
- package/dist/monitoring/metrics.js +349 -0
- package/dist/monitoring/metrics.js.map +1 -0
- package/dist/monitoring 2/health.d.ts +241 -0
- package/dist/monitoring 2/health.d.ts.map +1 -0
- package/dist/monitoring 2/health.js +649 -0
- package/dist/monitoring 2/health.js.map +1 -0
- package/dist/monitoring 2/metrics.d.ts +153 -0
- package/dist/monitoring 2/metrics.d.ts.map +1 -0
- package/dist/monitoring 2/metrics.js +349 -0
- package/dist/monitoring 2/metrics.js.map +1 -0
- package/dist/retrieval/hybrid-search.d.ts +186 -0
- package/dist/retrieval/hybrid-search.d.ts.map +1 -0
- package/dist/retrieval/hybrid-search.js +330 -0
- package/dist/retrieval/hybrid-search.js.map +1 -0
- package/dist/retrieval 2/hybrid-search.d.ts +186 -0
- package/dist/retrieval 2/hybrid-search.d.ts.map +1 -0
- package/dist/retrieval 2/hybrid-search.js +327 -0
- package/dist/retrieval 2/hybrid-search.js.map +1 -0
- package/dist/router/smart-router.d.ts +157 -0
- package/dist/router/smart-router.d.ts.map +1 -0
- package/dist/router/smart-router.js +524 -0
- package/dist/router/smart-router.js.map +1 -0
- package/dist/router 2/smart-router.d.ts +111 -0
- package/dist/router 2/smart-router.d.ts.map +1 -0
- package/dist/router 2/smart-router.js +383 -0
- package/dist/router 2/smart-router.js.map +1 -0
- package/dist/security/rate-limit-middleware.d.ts +119 -0
- package/dist/security/rate-limit-middleware.d.ts.map +1 -0
- package/dist/security/rate-limit-middleware.js +193 -0
- package/dist/security/rate-limit-middleware.js.map +1 -0
- package/dist/security 2/rate-limit-middleware.d.ts +119 -0
- package/dist/security 2/rate-limit-middleware.d.ts.map +1 -0
- package/dist/security 2/rate-limit-middleware.js +193 -0
- package/dist/security 2/rate-limit-middleware.js.map +1 -0
- package/dist/server 2.js +861 -0
- package/dist/server-wrapper 2.js +170 -0
- package/dist/server-wrapper.d 2.ts +12 -0
- package/dist/server-wrapper.d.ts +12 -0
- package/dist/server-wrapper.d.ts 2.map +1 -0
- package/dist/server-wrapper.d.ts.map +1 -0
- package/dist/server-wrapper.js +170 -0
- package/dist/server-wrapper.js 2.map +1 -0
- package/dist/server-wrapper.js.map +1 -0
- package/dist/server.d 2.ts +14 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts 2.map +1 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +656 -0
- package/dist/server.js 2.map +1 -0
- package/dist/server.js.map +1 -0
- package/dist/synthesis/conflict-detector.d.ts +43 -0
- package/dist/synthesis/conflict-detector.d.ts.map +1 -0
- package/dist/synthesis/conflict-detector.js +125 -0
- package/dist/synthesis/conflict-detector.js.map +1 -0
- package/dist/synthesis 2/conflict-detector.d.ts +43 -0
- package/dist/synthesis 2/conflict-detector.d.ts.map +1 -0
- package/dist/synthesis 2/conflict-detector.js +125 -0
- package/dist/synthesis 2/conflict-detector.js.map +1 -0
- package/dist/utils/monitoring.d.ts +25 -0
- package/dist/utils/monitoring.d.ts.map +1 -0
- package/dist/utils/monitoring.js +41 -0
- package/dist/utils/monitoring.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +97 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +166 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/validation.d.ts +139 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +195 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils 2/monitoring.d.ts +25 -0
- package/dist/utils 2/monitoring.d.ts.map +1 -0
- package/dist/utils 2/monitoring.js +41 -0
- package/dist/utils 2/monitoring.js.map +1 -0
- package/dist/utils 2/rate-limiter.d.ts +97 -0
- package/dist/utils 2/rate-limiter.d.ts.map +1 -0
- package/dist/utils 2/rate-limiter.js +166 -0
- package/dist/utils 2/rate-limiter.js.map +1 -0
- package/dist/utils 2/validation.d.ts +139 -0
- package/dist/utils 2/validation.d.ts.map +1 -0
- package/dist/utils 2/validation.js +195 -0
- package/dist/utils 2/validation.js.map +1 -0
- package/docker-compose.yml +65 -0
- package/embedding-service/Dockerfile +29 -0
- package/embedding-service/README.md +68 -0
- package/embedding-service/requirements.txt +6 -0
- package/embedding-service/server.py +110 -0
- package/package.json +92 -0
- package/scripts/FRESH_INSTALL.md +280 -0
- package/scripts/README.md +101 -0
- package/scripts/ensure-infrastructure.sh +38 -0
- package/scripts/fresh-install-auto.sh +96 -0
- package/scripts/fresh-install.sh +167 -0
- package/scripts/health-check.sh +72 -0
- package/scripts/init-qdrant.ts +201 -0
- package/scripts/postinstall.js +94 -0
- package/scripts/run-accuracy-benchmark.ts +1237 -0
- package/scripts/run-real-benchmark.ts +581 -0
- 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"
|