@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.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/src/agents/base/base.ts +9 -1
  3. package/src/chat/modules/AgentModule.ts +22 -3
  4. package/src/embeddings.ts +2 -4
  5. package/src/processors/CustomVariables.ts +48 -70
  6. package/src/processors/TokenCompressor.ts +5 -4
  7. package/src/processors/ToolResponseCache.ts +6 -9
  8. package/tests/processors/CustomVariables.test.ts +110 -55
  9. package/tests/processors/TokenCompressor.test.ts +48 -49
  10. package/tests/processors/ToolResponseCache.test.ts +309 -261
  11. package/ts_build/package.json +1 -1
  12. package/ts_build/src/agents/base/base.d.ts +5 -0
  13. package/ts_build/src/agents/base/base.js +3 -1
  14. package/ts_build/src/agents/base/base.js.map +1 -1
  15. package/ts_build/src/chat/modules/AgentModule.js +9 -1
  16. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  17. package/ts_build/src/embeddings.js +2 -4
  18. package/ts_build/src/embeddings.js.map +1 -1
  19. package/ts_build/src/processors/CustomVariables.js +36 -47
  20. package/ts_build/src/processors/CustomVariables.js.map +1 -1
  21. package/ts_build/src/processors/TokenCompressor.js +2 -2
  22. package/ts_build/src/processors/TokenCompressor.js.map +1 -1
  23. package/ts_build/src/processors/ToolResponseCache.js +6 -8
  24. package/ts_build/src/processors/ToolResponseCache.js.map +1 -1
  25. package/ts_build/tests/processors/CustomVariables.test.js +41 -38
  26. package/ts_build/tests/processors/CustomVariables.test.js.map +1 -1
  27. package/ts_build/tests/processors/TokenCompressor.test.js +4 -5
  28. package/ts_build/tests/processors/TokenCompressor.test.js.map +1 -1
  29. package/ts_build/tests/processors/ToolResponseCache.test.js +89 -78
  30. 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
- addTool: jest.fn(),
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.getTool).toHaveBeenCalledWith("expandTokens");
26
- expect(mockToolsService.addTool).toHaveBeenCalled();
25
+ expect(mockToolsService.addTools).toHaveBeenCalled();
27
26
  expect(mockToolsService.addFunctions).toHaveBeenCalled();
28
27
  });
29
28
 
30
- it("should not register tool if already exists", () => {
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.addTool).toHaveBeenCalledTimes(1);
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
+ });