@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
@@ -1,135 +1,148 @@
1
1
  import { Message } from "../../src/clients/types";
2
- import { ToolResponseCache, jqToolResponseDefinition } from "../../src/processors/ToolResponseCache";
2
+ import {
3
+ ToolResponseCache,
4
+ jqToolResponseDefinition,
5
+ } from "../../src/processors/ToolResponseCache";
3
6
  import { ToolsService } from "../../src/services";
4
7
 
5
8
  // Mock node-jq
6
9
  jest.mock("node-jq", () => ({
7
- run: jest.fn().mockImplementation(async (query: string, data: any, options: any) => {
8
- // Simulate common JQ queries based on the test data
9
-
10
- // Handle .test query on {"test": "value"}
11
- if (query === ".test") {
12
- if (data && data.test !== undefined) {
13
- return JSON.stringify(data.test);
10
+ run: jest
11
+ .fn()
12
+ .mockImplementation(async (query: string, data: any, options: any) => {
13
+ // Simulate common JQ queries based on the test data
14
+
15
+ // Handle .test query on {"test": "value"}
16
+ if (query === ".test") {
17
+ if (data && data.test !== undefined) {
18
+ return JSON.stringify(data.test);
19
+ }
20
+ }
21
+
22
+ // Handle .id query for extracting id values
23
+ if (query === ".id") {
24
+ return data && data.id !== undefined ? data.id.toString() : "null";
25
+ }
26
+
27
+ // Handle .data | length query for counting array elements
28
+ if (query === ".data | length") {
29
+ if (data && Array.isArray(data.data)) {
30
+ return data.data.length.toString();
31
+ }
32
+ }
33
+
34
+ // Handle .data | add query for summing array elements
35
+ if (query === ".data | add") {
36
+ if (data && Array.isArray(data.data)) {
37
+ const sum = data.data.reduce((a, b) => a + b, 0);
38
+ return sum.toString();
39
+ }
40
+ }
41
+
42
+ // Handle .unicode query for special characters test
43
+ if (query === ".unicode") {
44
+ if (data && data.unicode !== undefined) {
45
+ return JSON.stringify(data.unicode);
46
+ }
47
+ }
48
+
49
+ // Handle .empty query for empty string test
50
+ if (query === ".empty") {
51
+ if (data && data.empty !== undefined) {
52
+ return JSON.stringify(data.empty);
53
+ }
14
54
  }
15
- }
16
-
17
- // Handle .id query for extracting id values
18
- if (query === ".id") {
19
- return data && data.id !== undefined ? data.id.toString() : "null";
20
- }
21
-
22
- // Handle .data | length query for counting array elements
23
- if (query === ".data | length") {
24
- if (data && Array.isArray(data.data)) {
25
- return data.data.length.toString();
55
+
56
+ // Handle .nullValue query for null value test
57
+ if (query === ".nullValue") {
58
+ if (data && data.nullValue !== undefined) {
59
+ return JSON.stringify(data.nullValue);
60
+ }
26
61
  }
27
- }
28
-
29
- // Handle .data | add query for summing array elements
30
- if (query === ".data | add") {
31
- if (data && Array.isArray(data.data)) {
32
- const sum = data.data.reduce((a, b) => a + b, 0);
33
- return sum.toString();
62
+
63
+ // Handle .name query on {name: "test", data: [1, 2, 3]}
64
+ if (query === ".name") {
65
+ if (data && data.name) {
66
+ return JSON.stringify(data.name);
67
+ }
34
68
  }
35
- }
36
-
37
- // Handle .unicode query for special characters test
38
- if (query === ".unicode") {
39
- if (data && data.unicode !== undefined) {
40
- return JSON.stringify(data.unicode);
69
+
70
+ // Handle .data[] query on {name: "test", data: [1, 2, 3]}
71
+ if (query === ".data[]") {
72
+ if (data && Array.isArray(data.data)) {
73
+ return data.data.join("\n");
74
+ }
41
75
  }
42
- }
43
-
44
- // Handle .empty query for empty string test
45
- if (query === ".empty") {
46
- if (data && data.empty !== undefined) {
47
- return JSON.stringify(data.empty);
76
+
77
+ // Handle map(.value) on [{id: 1, value: "a"}, {id: 2, value: "b"}]
78
+ if (query === "map(.value)") {
79
+ if (Array.isArray(data)) {
80
+ const values = data.map((item) => item.value);
81
+ return JSON.stringify(values);
82
+ }
48
83
  }
49
- }
50
-
51
- // Handle .nullValue query for null value test
52
- if (query === ".nullValue") {
53
- if (data && data.nullValue !== undefined) {
54
- return JSON.stringify(data.nullValue);
84
+
85
+ // Handle map({identifier: .id, content: .value}) transformation
86
+ if (query === "map({identifier: .id, content: .value})") {
87
+ if (Array.isArray(data)) {
88
+ const transformed = data.map((item) => ({
89
+ identifier: item.id,
90
+ content: item.value,
91
+ }));
92
+ return JSON.stringify(transformed);
93
+ }
55
94
  }
56
- }
57
-
58
- // Handle .name query on {name: "test", data: [1, 2, 3]}
59
- if (query === ".name") {
60
- if (data && data.name) {
61
- return JSON.stringify(data.name);
95
+
96
+ // Handle "." query (return entire object, formatted)
97
+ if (query === ".") {
98
+ return JSON.stringify(data, null, 2);
62
99
  }
63
- }
64
-
65
- // Handle .data[] query on {name: "test", data: [1, 2, 3]}
66
- if (query === ".data[]") {
67
- if (data && Array.isArray(data.data)) {
68
- return data.data.join("\n");
100
+
101
+ // Handle .nested.inner query for nested JSON
102
+ if (query === ".nested.inner") {
103
+ if (data && data.nested && data.nested.inner) {
104
+ return JSON.stringify(data.nested.inner);
105
+ }
106
+ }
107
+
108
+ // Handle map(select(.id > 10)) - empty result
109
+ if (query === "map(select(.id > 10))") {
110
+ return JSON.stringify([]);
69
111
  }
70
- }
71
-
72
- // Handle map(.value) on [{id: 1, value: "a"}, {id: 2, value: "b"}]
73
- if (query === "map(.value)") {
74
- if (Array.isArray(data)) {
75
- const values = data.map(item => item.value);
76
- return JSON.stringify(values);
112
+
113
+ // Handle invalid queries - throw error
114
+ if (query === ".invalid[") {
115
+ throw new Error("Invalid JQ query syntax");
77
116
  }
78
- }
79
-
80
- // Handle map({identifier: .id, content: .value}) transformation
81
- if (query === "map({identifier: .id, content: .value})") {
82
- if (Array.isArray(data)) {
83
- const transformed = data.map(item => ({
84
- identifier: item.id,
85
- content: item.value
86
- }));
87
- return JSON.stringify(transformed);
117
+
118
+ // Handle deep nested queries
119
+ if (query === ".level1.level2.level3.level4.level5.deepValue") {
120
+ if (
121
+ data &&
122
+ data.level1 &&
123
+ data.level1.level2 &&
124
+ data.level1.level2.level3 &&
125
+ data.level1.level2.level3.level4 &&
126
+ data.level1.level2.level3.level4.level5
127
+ ) {
128
+ return JSON.stringify(
129
+ data.level1.level2.level3.level4.level5.deepValue
130
+ );
131
+ }
88
132
  }
89
- }
90
-
91
- // Handle "." query (return entire object, formatted)
92
- if (query === ".") {
93
- return JSON.stringify(data, null, 2);
94
- }
95
-
96
- // Handle .nested.inner query for nested JSON
97
- if (query === ".nested.inner") {
98
- if (data && data.nested && data.nested.inner) {
99
- return JSON.stringify(data.nested.inner);
133
+
134
+ // Handle queries on invalid JSON data
135
+ if (typeof data === "string" && data === "invalid json string") {
136
+ throw new Error("Invalid JSON input");
100
137
  }
101
- }
102
-
103
- // Handle map(select(.id > 10)) - empty result
104
- if (query === "map(select(.id > 10))") {
105
- return JSON.stringify([]);
106
- }
107
-
108
- // Handle invalid queries - throw error
109
- if (query === ".invalid[") {
110
- throw new Error("Invalid JQ query syntax");
111
- }
112
-
113
- // Handle deep nested queries
114
- if (query === ".level1.level2.level3.level4.level5.deepValue") {
115
- if (data && data.level1 && data.level1.level2 && data.level1.level2.level3 &&
116
- data.level1.level2.level3.level4 && data.level1.level2.level3.level4.level5) {
117
- return JSON.stringify(data.level1.level2.level3.level4.level5.deepValue);
138
+
139
+ // Default fallback - return stringified data
140
+ try {
141
+ return JSON.stringify(data);
142
+ } catch (error) {
143
+ throw new Error(`JQ query failed: ${error}`);
118
144
  }
119
- }
120
-
121
- // Handle queries on invalid JSON data
122
- if (typeof data === "string" && data === "invalid json string") {
123
- throw new Error("Invalid JSON input");
124
- }
125
-
126
- // Default fallback - return stringified data
127
- try {
128
- return JSON.stringify(data);
129
- } catch (error) {
130
- throw new Error(`JQ query failed: ${error}`);
131
- }
132
- })
145
+ }),
133
146
  }));
134
147
 
135
148
  const mockJq = require("node-jq");
@@ -140,9 +153,9 @@ describe("ToolResponseCache", () => {
140
153
 
141
154
  beforeEach(() => {
142
155
  jest.clearAllMocks();
143
-
156
+
144
157
  mockToolsService = {
145
- addTool: jest.fn(),
158
+ addTools: jest.fn(),
146
159
  addFunctions: jest.fn(),
147
160
  getTool: jest.fn().mockReturnValue(undefined),
148
161
  callTool: jest.fn(),
@@ -155,18 +168,20 @@ describe("ToolResponseCache", () => {
155
168
  it("should create an instance and register tool with ToolsService", () => {
156
169
  expect(cache).toBeDefined();
157
170
  expect(cache).toBeInstanceOf(ToolResponseCache);
158
- expect(mockToolsService.addTool).toHaveBeenCalledWith(jqToolResponseDefinition);
171
+ expect(mockToolsService.addTools).toHaveBeenCalledWith([
172
+ jqToolResponseDefinition,
173
+ ]);
159
174
  expect(mockToolsService.addFunctions).toHaveBeenCalledWith({
160
175
  jqToolResponse: expect.any(Function),
161
176
  });
162
177
  });
163
178
 
164
- it("should not register tool if it already exists", () => {
179
+ it("should overwrite tool if it already exists", () => {
165
180
  mockToolsService.getTool.mockReturnValue(jqToolResponseDefinition);
166
181
  const newCache = new ToolResponseCache(mockToolsService);
167
-
168
- // Should only be called once from the first instance
169
- expect(mockToolsService.addTool).toHaveBeenCalledTimes(1);
182
+
183
+ // Should be called each time
184
+ expect(mockToolsService.addTools).toHaveBeenCalledTimes(2);
170
185
  });
171
186
  });
172
187
 
@@ -178,100 +193,102 @@ describe("ToolResponseCache", () => {
178
193
 
179
194
  it("should process tool response messages", async () => {
180
195
  const processor = cache.createProcessor();
181
-
196
+
182
197
  const messages: Message[] = [
183
198
  {
184
199
  role: "tool",
185
200
  tool_call_id: "call_123",
186
- content: '{"data": [{"name": "test", "value": 42}]}'
187
- }
201
+ content: '{"data": [{"name": "test", "value": 42}]}',
202
+ },
188
203
  ];
189
204
 
190
205
  await processor(messages, messages);
191
-
206
+
192
207
  // Verify message was stored
193
208
  expect(cache.getStorageKeys()).toContain("call_123");
194
- expect(cache.retrieveRawResponse("call_123")).toBe('{"data": [{"name": "test", "value": 42}]}');
209
+ expect(cache.retrieveRawResponse("call_123")).toBe(
210
+ '{"data": [{"name": "test", "value": 42}]}'
211
+ );
195
212
  });
196
213
 
197
214
  it("should ignore non-tool messages", async () => {
198
215
  const processor = cache.createProcessor();
199
-
216
+
200
217
  const messages: Message[] = [
201
218
  {
202
219
  role: "user",
203
- content: "This is a user message"
220
+ content: "This is a user message",
204
221
  },
205
222
  {
206
- role: "assistant",
207
- content: "This is an assistant message"
208
- }
223
+ role: "assistant",
224
+ content: "This is an assistant message",
225
+ },
209
226
  ];
210
227
 
211
228
  await processor(messages, messages);
212
-
229
+
213
230
  expect(cache.getStorageSize()).toBe(0);
214
231
  });
215
232
  it("should ignore tool messages without tool_call_id", async () => {
216
233
  const processor = cache.createProcessor();
217
-
234
+
218
235
  const messages: Message[] = [
219
236
  {
220
237
  role: "tool",
221
- content: "Tool response without call ID"
222
- } as Message
238
+ content: "Tool response without call ID",
239
+ } as Message,
223
240
  ];
224
241
 
225
242
  await processor(messages, messages);
226
-
243
+
227
244
  expect(cache.getStorageSize()).toBe(0);
228
245
  });
229
246
 
230
247
  it("should ignore tool messages with non-string content", async () => {
231
248
  const processor = cache.createProcessor();
232
-
249
+
233
250
  const messages: Message[] = [
234
251
  {
235
252
  role: "tool",
236
253
  tool_call_id: "call_123",
237
- content: null
254
+ content: null,
238
255
  } as Message,
239
256
  {
240
- role: "tool",
257
+ role: "tool",
241
258
  tool_call_id: "call_456",
242
- content: undefined
259
+ content: undefined,
243
260
  } as Message,
244
261
  {
245
262
  role: "tool",
246
- tool_call_id: "call_789",
247
- content: 42 as any
248
- } as Message
263
+ tool_call_id: "call_789",
264
+ content: 42 as any,
265
+ } as Message,
249
266
  ];
250
267
 
251
268
  await processor(messages, messages);
252
-
269
+
253
270
  expect(cache.getStorageSize()).toBe(0);
254
271
  });
255
272
 
256
273
  it("should apply filter function when provided", async () => {
257
274
  const filterFn = (msg: Message) => msg.tool_call_id === "call_allowed";
258
275
  const processor = cache.createProcessor(filterFn);
259
-
276
+
260
277
  const messages: Message[] = [
261
278
  {
262
279
  role: "tool",
263
280
  tool_call_id: "call_allowed",
264
- content: "Allowed content"
281
+ content: "Allowed content",
265
282
  },
266
283
  {
267
284
  role: "tool",
268
285
  tool_call_id: "call_blocked",
269
- content: "Blocked content"
270
- }
286
+ content: "Blocked content",
287
+ },
271
288
  ];
272
289
 
273
290
  await processor(messages, messages);
274
-
291
+
275
292
  expect(cache.getStorageKeys()).toContain("call_allowed");
276
293
  expect(cache.getStorageKeys()).not.toContain("call_blocked");
277
294
  expect(cache.getStorageSize()).toBe(1);
@@ -282,13 +299,13 @@ describe("ToolResponseCache", () => {
282
299
  it("should store tool response content with metadata", () => {
283
300
  const content = '{"test": "data"}';
284
301
  const toolCallId = "call_123";
285
-
302
+
286
303
  // Access private method for testing
287
304
  (cache as any).storeToolResponse(content, toolCallId);
288
-
305
+
289
306
  expect(cache.retrieveRawResponse(toolCallId)).toBe(content);
290
307
  expect(cache.getStorageKeys()).toContain(toolCallId);
291
-
308
+
292
309
  // Check metadata
293
310
  const metadata = (cache as any).metadataStorage[toolCallId];
294
311
  expect(metadata.toolCallId).toBe(toolCallId);
@@ -299,10 +316,19 @@ describe("ToolResponseCache", () => {
299
316
  describe("queryToolResponse", () => {
300
317
  beforeEach(() => {
301
318
  // Store some test data
302
- cache.storeToolResponse('{"name": "test", "data": [1, 2, 3]}', "call_123");
303
- cache.storeToolResponse('[{"id": 1, "value": "a"}, {"id": 2, "value": "b"}]', "call_456");
304
- cache.storeToolResponse('{"nested": "{\\"inner\\": \\"value\\"}"}', "call_789");
305
- cache.storeToolResponse('invalid json string', "call_invalid");
319
+ cache.storeToolResponse(
320
+ '{"name": "test", "data": [1, 2, 3]}',
321
+ "call_123"
322
+ );
323
+ cache.storeToolResponse(
324
+ '[{"id": 1, "value": "a"}, {"id": 2, "value": "b"}]',
325
+ "call_456"
326
+ );
327
+ cache.storeToolResponse(
328
+ '{"nested": "{\\"inner\\": \\"value\\"}"}',
329
+ "call_789"
330
+ );
331
+ cache.storeToolResponse("invalid json string", "call_invalid");
306
332
  });
307
333
 
308
334
  it("should execute simple JQ queries successfully", async () => {
@@ -316,16 +342,19 @@ describe("ToolResponseCache", () => {
316
342
  });
317
343
 
318
344
  it("should execute complex JQ queries", async () => {
319
- const result = await cache.queryToolResponse("call_456", 'map(.value)');
345
+ const result = await cache.queryToolResponse("call_456", "map(.value)");
320
346
  expect(result).toBe('["a","b"]');
321
347
  });
322
348
 
323
349
  it("should handle object transformation queries", async () => {
324
- const result = await cache.queryToolResponse("call_456", 'map({identifier: .id, content: .value})');
350
+ const result = await cache.queryToolResponse(
351
+ "call_456",
352
+ "map({identifier: .id, content: .value})"
353
+ );
325
354
  const parsed = JSON.parse(result);
326
355
  expect(parsed).toEqual([
327
- {identifier: 1, content: "a"},
328
- {identifier: 2, content: "b"}
356
+ { identifier: 1, content: "a" },
357
+ { identifier: 2, content: "b" },
329
358
  ]);
330
359
  });
331
360
 
@@ -344,14 +373,14 @@ describe("ToolResponseCache", () => {
344
373
  it("should handle non-JSON data with helpful error", async () => {
345
374
  const result = await cache.queryToolResponse("call_invalid", ".test");
346
375
  expect(result).toContain("Error: Tool response data is not valid JSON");
347
- expect(result).toContain("toolCallId \"call_invalid\"");
376
+ expect(result).toContain('toolCallId "call_invalid"');
348
377
  });
349
378
 
350
379
  it("should return formatted JSON for complex results", async () => {
351
380
  const result = await cache.queryToolResponse("call_456", ".");
352
381
  expect(result).toContain("[\n");
353
382
  expect(result).toContain(" {");
354
- expect(result).toContain(" \"id\":");
383
+ expect(result).toContain(' "id":');
355
384
  });
356
385
 
357
386
  it("should handle nested JSON strings parsing", async () => {
@@ -360,7 +389,10 @@ describe("ToolResponseCache", () => {
360
389
  });
361
390
 
362
391
  it("should handle empty query results", async () => {
363
- const result = await cache.queryToolResponse("call_456", 'map(select(.id > 10))');
392
+ const result = await cache.queryToolResponse(
393
+ "call_456",
394
+ "map(select(.id > 10))"
395
+ );
364
396
  expect(result).toBe("[]");
365
397
  });
366
398
  });
@@ -369,24 +401,21 @@ describe("ToolResponseCache", () => {
369
401
  it("should parse simple JSON strings", () => {
370
402
  const input = '{"test": "value"}';
371
403
  const result = cache.parseNestedJsonStrings(input);
372
- expect(result).toEqual({test: "value"});
404
+ expect(result).toEqual({ test: "value" });
373
405
  });
374
406
 
375
407
  it("should parse nested JSON strings recursively", () => {
376
408
  const input = '{"outer": "{\\"inner\\": \\"value\\"}"}';
377
409
  const result = cache.parseNestedJsonStrings(input);
378
410
  expect(result).toEqual({
379
- outer: {inner: "value"}
411
+ outer: { inner: "value" },
380
412
  });
381
413
  });
382
414
 
383
415
  it("should handle arrays with JSON strings", () => {
384
416
  const input = ['{"test": "value1"}', '{"test": "value2"}'];
385
417
  const result = cache.parseNestedJsonStrings(input);
386
- expect(result).toEqual([
387
- {test: "value1"},
388
- {test: "value2"}
389
- ]);
418
+ expect(result).toEqual([{ test: "value1" }, { test: "value2" }]);
390
419
  });
391
420
 
392
421
  it("should handle mixed nested structures", () => {
@@ -394,16 +423,16 @@ describe("ToolResponseCache", () => {
394
423
  stringField: '{"nested": "value"}',
395
424
  arrayField: ['{"item": 1}', '{"item": 2}'],
396
425
  objectField: {
397
- deepString: '{"deep": "nested"}'
398
- }
426
+ deepString: '{"deep": "nested"}',
427
+ },
399
428
  };
400
429
  const result = cache.parseNestedJsonStrings(input);
401
430
  expect(result).toEqual({
402
- stringField: {nested: "value"},
403
- arrayField: [{item: 1}, {item: 2}],
431
+ stringField: { nested: "value" },
432
+ arrayField: [{ item: 1 }, { item: 2 }],
404
433
  objectField: {
405
- deepString: {deep: "nested"}
406
- }
434
+ deepString: { deep: "nested" },
435
+ },
407
436
  });
408
437
  });
409
438
 
@@ -412,14 +441,14 @@ describe("ToolResponseCache", () => {
412
441
  jsonString: '{"test": "value"}',
413
442
  regularString: "just a string",
414
443
  number: 42,
415
- boolean: true
444
+ boolean: true,
416
445
  };
417
446
  const result = cache.parseNestedJsonStrings(input);
418
447
  expect(result).toEqual({
419
- jsonString: {test: "value"},
420
- regularString: "just a string",
448
+ jsonString: { test: "value" },
449
+ regularString: "just a string",
421
450
  number: 42,
422
- boolean: true
451
+ boolean: true,
423
452
  });
424
453
  });
425
454
 
@@ -433,7 +462,7 @@ describe("ToolResponseCache", () => {
433
462
  it("should return stored raw content", () => {
434
463
  const content = '{"test": "data"}';
435
464
  cache.storeToolResponse(content, "call_123");
436
-
465
+
437
466
  const result = cache.retrieveRawResponse("call_123");
438
467
  expect(result).toBe(content);
439
468
  });
@@ -448,11 +477,11 @@ describe("ToolResponseCache", () => {
448
477
  it("should clear all stored data and metadata", () => {
449
478
  cache.storeToolResponse('{"test": "data"}', "call_123");
450
479
  cache.storeToolResponse('{"more": "data"}', "call_456");
451
-
480
+
452
481
  expect(cache.getStorageSize()).toBe(2);
453
-
482
+
454
483
  cache.clearStorage();
455
-
484
+
456
485
  expect(cache.getStorageSize()).toBe(0);
457
486
  expect(cache.getStorageKeys()).toEqual([]);
458
487
  expect(cache.retrieveRawResponse("call_123")).toBeNull();
@@ -469,7 +498,7 @@ describe("ToolResponseCache", () => {
469
498
  cache.storeToolResponse('{"test": "data1"}', "call_123");
470
499
  cache.storeToolResponse('{"test": "data2"}', "call_456");
471
500
  cache.storeToolResponse('{"test": "data3"}', "call_789");
472
-
501
+
473
502
  const keys = cache.getStorageKeys();
474
503
  expect(keys).toContain("call_123");
475
504
  expect(keys).toContain("call_456");
@@ -485,13 +514,13 @@ describe("ToolResponseCache", () => {
485
514
 
486
515
  it("should return correct count after storing responses", () => {
487
516
  expect(cache.getStorageSize()).toBe(0);
488
-
517
+
489
518
  cache.storeToolResponse('{"test": "data1"}', "call_123");
490
519
  expect(cache.getStorageSize()).toBe(1);
491
-
520
+
492
521
  cache.storeToolResponse('{"test": "data2"}', "call_456");
493
522
  expect(cache.getStorageSize()).toBe(2);
494
-
523
+
495
524
  cache.clearStorage();
496
525
  expect(cache.getStorageSize()).toBe(0);
497
526
  });
@@ -503,49 +532,49 @@ describe("ToolResponseCache", () => {
503
532
  beforeEach(() => {
504
533
  mockToolsService = {
505
534
  getTool: jest.fn(),
506
- addTool: jest.fn(),
507
- addFunctions: jest.fn()
535
+ addTools: jest.fn(),
536
+ addFunctions: jest.fn(),
508
537
  } as any;
509
538
  });
510
539
 
511
540
  it("should register tool when not already present", () => {
512
541
  mockToolsService.getTool.mockReturnValue(null);
513
-
542
+
514
543
  cache.registerTool(mockToolsService);
515
-
516
- expect(mockToolsService.getTool).toHaveBeenCalledWith("jqToolResponse");
517
- expect(mockToolsService.addTool).toHaveBeenCalledWith(expect.objectContaining({
518
- type: "function",
519
- function: expect.objectContaining({
520
- name: "jqToolResponse"
521
- })
522
- }));
544
+
545
+ expect(mockToolsService.addTools).toHaveBeenCalledWith([
546
+ {
547
+ type: "function",
548
+ function: expect.objectContaining({
549
+ name: "jqToolResponse",
550
+ }),
551
+ },
552
+ ]);
523
553
  expect(mockToolsService.addFunctions).toHaveBeenCalledWith({
524
- jqToolResponse: expect.any(Function)
554
+ jqToolResponse: expect.any(Function),
525
555
  });
526
556
  });
527
557
 
528
- it("should not register tool when already present", () => {
558
+ it("should overwrite tool when already present", () => {
529
559
  mockToolsService.getTool.mockReturnValue({} as any);
530
-
560
+
531
561
  cache.registerTool(mockToolsService);
532
-
533
- expect(mockToolsService.getTool).toHaveBeenCalledWith("jqToolResponse");
534
- expect(mockToolsService.addTool).not.toHaveBeenCalled();
535
- expect(mockToolsService.addFunctions).not.toHaveBeenCalled();
562
+
563
+ expect(mockToolsService.addTools).toHaveBeenCalled();
564
+ expect(mockToolsService.addFunctions).toHaveBeenCalled();
536
565
  });
537
566
 
538
567
  it("should register function that calls queryToolResponse", async () => {
539
568
  mockToolsService.getTool.mockReturnValue(null);
540
-
569
+
541
570
  cache.registerTool(mockToolsService);
542
-
571
+
543
572
  const addFunctionsCall = mockToolsService.addFunctions.mock.calls[0][0];
544
573
  const jqFunction = addFunctionsCall.jqToolResponse;
545
-
574
+
546
575
  // Store test data
547
576
  cache.storeToolResponse('{"test": "value"}', "call_123");
548
-
577
+
549
578
  // Test the registered function
550
579
  const result = await jqFunction("call_123", ".test");
551
580
  expect(result).toBe('"value"');
@@ -555,17 +584,22 @@ describe("ToolResponseCache", () => {
555
584
  describe("Edge Cases and Error Handling", () => {
556
585
  it("should handle very large JSON objects", async () => {
557
586
  const largeObject = {
558
- data: Array(1000).fill(0).map((_, i) => ({
559
- id: i,
560
- name: `item_${i}`,
561
- description: `This is item number ${i} with some additional text to make it larger`
562
- }))
587
+ data: Array(1000)
588
+ .fill(0)
589
+ .map((_, i) => ({
590
+ id: i,
591
+ name: `item_${i}`,
592
+ description: `This is item number ${i} with some additional text to make it larger`,
593
+ })),
563
594
  };
564
595
  const largeContent = JSON.stringify(largeObject);
565
-
596
+
566
597
  cache.storeToolResponse(largeContent, "call_large");
567
-
568
- const result = await cache.queryToolResponse("call_large", ".data | length");
598
+
599
+ const result = await cache.queryToolResponse(
600
+ "call_large",
601
+ ".data | length"
602
+ );
569
603
  expect(result).toBe("1000");
570
604
  });
571
605
 
@@ -573,11 +607,11 @@ describe("ToolResponseCache", () => {
573
607
  const specialContent = JSON.stringify({
574
608
  unicode: "Hello 世界 🌍",
575
609
  escaped: "Line 1\nLine 2\tTabbed",
576
- quotes: 'He said "Hello" to me'
610
+ quotes: 'He said "Hello" to me',
577
611
  });
578
-
612
+
579
613
  cache.storeToolResponse(specialContent, "call_special");
580
-
614
+
581
615
  const result = await cache.queryToolResponse("call_special", ".unicode");
582
616
  expect(result).toBe('"Hello 世界 🌍"');
583
617
  });
@@ -587,33 +621,38 @@ describe("ToolResponseCache", () => {
587
621
  empty: "",
588
622
  nullValue: null,
589
623
  zero: 0,
590
- false: false
624
+ false: false,
591
625
  });
592
-
626
+
593
627
  cache.storeToolResponse(content, "call_empty");
594
-
628
+
595
629
  const emptyResult = await cache.queryToolResponse("call_empty", ".empty");
596
630
  expect(emptyResult).toBe('""');
597
-
598
- const nullResult = await cache.queryToolResponse("call_empty", ".nullValue");
631
+
632
+ const nullResult = await cache.queryToolResponse(
633
+ "call_empty",
634
+ ".nullValue"
635
+ );
599
636
  expect(nullResult).toBe("null");
600
637
  });
601
638
 
602
639
  it("should handle concurrent storage operations", async () => {
603
- const promises = Array(10).fill(0).map((_, i) =>
604
- Promise.resolve(cache.storeToolResponse(`{"id": ${i}}`, `call_${i}`))
605
- );
606
-
640
+ const promises = Array(10)
641
+ .fill(0)
642
+ .map((_, i) =>
643
+ Promise.resolve(cache.storeToolResponse(`{"id": ${i}}`, `call_${i}`))
644
+ );
645
+
607
646
  await Promise.all(promises);
608
-
647
+
609
648
  expect(cache.getStorageSize()).toBe(10);
610
-
649
+
611
650
  const results = await Promise.all(
612
- Array(10).fill(0).map((_, i) =>
613
- cache.queryToolResponse(`call_${i}`, ".id")
614
- )
651
+ Array(10)
652
+ .fill(0)
653
+ .map((_, i) => cache.queryToolResponse(`call_${i}`, ".id"))
615
654
  );
616
-
655
+
617
656
  results.forEach((result, i) => {
618
657
  expect(result).toBe(i.toString());
619
658
  });
@@ -626,17 +665,20 @@ describe("ToolResponseCache", () => {
626
665
  level3: {
627
666
  level4: {
628
667
  level5: {
629
- deepValue: "found it!"
630
- }
631
- }
632
- }
633
- }
634
- }
668
+ deepValue: "found it!",
669
+ },
670
+ },
671
+ },
672
+ },
673
+ },
635
674
  };
636
-
675
+
637
676
  cache.storeToolResponse(JSON.stringify(deepObject), "call_deep");
638
-
639
- const result = await cache.queryToolResponse("call_deep", ".level1.level2.level3.level4.level5.deepValue");
677
+
678
+ const result = await cache.queryToolResponse(
679
+ "call_deep",
680
+ ".level1.level2.level3.level4.level5.deepValue"
681
+ );
640
682
  expect(result).toBe('"found it!"');
641
683
  });
642
684
  });
@@ -644,14 +686,14 @@ describe("ToolResponseCache", () => {
644
686
  describe("Integration with Message Processing", () => {
645
687
  it("should integrate with complete message processing workflow", async () => {
646
688
  const processor = cache.createProcessor();
647
-
689
+
648
690
  const messages: Message[] = [
649
691
  {
650
692
  role: "user",
651
- content: "Test request"
693
+ content: "Test request",
652
694
  },
653
695
  {
654
- role: "assistant",
696
+ role: "assistant",
655
697
  content: "Processing...",
656
698
  tool_calls: [
657
699
  {
@@ -659,30 +701,36 @@ describe("ToolResponseCache", () => {
659
701
  type: "function",
660
702
  function: {
661
703
  name: "testTool",
662
- arguments: "{}"
663
- }
664
- }
665
- ]
704
+ arguments: "{}",
705
+ },
706
+ },
707
+ ],
666
708
  },
667
709
  {
668
710
  role: "tool",
669
711
  tool_call_id: "call_123",
670
712
  content: JSON.stringify({
671
713
  result: "success",
672
- data: [1, 2, 3, 4, 5]
673
- })
674
- }
714
+ data: [1, 2, 3, 4, 5],
715
+ }),
716
+ },
675
717
  ];
676
718
 
677
719
  await processor(messages, messages);
678
-
720
+
679
721
  expect(cache.getStorageSize()).toBe(1);
680
-
681
- const result = await cache.queryToolResponse("call_123", ".data | length");
722
+
723
+ const result = await cache.queryToolResponse(
724
+ "call_123",
725
+ ".data | length"
726
+ );
682
727
  expect(result).toBe("5");
683
-
684
- const sumResult = await cache.queryToolResponse("call_123", ".data | add");
728
+
729
+ const sumResult = await cache.queryToolResponse(
730
+ "call_123",
731
+ ".data | add"
732
+ );
685
733
  expect(result).toBe("5");
686
734
  });
687
735
  });
688
- });
736
+ });