@tyvm/knowhow 0.0.48 → 0.0.49
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/package.json +1 -1
- package/src/agents/base/base.ts +9 -1
- package/src/chat/modules/AgentModule.ts +22 -3
- package/src/embeddings.ts +2 -4
- package/src/processors/CustomVariables.ts +48 -70
- package/src/processors/TokenCompressor.ts +5 -4
- package/src/processors/ToolResponseCache.ts +6 -9
- package/tests/processors/CustomVariables.test.ts +110 -55
- package/tests/processors/TokenCompressor.test.ts +48 -49
- package/tests/processors/ToolResponseCache.test.ts +309 -261
- package/ts_build/package.json +1 -1
- package/ts_build/src/agents/base/base.d.ts +5 -0
- package/ts_build/src/agents/base/base.js +3 -1
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.js +9 -1
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/embeddings.js +2 -4
- package/ts_build/src/embeddings.js.map +1 -1
- package/ts_build/src/processors/CustomVariables.js +36 -47
- package/ts_build/src/processors/CustomVariables.js.map +1 -1
- package/ts_build/src/processors/TokenCompressor.js +2 -2
- package/ts_build/src/processors/TokenCompressor.js.map +1 -1
- package/ts_build/src/processors/ToolResponseCache.js +6 -8
- package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
- package/ts_build/tests/processors/CustomVariables.test.js +41 -38
- package/ts_build/tests/processors/CustomVariables.test.js.map +1 -1
- package/ts_build/tests/processors/TokenCompressor.test.js +4 -5
- package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -1
- package/ts_build/tests/processors/ToolResponseCache.test.js +89 -78
- package/ts_build/tests/processors/ToolResponseCache.test.js.map +1 -1
|
@@ -8,11 +8,11 @@ describe("TokenCompressor", () => {
|
|
|
8
8
|
|
|
9
9
|
beforeEach(() => {
|
|
10
10
|
mockToolsService = {
|
|
11
|
-
|
|
11
|
+
addTools: jest.fn(),
|
|
12
12
|
addFunctions: jest.fn(),
|
|
13
13
|
getTool: jest.fn(),
|
|
14
14
|
} as any;
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
tokenCompressor = new TokenCompressor(mockToolsService);
|
|
17
17
|
});
|
|
18
18
|
|
|
@@ -22,17 +22,16 @@ describe("TokenCompressor", () => {
|
|
|
22
22
|
|
|
23
23
|
describe("constructor", () => {
|
|
24
24
|
it("should register expandTokens tool with ToolsService", () => {
|
|
25
|
-
expect(mockToolsService.
|
|
26
|
-
expect(mockToolsService.addTool).toHaveBeenCalled();
|
|
25
|
+
expect(mockToolsService.addTools).toHaveBeenCalled();
|
|
27
26
|
expect(mockToolsService.addFunctions).toHaveBeenCalled();
|
|
28
27
|
});
|
|
29
28
|
|
|
30
|
-
it("should
|
|
29
|
+
it("should overwrite tool if already exists", () => {
|
|
31
30
|
mockToolsService.getTool.mockReturnValue({ type: "function", function: { name: "expandTokens" } } as any);
|
|
32
31
|
const newCompressor = new TokenCompressor(mockToolsService);
|
|
33
|
-
|
|
32
|
+
|
|
34
33
|
// Should only be called once from the first instance
|
|
35
|
-
expect(mockToolsService.
|
|
34
|
+
expect(mockToolsService.addTools).toHaveBeenCalledTimes(2);
|
|
36
35
|
});
|
|
37
36
|
|
|
38
37
|
it("should work without ToolsService", () => {
|
|
@@ -50,7 +49,7 @@ describe("TokenCompressor", () => {
|
|
|
50
49
|
it("should compress long strings using chunking", () => {
|
|
51
50
|
const longContent = "x".repeat(40000); // Well above threshold (40k chars = 10k tokens > 8k maxTokens)
|
|
52
51
|
const result = tokenCompressor.compressContent(longContent);
|
|
53
|
-
|
|
52
|
+
|
|
54
53
|
expect(result).toContain("[COMPRESSED_STRING");
|
|
55
54
|
expect(result).toContain("Key:");
|
|
56
55
|
expect(result).toContain("expandTokens");
|
|
@@ -64,7 +63,7 @@ describe("TokenCompressor", () => {
|
|
|
64
63
|
};
|
|
65
64
|
const content = JSON.stringify(largeJson);
|
|
66
65
|
const result = tokenCompressor.compressContent(content);
|
|
67
|
-
|
|
66
|
+
|
|
68
67
|
// Should be parsed and compressed as JSON
|
|
69
68
|
const parsed = JSON.parse(result);
|
|
70
69
|
expect(typeof parsed.data).toBe("string");
|
|
@@ -79,7 +78,7 @@ describe("TokenCompressor", () => {
|
|
|
79
78
|
};
|
|
80
79
|
const content = JSON.stringify(complexJson);
|
|
81
80
|
const result = tokenCompressor.compressContent(content);
|
|
82
|
-
|
|
81
|
+
|
|
83
82
|
const parsed = JSON.parse(result);
|
|
84
83
|
expect(parsed.smallProp).toBe("small"); // Should remain unchanged
|
|
85
84
|
expect(parsed.largeProp).toContain("[COMPRESSED_JSON_PROPERTY"); // Should be compressed
|
|
@@ -96,11 +95,11 @@ describe("TokenCompressor", () => {
|
|
|
96
95
|
it("should create chunked compression for large strings", () => {
|
|
97
96
|
const content = "x".repeat(20000);
|
|
98
97
|
const result = tokenCompressor.compressStringInChunks(content);
|
|
99
|
-
|
|
98
|
+
|
|
100
99
|
expect(result).toContain("[COMPRESSED_STRING");
|
|
101
100
|
expect(result).toContain("chunks");
|
|
102
101
|
expect(result).toContain("Key:");
|
|
103
|
-
|
|
102
|
+
|
|
104
103
|
// Should have stored chunks in storage
|
|
105
104
|
expect(tokenCompressor.getStorageSize()).toBeGreaterThan(0);
|
|
106
105
|
});
|
|
@@ -108,10 +107,10 @@ describe("TokenCompressor", () => {
|
|
|
108
107
|
it("should create chain of NEXT_CHUNK_KEY references", () => {
|
|
109
108
|
const content = "a".repeat(20000);
|
|
110
109
|
tokenCompressor.compressStringInChunks(content);
|
|
111
|
-
|
|
110
|
+
|
|
112
111
|
const keys = tokenCompressor.getStorageKeys();
|
|
113
112
|
expect(keys.length).toBeGreaterThan(1);
|
|
114
|
-
|
|
113
|
+
|
|
115
114
|
// Check that chunks are linked
|
|
116
115
|
const firstChunk = tokenCompressor.retrieveString(keys[0]);
|
|
117
116
|
expect(firstChunk).toContain("NEXT_CHUNK_KEY");
|
|
@@ -123,7 +122,7 @@ describe("TokenCompressor", () => {
|
|
|
123
122
|
// Create array that will definitely create chunks > 4000 chars (50 elements × 200 chars = 10k+ chars per potential chunk)
|
|
124
123
|
const largeArray = new Array(15).fill("x".repeat(2200));
|
|
125
124
|
const result = tokenCompressor.compressJsonProperties(largeArray);
|
|
126
|
-
|
|
125
|
+
|
|
127
126
|
// Should contain compression markers
|
|
128
127
|
expect(JSON.stringify(result)).toContain("[COMPRESSED_JSON_ARRAY_CHUNK");
|
|
129
128
|
});
|
|
@@ -136,7 +135,7 @@ describe("TokenCompressor", () => {
|
|
|
136
135
|
}
|
|
137
136
|
}
|
|
138
137
|
};
|
|
139
|
-
|
|
138
|
+
|
|
140
139
|
const result = tokenCompressor.compressJsonProperties(nestedObj);
|
|
141
140
|
expect(JSON.stringify(result)).toContain("[COMPRESSED_JSON");
|
|
142
141
|
});
|
|
@@ -148,7 +147,7 @@ describe("TokenCompressor", () => {
|
|
|
148
147
|
{ data: "y".repeat(5000) }, // Large object
|
|
149
148
|
"another small item"
|
|
150
149
|
];
|
|
151
|
-
|
|
150
|
+
|
|
152
151
|
const result = tokenCompressor.compressJsonProperties(mixedArray);
|
|
153
152
|
expect(Array.isArray(result)).toBe(true);
|
|
154
153
|
});
|
|
@@ -166,9 +165,9 @@ describe("TokenCompressor", () => {
|
|
|
166
165
|
role: "user",
|
|
167
166
|
content: "x".repeat(20000)
|
|
168
167
|
};
|
|
169
|
-
|
|
168
|
+
|
|
170
169
|
await tokenCompressor.compressMessage(message);
|
|
171
|
-
|
|
170
|
+
|
|
172
171
|
expect(message.content).toContain("[COMPRESSED_STRING");
|
|
173
172
|
});
|
|
174
173
|
|
|
@@ -180,12 +179,12 @@ describe("TokenCompressor", () => {
|
|
|
180
179
|
{ type: "image_url", image_url: { url: "http://example.com/img.jpg" } }
|
|
181
180
|
]
|
|
182
181
|
};
|
|
183
|
-
|
|
182
|
+
|
|
184
183
|
await tokenCompressor.compressMessage(message);
|
|
185
|
-
|
|
184
|
+
|
|
186
185
|
const textContent = message.content[0] as { type: string; text: string };
|
|
187
186
|
expect(textContent.text).toContain("[COMPRESSED_STRING");
|
|
188
|
-
|
|
187
|
+
|
|
189
188
|
// Non-text content should remain unchanged
|
|
190
189
|
const imageContent = message.content[1] as { type: string; image_url: { url: string } };
|
|
191
190
|
expect(imageContent.image_url.url).toBe("http://example.com/img.jpg");
|
|
@@ -204,9 +203,9 @@ describe("TokenCompressor", () => {
|
|
|
204
203
|
{ role: "user", content: "x".repeat(20000) },
|
|
205
204
|
{ role: "assistant", content: "short response" }
|
|
206
205
|
];
|
|
207
|
-
|
|
206
|
+
|
|
208
207
|
await processor([], messages);
|
|
209
|
-
|
|
208
|
+
|
|
210
209
|
expect(messages[0].content).toContain("[COMPRESSED_STRING");
|
|
211
210
|
expect(messages[1].content).toBe("short response"); // Should remain short
|
|
212
211
|
});
|
|
@@ -219,9 +218,9 @@ describe("TokenCompressor", () => {
|
|
|
219
218
|
{ role: "user", content: "x".repeat(20000) },
|
|
220
219
|
{ role: "assistant", content: "x".repeat(20000) }
|
|
221
220
|
];
|
|
222
|
-
|
|
221
|
+
|
|
223
222
|
await processor([], messages);
|
|
224
|
-
|
|
223
|
+
|
|
225
224
|
expect(messages[0].content).toContain("[COMPRESSED_STRING");
|
|
226
225
|
expect(messages[1].content).not.toContain("[COMPRESSED_STRING");
|
|
227
226
|
});
|
|
@@ -231,10 +230,10 @@ describe("TokenCompressor", () => {
|
|
|
231
230
|
it("should store and retrieve strings", () => {
|
|
232
231
|
const key = "test_key";
|
|
233
232
|
const value = "test_value";
|
|
234
|
-
|
|
233
|
+
|
|
235
234
|
tokenCompressor.storeString(key, value);
|
|
236
235
|
const retrieved = tokenCompressor.retrieveString(key);
|
|
237
|
-
|
|
236
|
+
|
|
238
237
|
expect(retrieved).toBe(value);
|
|
239
238
|
});
|
|
240
239
|
|
|
@@ -246,9 +245,9 @@ describe("TokenCompressor", () => {
|
|
|
246
245
|
it("should clear all storage", () => {
|
|
247
246
|
tokenCompressor.storeString("key1", "value1");
|
|
248
247
|
tokenCompressor.storeString("key2", "value2");
|
|
249
|
-
|
|
248
|
+
|
|
250
249
|
expect(tokenCompressor.getStorageSize()).toBe(2);
|
|
251
|
-
|
|
250
|
+
|
|
252
251
|
tokenCompressor.clearStorage();
|
|
253
252
|
expect(tokenCompressor.getStorageSize()).toBe(0);
|
|
254
253
|
});
|
|
@@ -256,7 +255,7 @@ describe("TokenCompressor", () => {
|
|
|
256
255
|
it("should return storage keys", () => {
|
|
257
256
|
tokenCompressor.storeString("key1", "value1");
|
|
258
257
|
tokenCompressor.storeString("key2", "value2");
|
|
259
|
-
|
|
258
|
+
|
|
260
259
|
const keys = tokenCompressor.getStorageKeys();
|
|
261
260
|
expect(keys).toContain("key1");
|
|
262
261
|
expect(keys).toContain("key2");
|
|
@@ -266,23 +265,23 @@ describe("TokenCompressor", () => {
|
|
|
266
265
|
describe("configuration", () => {
|
|
267
266
|
it("should allow setting compression threshold", () => {
|
|
268
267
|
tokenCompressor.setCompressionThreshold(1000);
|
|
269
|
-
|
|
268
|
+
|
|
270
269
|
const content = "x".repeat(5000); // Above new threshold (5000 chars = 1250 tokens > 1000)
|
|
271
270
|
const result = tokenCompressor.compressContent(content);
|
|
272
|
-
|
|
271
|
+
|
|
273
272
|
expect(result).toContain("[COMPRESSED_STRING");
|
|
274
273
|
});
|
|
275
274
|
|
|
276
275
|
it("should adjust character limit with threshold", () => {
|
|
277
276
|
const originalThreshold = 4000;
|
|
278
277
|
const newThreshold = 2000;
|
|
279
|
-
|
|
278
|
+
|
|
280
279
|
tokenCompressor.setCompressionThreshold(newThreshold);
|
|
281
|
-
|
|
280
|
+
|
|
282
281
|
// Should compress at lower threshold
|
|
283
282
|
const content = "x".repeat(9000); // Above new threshold (9000 chars = 2250 tokens > 2000)
|
|
284
283
|
const result = tokenCompressor.compressContent(content);
|
|
285
|
-
|
|
284
|
+
|
|
286
285
|
expect(result).toContain("[COMPRESSED_STRING");
|
|
287
286
|
});
|
|
288
287
|
});
|
|
@@ -291,7 +290,7 @@ describe("TokenCompressor", () => {
|
|
|
291
290
|
it("should register expandTokens function correctly", () => {
|
|
292
291
|
const toolsServiceCalls = mockToolsService.addFunctions.mock.calls;
|
|
293
292
|
expect(toolsServiceCalls.length).toBe(1);
|
|
294
|
-
|
|
293
|
+
|
|
295
294
|
const functions = toolsServiceCalls[0][0];
|
|
296
295
|
expect(functions.expandTokens).toBeDefined();
|
|
297
296
|
expect(typeof functions.expandTokens).toBe("function");
|
|
@@ -301,11 +300,11 @@ describe("TokenCompressor", () => {
|
|
|
301
300
|
const key = "test_key";
|
|
302
301
|
const value = "test_value";
|
|
303
302
|
tokenCompressor.storeString(key, value);
|
|
304
|
-
|
|
303
|
+
|
|
305
304
|
const toolsServiceCalls = mockToolsService.addFunctions.mock.calls;
|
|
306
305
|
const functions = toolsServiceCalls[0][0];
|
|
307
306
|
const result = functions.expandTokens(key);
|
|
308
|
-
|
|
307
|
+
|
|
309
308
|
expect(result).toBe(value);
|
|
310
309
|
});
|
|
311
310
|
|
|
@@ -313,7 +312,7 @@ describe("TokenCompressor", () => {
|
|
|
313
312
|
const toolsServiceCalls = mockToolsService.addFunctions.mock.calls;
|
|
314
313
|
const functions = toolsServiceCalls[0][0];
|
|
315
314
|
const result = functions.expandTokens("non_existent");
|
|
316
|
-
|
|
315
|
+
|
|
317
316
|
expect(result).toContain("Error: No data found for key");
|
|
318
317
|
expect(result).toContain("Available keys:");
|
|
319
318
|
});
|
|
@@ -328,7 +327,7 @@ describe("TokenCompressor", () => {
|
|
|
328
327
|
it("should handle malformed JSON gracefully", () => {
|
|
329
328
|
const malformedJson = '{"incomplete": "json"'; // Missing closing brace
|
|
330
329
|
const result = tokenCompressor.compressContent(malformedJson);
|
|
331
|
-
|
|
330
|
+
|
|
332
331
|
// Should treat as string, not JSON
|
|
333
332
|
if (result.length > malformedJson.length) {
|
|
334
333
|
expect(result).toContain("[COMPRESSED_STRING");
|
|
@@ -340,13 +339,13 @@ describe("TokenCompressor", () => {
|
|
|
340
339
|
it("should handle very large objects without stack overflow", () => {
|
|
341
340
|
const deepObject = { level: 0 } as any;
|
|
342
341
|
let current = deepObject;
|
|
343
|
-
|
|
342
|
+
|
|
344
343
|
// Create deeply nested object
|
|
345
344
|
for (let i = 1; i < 100; i++) {
|
|
346
345
|
current.next = { level: i, data: "x".repeat(100) };
|
|
347
346
|
current = current.next;
|
|
348
347
|
}
|
|
349
|
-
|
|
348
|
+
|
|
350
349
|
expect(() => {
|
|
351
350
|
tokenCompressor.compressJsonProperties(deepObject);
|
|
352
351
|
}).not.toThrow();
|
|
@@ -355,11 +354,11 @@ describe("TokenCompressor", () => {
|
|
|
355
354
|
it("should handle circular references in JSON", () => {
|
|
356
355
|
const obj: any = { name: "test" };
|
|
357
356
|
obj.self = obj; // Create circular reference
|
|
358
|
-
|
|
357
|
+
|
|
359
358
|
expect(() => {
|
|
360
359
|
JSON.stringify(obj); // This should throw
|
|
361
360
|
}).toThrow();
|
|
362
|
-
|
|
361
|
+
|
|
363
362
|
// Our compressor should handle this gracefully by not receiving circular JSON
|
|
364
363
|
const safeObj = { name: "test", data: "x".repeat(10000) };
|
|
365
364
|
expect(() => {
|
|
@@ -372,9 +371,9 @@ describe("TokenCompressor", () => {
|
|
|
372
371
|
it("should handle large arrays efficiently", () => {
|
|
373
372
|
const largeArray = new Array(1000).fill("x".repeat(100));
|
|
374
373
|
const startTime = Date.now();
|
|
375
|
-
|
|
374
|
+
|
|
376
375
|
tokenCompressor.compressJsonProperties(largeArray);
|
|
377
|
-
|
|
376
|
+
|
|
378
377
|
const duration = Date.now() - startTime;
|
|
379
378
|
expect(duration).toBeLessThan(5000); // Should complete within 5 seconds
|
|
380
379
|
});
|
|
@@ -383,9 +382,9 @@ describe("TokenCompressor", () => {
|
|
|
383
382
|
const content = "small content";
|
|
384
383
|
const result1 = tokenCompressor.compressContent(content);
|
|
385
384
|
const result2 = tokenCompressor.compressContent(result1);
|
|
386
|
-
|
|
385
|
+
|
|
387
386
|
expect(result1).toBe(content);
|
|
388
387
|
expect(result2).toBe(content);
|
|
389
388
|
});
|
|
390
389
|
});
|
|
391
|
-
});
|
|
390
|
+
});
|