lynkr 7.2.5 → 8.0.1
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/README.md +3 -3
- package/config/model-tiers.json +89 -0
- package/install.sh +6 -1
- package/package.json +4 -2
- package/scripts/setup.js +0 -1
- package/src/agents/executor.js +14 -6
- package/src/api/middleware/session.js +15 -2
- package/src/api/openai-router.js +162 -37
- package/src/api/providers-handler.js +15 -1
- package/src/api/router.js +107 -2
- package/src/budget/index.js +4 -3
- package/src/clients/databricks.js +431 -234
- package/src/clients/gpt-utils.js +181 -0
- package/src/clients/ollama-utils.js +66 -140
- package/src/clients/routing.js +0 -1
- package/src/clients/standard-tools.js +99 -3
- package/src/config/index.js +133 -35
- package/src/context/toon.js +173 -0
- package/src/logger/index.js +23 -0
- package/src/orchestrator/index.js +688 -213
- package/src/routing/agentic-detector.js +320 -0
- package/src/routing/complexity-analyzer.js +202 -2
- package/src/routing/cost-optimizer.js +305 -0
- package/src/routing/index.js +168 -159
- package/src/routing/model-tiers.js +365 -0
- package/src/server.js +4 -14
- package/src/sessions/cleanup.js +3 -3
- package/src/sessions/record.js +10 -1
- package/src/sessions/store.js +7 -2
- package/src/tools/agent-task.js +48 -1
- package/src/tools/index.js +19 -2
- package/src/tools/lazy-loader.js +7 -0
- package/src/tools/tinyfish.js +358 -0
- package/src/tools/truncate.js +1 -0
- package/.github/FUNDING.yml +0 -15
- package/.github/workflows/README.md +0 -215
- package/.github/workflows/ci.yml +0 -69
- package/.github/workflows/index.yml +0 -62
- package/.github/workflows/web-tools-tests.yml +0 -56
- package/CITATIONS.bib +0 -6
- package/CLAWROUTER_ROUTING_PLAN.md +0 -910
- package/DEPLOYMENT.md +0 -1001
- package/LYNKR-TUI-PLAN.md +0 -984
- package/PERFORMANCE-REPORT.md +0 -866
- package/PLAN-per-client-model-routing.md +0 -252
- package/ROUTER_COMPARISON.md +0 -173
- package/TIER_ROUTING_PLAN.md +0 -771
- package/docs/42642f749da6234f41b6b425c3bb07c9.txt +0 -1
- package/docs/BingSiteAuth.xml +0 -4
- package/docs/docs-style.css +0 -478
- package/docs/docs.html +0 -197
- package/docs/google5be250e608e6da39.html +0 -1
- package/docs/index.html +0 -577
- package/docs/index.md +0 -577
- package/docs/robots.txt +0 -4
- package/docs/sitemap.xml +0 -44
- package/docs/style.css +0 -1223
- package/documentation/README.md +0 -100
- package/documentation/api.md +0 -806
- package/documentation/claude-code-cli.md +0 -672
- package/documentation/codex-cli.md +0 -397
- package/documentation/contributing.md +0 -571
- package/documentation/cursor-integration.md +0 -731
- package/documentation/docker.md +0 -867
- package/documentation/embeddings.md +0 -760
- package/documentation/faq.md +0 -659
- package/documentation/features.md +0 -396
- package/documentation/headroom.md +0 -519
- package/documentation/installation.md +0 -706
- package/documentation/memory-system.md +0 -476
- package/documentation/production.md +0 -601
- package/documentation/providers.md +0 -906
- package/documentation/testing.md +0 -629
- package/documentation/token-optimization.md +0 -323
- package/documentation/tools.md +0 -697
- package/documentation/troubleshooting.md +0 -893
- package/final-test.js +0 -33
- package/headroom-sidecar/config.py +0 -93
- package/headroom-sidecar/requirements.txt +0 -14
- package/headroom-sidecar/server.py +0 -451
- package/monitor-agents.sh +0 -31
- package/scripts/audit-log-reader.js +0 -399
- package/scripts/compact-dictionary.js +0 -204
- package/scripts/test-deduplication.js +0 -448
- package/src/db/database.sqlite +0 -0
- package/test/README.md +0 -212
- package/test/azure-openai-config.test.js +0 -204
- package/test/azure-openai-error-resilience.test.js +0 -238
- package/test/azure-openai-format-conversion.test.js +0 -354
- package/test/azure-openai-integration.test.js +0 -281
- package/test/azure-openai-routing.test.js +0 -177
- package/test/azure-openai-streaming.test.js +0 -171
- package/test/bedrock-integration.test.js +0 -471
- package/test/comprehensive-test-suite.js +0 -928
- package/test/config-validation.test.js +0 -207
- package/test/cursor-integration.test.js +0 -484
- package/test/format-conversion.test.js +0 -578
- package/test/hybrid-routing-integration.test.js +0 -254
- package/test/hybrid-routing-performance.test.js +0 -418
- package/test/llamacpp-integration.test.js +0 -863
- package/test/lmstudio-integration.test.js +0 -335
- package/test/memory/extractor.test.js +0 -398
- package/test/memory/retriever.test.js +0 -613
- package/test/memory/retriever.test.js.bak +0 -585
- package/test/memory/search.test.js +0 -537
- package/test/memory/search.test.js.bak +0 -389
- package/test/memory/store.test.js +0 -344
- package/test/memory/store.test.js.bak +0 -312
- package/test/memory/surprise.test.js +0 -300
- package/test/memory-performance.test.js +0 -472
- package/test/openai-integration.test.js +0 -686
- package/test/openrouter-error-resilience.test.js +0 -418
- package/test/passthrough-mode.test.js +0 -385
- package/test/performance-benchmark.js +0 -351
- package/test/performance-tests.js +0 -528
- package/test/routing.test.js +0 -219
- package/test/web-tools.test.js +0 -329
- package/test-agents-simple.js +0 -43
- package/test-cli-connection.sh +0 -33
- package/test-learning-unit.js +0 -126
- package/test-learning.js +0 -112
- package/test-parallel-agents.sh +0 -124
- package/test-parallel-direct.js +0 -155
- package/test-subagents.sh +0 -117
|
@@ -1,448 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Test script for deduplication functionality
|
|
5
|
-
* Creates mock log entries and verifies deduplication works correctly
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const fs = require("fs");
|
|
9
|
-
const path = require("path");
|
|
10
|
-
const { ContentDeduplicator } = require("../src/logger/deduplicator");
|
|
11
|
-
|
|
12
|
-
// Test configuration
|
|
13
|
-
const TEST_DICT_PATH = path.join(process.cwd(), "logs", "test-dictionary.jsonl");
|
|
14
|
-
const TEST_LOG_PATH = path.join(process.cwd(), "logs", "test-audit.log");
|
|
15
|
-
|
|
16
|
-
// Clean up test files if they exist
|
|
17
|
-
function cleanup() {
|
|
18
|
-
if (fs.existsSync(TEST_DICT_PATH)) {
|
|
19
|
-
fs.unlinkSync(TEST_DICT_PATH);
|
|
20
|
-
}
|
|
21
|
-
if (fs.existsSync(TEST_LOG_PATH)) {
|
|
22
|
-
fs.unlinkSync(TEST_LOG_PATH);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Test 1: Basic deduplication
|
|
27
|
-
function testBasicDeduplication() {
|
|
28
|
-
console.log("\n=== Test 1: Basic Deduplication ===");
|
|
29
|
-
|
|
30
|
-
const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
|
|
31
|
-
minSize: 50, // Lower threshold for testing
|
|
32
|
-
cacheSize: 10,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const content1 = "This is a test content that is longer than 50 characters and should be deduplicated.";
|
|
36
|
-
const content2 = "This is a test content that is longer than 50 characters and should be deduplicated.";
|
|
37
|
-
const content3 = "Short";
|
|
38
|
-
|
|
39
|
-
// First content should be stored
|
|
40
|
-
const ref1 = deduplicator.storeContent(content1);
|
|
41
|
-
console.log("✓ Stored content1:", ref1);
|
|
42
|
-
|
|
43
|
-
// Second identical content should return same reference
|
|
44
|
-
const ref2 = deduplicator.storeContent(content2);
|
|
45
|
-
console.log("✓ Stored content2 (should be same hash):", ref2);
|
|
46
|
-
|
|
47
|
-
// Verify same hash
|
|
48
|
-
if (ref1.$ref !== ref2.$ref) {
|
|
49
|
-
console.error("✗ FAIL: Different hashes for identical content!");
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
console.log("✓ PASS: Identical content produces same hash");
|
|
53
|
-
|
|
54
|
-
// Short content should not be deduplicated (below threshold)
|
|
55
|
-
const shouldNotDedup = deduplicator.shouldDeduplicate(content3, 50);
|
|
56
|
-
if (shouldNotDedup) {
|
|
57
|
-
console.error("✗ FAIL: Short content should not be deduplicated!");
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
console.log("✓ PASS: Short content not deduplicated");
|
|
61
|
-
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Test 2: Content restoration
|
|
66
|
-
function testContentRestoration() {
|
|
67
|
-
console.log("\n=== Test 2: Content Restoration ===");
|
|
68
|
-
|
|
69
|
-
const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
|
|
70
|
-
minSize: 50,
|
|
71
|
-
cacheSize: 10,
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const originalContent = "This is original content that needs to be restored from the dictionary file.";
|
|
75
|
-
|
|
76
|
-
// Store and get reference
|
|
77
|
-
const ref = deduplicator.storeContent(originalContent);
|
|
78
|
-
console.log("✓ Stored content with ref:", ref.$ref);
|
|
79
|
-
|
|
80
|
-
// Retrieve content
|
|
81
|
-
const retrieved = deduplicator.getContent(ref.$ref);
|
|
82
|
-
console.log("✓ Retrieved content length:", retrieved?.length);
|
|
83
|
-
|
|
84
|
-
// Verify content matches
|
|
85
|
-
if (retrieved !== originalContent) {
|
|
86
|
-
console.error("✗ FAIL: Retrieved content doesn't match original!");
|
|
87
|
-
console.error("Expected:", originalContent);
|
|
88
|
-
console.error("Got:", retrieved);
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
console.log("✓ PASS: Content restored correctly");
|
|
92
|
-
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Test 3: Entry deduplication and restoration
|
|
97
|
-
function testEntryProcessing() {
|
|
98
|
-
console.log("\n=== Test 3: Entry Deduplication and Restoration ===");
|
|
99
|
-
|
|
100
|
-
const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
|
|
101
|
-
minSize: 50,
|
|
102
|
-
cacheSize: 10,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const systemPrompt = "You are a helpful AI assistant. This is a long system prompt that should be deduplicated.";
|
|
106
|
-
const userMessage = "This is a user message that is long enough to be deduplicated by the deduplication system.";
|
|
107
|
-
|
|
108
|
-
const entry = {
|
|
109
|
-
type: "llm_request",
|
|
110
|
-
correlationId: "test-123",
|
|
111
|
-
systemPrompt: systemPrompt,
|
|
112
|
-
userMessages: userMessage,
|
|
113
|
-
model: "test-model",
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
// Deduplicate entry
|
|
117
|
-
const deduplicated = deduplicator.deduplicateEntry(entry, ["systemPrompt", "userMessages"]);
|
|
118
|
-
console.log("✓ Deduplicated entry:", JSON.stringify(deduplicated, null, 2));
|
|
119
|
-
|
|
120
|
-
// Verify fields are now references
|
|
121
|
-
if (typeof deduplicated.systemPrompt !== "object" || !deduplicated.systemPrompt.$ref) {
|
|
122
|
-
console.error("✗ FAIL: systemPrompt was not deduplicated!");
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
if (typeof deduplicated.userMessages !== "object" || !deduplicated.userMessages.$ref) {
|
|
126
|
-
console.error("✗ FAIL: userMessages was not deduplicated!");
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
console.log("✓ PASS: Fields converted to references");
|
|
130
|
-
|
|
131
|
-
// Restore entry
|
|
132
|
-
const restored = deduplicator.restoreEntry(deduplicated);
|
|
133
|
-
console.log("✓ Restored entry keys:", Object.keys(restored));
|
|
134
|
-
|
|
135
|
-
// Verify restoration
|
|
136
|
-
if (restored.systemPrompt !== systemPrompt) {
|
|
137
|
-
console.error("✗ FAIL: systemPrompt not restored correctly!");
|
|
138
|
-
console.error("Expected:", systemPrompt);
|
|
139
|
-
console.error("Got:", restored.systemPrompt);
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
if (restored.userMessages !== userMessage) {
|
|
143
|
-
console.error("✗ FAIL: userMessages not restored correctly!");
|
|
144
|
-
console.error("Expected:", userMessage);
|
|
145
|
-
console.error("Got:", restored.userMessages);
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
console.log("✓ PASS: Entry restored correctly");
|
|
149
|
-
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Test 4: Dictionary persistence
|
|
154
|
-
function testDictionaryPersistence() {
|
|
155
|
-
console.log("\n=== Test 4: Dictionary Persistence ===");
|
|
156
|
-
|
|
157
|
-
// Create first deduplicator and store content
|
|
158
|
-
const deduplicator1 = new ContentDeduplicator(TEST_DICT_PATH, {
|
|
159
|
-
minSize: 50,
|
|
160
|
-
cacheSize: 10,
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const content = "This is test content for persistence verification across deduplicator instances.";
|
|
164
|
-
const ref = deduplicator1.storeContent(content);
|
|
165
|
-
console.log("✓ Stored content with first deduplicator:", ref.$ref);
|
|
166
|
-
|
|
167
|
-
// Wait for async write to complete
|
|
168
|
-
setTimeout(() => {
|
|
169
|
-
// Create second deduplicator (should load from dictionary)
|
|
170
|
-
const deduplicator2 = new ContentDeduplicator(TEST_DICT_PATH, {
|
|
171
|
-
minSize: 50,
|
|
172
|
-
cacheSize: 10,
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// Try to retrieve with second deduplicator
|
|
176
|
-
const retrieved = deduplicator2.getContent(ref.$ref);
|
|
177
|
-
|
|
178
|
-
if (retrieved !== content) {
|
|
179
|
-
console.error("✗ FAIL: Content not persisted to dictionary!");
|
|
180
|
-
console.error("Expected:", content);
|
|
181
|
-
console.error("Got:", retrieved);
|
|
182
|
-
return false;
|
|
183
|
-
}
|
|
184
|
-
console.log("✓ PASS: Dictionary persisted and loaded correctly");
|
|
185
|
-
|
|
186
|
-
// Show dictionary stats
|
|
187
|
-
const stats = deduplicator2.getStats();
|
|
188
|
-
console.log("\nDeduplication Stats:");
|
|
189
|
-
console.log(` Cache size: ${stats.cacheSize}`);
|
|
190
|
-
console.log(` Unique blocks: ${stats.uniqueContentBlocks}`);
|
|
191
|
-
console.log(` Total references: ${stats.totalReferences}`);
|
|
192
|
-
|
|
193
|
-
return true;
|
|
194
|
-
}, 100);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Test 5: Size calculation and verification
|
|
198
|
-
function testSizeCalculation() {
|
|
199
|
-
console.log("\n=== Test 5: Size Calculation ===");
|
|
200
|
-
|
|
201
|
-
const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
|
|
202
|
-
minSize: 50,
|
|
203
|
-
cacheSize: 10,
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
const content = "This is a test content string that will be deduplicated and have its size calculated.";
|
|
207
|
-
const ref = deduplicator.storeContent(content);
|
|
208
|
-
|
|
209
|
-
console.log("✓ Content length:", content.length);
|
|
210
|
-
console.log("✓ Reference size field:", ref.size);
|
|
211
|
-
|
|
212
|
-
if (ref.size !== content.length) {
|
|
213
|
-
console.error("✗ FAIL: Size mismatch!");
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
console.log("✓ PASS: Size calculated correctly");
|
|
217
|
-
|
|
218
|
-
// Calculate space saved
|
|
219
|
-
const refSize = JSON.stringify(ref).length;
|
|
220
|
-
const originalSize = content.length;
|
|
221
|
-
const saved = originalSize - refSize;
|
|
222
|
-
const savedPercent = ((saved / originalSize) * 100).toFixed(1);
|
|
223
|
-
|
|
224
|
-
console.log(`\nSpace saved: ${saved} bytes (${savedPercent}%)`);
|
|
225
|
-
console.log(` Original: ${originalSize} bytes`);
|
|
226
|
-
console.log(` Reference: ${refSize} bytes`);
|
|
227
|
-
|
|
228
|
-
return true;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Test 6: Content sanitization (empty User: entries removal)
|
|
232
|
-
function testContentSanitization() {
|
|
233
|
-
console.log("\n=== Test 6: Content Sanitization (Empty User: Removal) ===");
|
|
234
|
-
|
|
235
|
-
const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
|
|
236
|
-
minSize: 50,
|
|
237
|
-
cacheSize: 10,
|
|
238
|
-
sanitize: true, // Enable sanitization
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
// Content with multiple empty "User:" entries
|
|
242
|
-
const dirtyContent = `Claude: I'll implement...
|
|
243
|
-
|
|
244
|
-
User:
|
|
245
|
-
|
|
246
|
-
Claude: Now I'll implement...
|
|
247
|
-
|
|
248
|
-
User:
|
|
249
|
-
|
|
250
|
-
User:
|
|
251
|
-
|
|
252
|
-
User:
|
|
253
|
-
|
|
254
|
-
Respond with the title for the conversation and nothing else.`;
|
|
255
|
-
|
|
256
|
-
console.log("✓ Original content length:", dirtyContent.length);
|
|
257
|
-
console.log("✓ Empty 'User:' entries in original:", (dirtyContent.match(/User:\s*\n/g) || []).length);
|
|
258
|
-
|
|
259
|
-
// Store the content (should be sanitized internally)
|
|
260
|
-
const ref = deduplicator.storeContent(dirtyContent);
|
|
261
|
-
console.log("✓ Stored content with ref:", ref.$ref);
|
|
262
|
-
|
|
263
|
-
// Retrieve it back
|
|
264
|
-
const retrieved = deduplicator.getContent(ref.$ref);
|
|
265
|
-
console.log("✓ Retrieved content length:", retrieved?.length);
|
|
266
|
-
|
|
267
|
-
// Count empty "User:" entries in retrieved content
|
|
268
|
-
// Pattern: "User:" followed by newline(s) and then "Claude:" or another "User:" or end
|
|
269
|
-
const emptyUserMatches = retrieved.match(/User:\s*\n+(?=(Claude:|User:|$))/g) || [];
|
|
270
|
-
console.log("✓ Empty 'User:' entries in retrieved:", emptyUserMatches.length);
|
|
271
|
-
|
|
272
|
-
// Verify empty User: entries were removed
|
|
273
|
-
if (emptyUserMatches.length > 0) {
|
|
274
|
-
console.error("✗ FAIL: Empty User: entries not removed!");
|
|
275
|
-
console.error("Retrieved content:", retrieved);
|
|
276
|
-
return false;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Verify content still contains Claude: entries
|
|
280
|
-
if (!retrieved.includes("Claude:")) {
|
|
281
|
-
console.error("✗ FAIL: Claude: entries were incorrectly removed!");
|
|
282
|
-
return false;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Verify the last line is preserved
|
|
286
|
-
if (!retrieved.includes("Respond with the title")) {
|
|
287
|
-
console.error("✗ FAIL: Content was over-sanitized!");
|
|
288
|
-
return false;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
console.log("✓ PASS: Empty User: entries removed, content preserved");
|
|
292
|
-
|
|
293
|
-
// Test with sanitization disabled
|
|
294
|
-
const dedupNoSanitize = new ContentDeduplicator(TEST_DICT_PATH, {
|
|
295
|
-
minSize: 50,
|
|
296
|
-
cacheSize: 10,
|
|
297
|
-
sanitize: false, // Disable sanitization
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
const refNoSanitize = dedupNoSanitize.storeContent(dirtyContent);
|
|
301
|
-
const retrievedNoSanitize = dedupNoSanitize.getContent(refNoSanitize.$ref);
|
|
302
|
-
|
|
303
|
-
// Should have empty User: entries when sanitization is disabled
|
|
304
|
-
const emptyUserNoSanitize = retrievedNoSanitize.match(/User:\s*\n+(?=(Claude:|User:|$))/g) || [];
|
|
305
|
-
if (emptyUserNoSanitize.length === 0) {
|
|
306
|
-
console.error("✗ FAIL: Content was sanitized even with sanitize=false!");
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
console.log("✓ PASS: Sanitization can be disabled");
|
|
311
|
-
|
|
312
|
-
return true;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Test 7: Content sanitization preserves non-empty User: entries
|
|
316
|
-
function testSanitizationPreservesContent() {
|
|
317
|
-
console.log("\n=== Test 7: Sanitization Preserves Non-Empty User: Entries ===");
|
|
318
|
-
|
|
319
|
-
const deduplicator = new ContentDeduplicator(TEST_DICT_PATH, {
|
|
320
|
-
minSize: 50,
|
|
321
|
-
cacheSize: 10,
|
|
322
|
-
sanitize: true,
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
// Content with both empty and non-empty User: entries
|
|
326
|
-
const mixedContent = `Claude: I'll help you.
|
|
327
|
-
|
|
328
|
-
User: Can you explain this?
|
|
329
|
-
|
|
330
|
-
Claude: Sure, here's the explanation.
|
|
331
|
-
|
|
332
|
-
User:
|
|
333
|
-
|
|
334
|
-
User: Another question here.
|
|
335
|
-
|
|
336
|
-
Claude: Here's the answer.`;
|
|
337
|
-
|
|
338
|
-
console.log("✓ Original has both empty and non-empty User: entries");
|
|
339
|
-
|
|
340
|
-
const ref = deduplicator.storeContent(mixedContent);
|
|
341
|
-
const retrieved = deduplicator.getContent(ref.$ref);
|
|
342
|
-
|
|
343
|
-
// Check that non-empty User: entries are preserved
|
|
344
|
-
if (!retrieved.includes("User: Can you explain this?")) {
|
|
345
|
-
console.error("✗ FAIL: Non-empty User: entry was removed!");
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (!retrieved.includes("User: Another question here.")) {
|
|
350
|
-
console.error("✗ FAIL: Non-empty User: entry was removed!");
|
|
351
|
-
return false;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Check that empty User: entries are removed
|
|
355
|
-
const lines = retrieved.split('\n');
|
|
356
|
-
let hasEmptyUser = false;
|
|
357
|
-
for (let i = 0; i < lines.length; i++) {
|
|
358
|
-
const line = lines[i].trim();
|
|
359
|
-
if (line === 'User:' || line === 'User: ') {
|
|
360
|
-
const nextLine = i + 1 < lines.length ? lines[i + 1].trim() : '';
|
|
361
|
-
if (nextLine === '' || nextLine === 'Claude:' || nextLine === 'User:') {
|
|
362
|
-
hasEmptyUser = true;
|
|
363
|
-
break;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (hasEmptyUser) {
|
|
369
|
-
console.error("✗ FAIL: Empty User: entries not removed from mixed content!");
|
|
370
|
-
return false;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
console.log("✓ PASS: Non-empty User: entries preserved, empty ones removed");
|
|
374
|
-
|
|
375
|
-
return true;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Main test runner
|
|
379
|
-
async function runTests() {
|
|
380
|
-
console.log("=".repeat(60));
|
|
381
|
-
console.log("LLM Audit Log Deduplication Test Suite");
|
|
382
|
-
console.log("=".repeat(60));
|
|
383
|
-
|
|
384
|
-
// Clean up before tests
|
|
385
|
-
cleanup();
|
|
386
|
-
|
|
387
|
-
const tests = [
|
|
388
|
-
testBasicDeduplication,
|
|
389
|
-
testContentRestoration,
|
|
390
|
-
testEntryProcessing,
|
|
391
|
-
testSizeCalculation,
|
|
392
|
-
testContentSanitization,
|
|
393
|
-
testSanitizationPreservesContent,
|
|
394
|
-
];
|
|
395
|
-
|
|
396
|
-
let passed = 0;
|
|
397
|
-
let failed = 0;
|
|
398
|
-
|
|
399
|
-
for (const test of tests) {
|
|
400
|
-
try {
|
|
401
|
-
const result = test();
|
|
402
|
-
if (result) {
|
|
403
|
-
passed++;
|
|
404
|
-
} else {
|
|
405
|
-
failed++;
|
|
406
|
-
}
|
|
407
|
-
} catch (err) {
|
|
408
|
-
console.error(`✗ Test failed with error: ${err.message}`);
|
|
409
|
-
console.error(err.stack);
|
|
410
|
-
failed++;
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
// Run async test separately
|
|
415
|
-
setTimeout(() => {
|
|
416
|
-
testDictionaryPersistence();
|
|
417
|
-
|
|
418
|
-
console.log("\n" + "=".repeat(60));
|
|
419
|
-
console.log("Test Results");
|
|
420
|
-
console.log("=".repeat(60));
|
|
421
|
-
console.log(`Passed: ${passed}/${passed + failed}`);
|
|
422
|
-
console.log(`Failed: ${failed}/${passed + failed}`);
|
|
423
|
-
|
|
424
|
-
if (failed === 0) {
|
|
425
|
-
console.log("\n✓ All tests passed!");
|
|
426
|
-
console.log("\nDictionary file created at:", TEST_DICT_PATH);
|
|
427
|
-
console.log("You can inspect it with: cat", TEST_DICT_PATH);
|
|
428
|
-
} else {
|
|
429
|
-
console.log("\n✗ Some tests failed!");
|
|
430
|
-
process.exit(1);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Clean up after tests
|
|
434
|
-
console.log("\nCleaning up test files...");
|
|
435
|
-
cleanup();
|
|
436
|
-
console.log("✓ Test files removed");
|
|
437
|
-
}, 200);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
// Run tests
|
|
441
|
-
if (require.main === module) {
|
|
442
|
-
runTests().catch((err) => {
|
|
443
|
-
console.error("Test suite failed:", err);
|
|
444
|
-
process.exit(1);
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
module.exports = { runTests };
|
package/src/db/database.sqlite
DELETED
|
File without changes
|
package/test/README.md
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
# Test Suite Documentation
|
|
2
|
-
|
|
3
|
-
All tests for the Lynkr project are consolidated in this `test/` directory.
|
|
4
|
-
|
|
5
|
-
## Test Files
|
|
6
|
-
|
|
7
|
-
### Unit Tests
|
|
8
|
-
**File**: `routing.test.js`
|
|
9
|
-
**Purpose**: Tests the hybrid routing logic in isolation
|
|
10
|
-
**Run**: `DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/routing.test.js`
|
|
11
|
-
**Coverage**: 10 tests
|
|
12
|
-
- Routing with PREFER_OLLAMA disabled
|
|
13
|
-
- Simple requests → Ollama
|
|
14
|
-
- Complex requests → Cloud
|
|
15
|
-
- Tool capability checks
|
|
16
|
-
- Fallback configuration
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
### Integration Tests
|
|
21
|
-
**File**: `hybrid-routing-integration.test.js`
|
|
22
|
-
**Purpose**: Tests configuration validation and metrics recording
|
|
23
|
-
**Run**: `node --test test/hybrid-routing-integration.test.js`
|
|
24
|
-
**Coverage**: 13 tests
|
|
25
|
-
- Configuration validation (5 tests)
|
|
26
|
-
- Metrics recording (6 tests)
|
|
27
|
-
- Helper functions (2 tests)
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
### Hybrid Routing Performance Tests
|
|
32
|
-
**File**: `hybrid-routing-performance.test.js`
|
|
33
|
-
**Purpose**: Measures performance overhead of hybrid routing
|
|
34
|
-
**Run**: `node test/hybrid-routing-performance.test.js`
|
|
35
|
-
**Key Metrics**:
|
|
36
|
-
- Routing decision: <0.01ms (36.8M decisions/sec)
|
|
37
|
-
- Metrics overhead: <0.01ms (43.6M ops/sec)
|
|
38
|
-
- Combined overhead: <0.02ms (15.6M ops/sec)
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
### System Performance Tests
|
|
43
|
-
**File**: `performance-tests.js`
|
|
44
|
-
**Purpose**: Tests system-wide performance optimizations
|
|
45
|
-
**Run**: `DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-tests.js`
|
|
46
|
-
**Coverage**:
|
|
47
|
-
- Database indexes (100% complete)
|
|
48
|
-
- Persistent prompt cache
|
|
49
|
-
- Regex pattern caching (4.5x faster)
|
|
50
|
-
- Lazy loading
|
|
51
|
-
- HTTP connection pooling
|
|
52
|
-
- Response compression
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
### Middleware Benchmarks
|
|
57
|
-
**File**: `performance-benchmark.js`
|
|
58
|
-
**Purpose**: Benchmarks middleware overhead
|
|
59
|
-
**Run**: `DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-benchmark.js`
|
|
60
|
-
**Coverage**:
|
|
61
|
-
- Metrics collection (3.4M ops/sec)
|
|
62
|
-
- Circuit breakers (3.9M ops/sec)
|
|
63
|
-
- Input validation (5.7M ops/sec)
|
|
64
|
-
- Load shedding
|
|
65
|
-
- Combined middleware stack
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
## Running All Tests
|
|
70
|
-
|
|
71
|
-
### Using npm scripts (Recommended)
|
|
72
|
-
|
|
73
|
-
**Run all tests (unit + performance):**
|
|
74
|
-
```bash
|
|
75
|
-
npm test
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
**Run only unit/integration tests:**
|
|
79
|
-
```bash
|
|
80
|
-
npm run test:unit
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
**Run only performance tests:**
|
|
84
|
-
```bash
|
|
85
|
-
npm run test:performance
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
**Run only benchmarks:**
|
|
89
|
-
```bash
|
|
90
|
-
npm run test:benchmark
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
**Run quick smoke test (routing only):**
|
|
94
|
-
```bash
|
|
95
|
-
npm run test:quick
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
**Run everything including benchmarks:**
|
|
99
|
-
```bash
|
|
100
|
-
npm run test:all
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### Manual execution (if needed)
|
|
104
|
-
|
|
105
|
-
**Quick Test:**
|
|
106
|
-
Run all unit and integration tests:
|
|
107
|
-
```bash
|
|
108
|
-
DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
**Full Test Suite:**
|
|
112
|
-
Run everything including performance tests:
|
|
113
|
-
```bash
|
|
114
|
-
# Unit + Integration tests
|
|
115
|
-
DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/routing.test.js
|
|
116
|
-
DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/hybrid-routing-integration.test.js
|
|
117
|
-
|
|
118
|
-
# Performance tests
|
|
119
|
-
node test/hybrid-routing-performance.test.js
|
|
120
|
-
DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-tests.js
|
|
121
|
-
DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-benchmark.js
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## Test Organization
|
|
127
|
-
|
|
128
|
-
```
|
|
129
|
-
test/
|
|
130
|
-
├── README.md ← This file
|
|
131
|
-
├── routing.test.js ← Unit tests (10 tests)
|
|
132
|
-
├── hybrid-routing-integration.test.js ← Integration tests (13 tests)
|
|
133
|
-
├── hybrid-routing-performance.test.js ← Routing performance benchmarks
|
|
134
|
-
├── performance-tests.js ← System performance tests
|
|
135
|
-
└── performance-benchmark.js ← Middleware benchmarks
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Important Notes
|
|
141
|
-
|
|
142
|
-
### Not Test Files
|
|
143
|
-
- `src/tests/` - This is **application code**, not tests! It provides test execution functionality as a feature.
|
|
144
|
-
|
|
145
|
-
### Environment Variables
|
|
146
|
-
Most tests require Databricks credentials (even though they're not used in actual API calls):
|
|
147
|
-
```bash
|
|
148
|
-
export DATABRICKS_API_KEY=test-key
|
|
149
|
-
export DATABRICKS_API_BASE=http://test.com
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Test Results Summary
|
|
153
|
-
|
|
154
|
-
| Test Type | Status | Count |
|
|
155
|
-
|-----------|--------|-------|
|
|
156
|
-
| Unit Tests | ✅ Passing | 10/10 |
|
|
157
|
-
| Integration Tests | ✅ Passing | 13/13 |
|
|
158
|
-
| Routing Performance | ✅ Complete | <0.02ms overhead |
|
|
159
|
-
| System Performance | ✅ Complete | 100% optimizations |
|
|
160
|
-
| Middleware Benchmarks | ✅ Complete | Acceptable overhead |
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
## CI/CD Integration
|
|
165
|
-
|
|
166
|
-
To run in CI/CD pipelines:
|
|
167
|
-
|
|
168
|
-
```bash
|
|
169
|
-
#!/bin/bash
|
|
170
|
-
set -e
|
|
171
|
-
|
|
172
|
-
# Run all tests using npm
|
|
173
|
-
echo "Running test suite..."
|
|
174
|
-
npm run test:all
|
|
175
|
-
|
|
176
|
-
echo "All tests passed!"
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
Or for a faster CI pipeline (skip benchmarks):
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
#!/bin/bash
|
|
183
|
-
set -e
|
|
184
|
-
|
|
185
|
-
echo "Running tests..."
|
|
186
|
-
npm test
|
|
187
|
-
|
|
188
|
-
echo "Tests passed!"
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
## Adding New Tests
|
|
194
|
-
|
|
195
|
-
When adding new tests, follow these conventions:
|
|
196
|
-
|
|
197
|
-
1. **Unit tests**: Test individual functions in isolation
|
|
198
|
-
- Place in `test/`
|
|
199
|
-
- Use `node:test` framework
|
|
200
|
-
- Name: `feature-name.test.js`
|
|
201
|
-
|
|
202
|
-
2. **Integration tests**: Test multiple components together
|
|
203
|
-
- Place in `test/`
|
|
204
|
-
- Use `node:test` framework
|
|
205
|
-
- Name: `feature-name-integration.test.js`
|
|
206
|
-
|
|
207
|
-
3. **Performance tests**: Benchmark specific features
|
|
208
|
-
- Place in `test/`
|
|
209
|
-
- Use custom benchmark utilities
|
|
210
|
-
- Name: `feature-name-performance.test.js`
|
|
211
|
-
|
|
212
|
-
4. **Always**: Document in this README!
|