@tyvm/knowhow 0.0.48 → 0.0.50
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/auth/browserLogin.ts +4 -4
- package/src/chat/modules/AgentModule.ts +22 -3
- package/src/embeddings.ts +2 -4
- package/src/login.ts +27 -20
- package/src/plugins/GitPlugin.ts +10 -5
- package/src/processors/CustomVariables.ts +48 -70
- package/src/processors/TokenCompressor.ts +5 -4
- package/src/processors/ToolResponseCache.ts +6 -9
- package/src/services/KnowhowClient.ts +43 -16
- 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/login.d.ts +4 -0
- package/ts_build/src/login.js +21 -16
- package/ts_build/src/login.js.map +1 -1
- package/ts_build/src/plugins/GitPlugin.js +5 -5
- package/ts_build/src/plugins/GitPlugin.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/src/services/KnowhowClient.d.ts +2 -1
- package/ts_build/src/services/KnowhowClient.js +36 -16
- package/ts_build/src/services/KnowhowClient.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,7 +8,7 @@ describe("CustomVariables", () => {
|
|
|
8
8
|
|
|
9
9
|
beforeEach(() => {
|
|
10
10
|
mockToolsService = {
|
|
11
|
-
|
|
11
|
+
addTools: jest.fn(),
|
|
12
12
|
addFunctions: jest.fn(),
|
|
13
13
|
getTool: jest.fn().mockReturnValue(undefined),
|
|
14
14
|
callTool: jest.fn(),
|
|
@@ -23,12 +23,12 @@ describe("CustomVariables", () => {
|
|
|
23
23
|
|
|
24
24
|
describe("constructor", () => {
|
|
25
25
|
it("should register all variable tools with ToolsService", () => {
|
|
26
|
-
expect(mockToolsService.
|
|
27
|
-
expect(mockToolsService.addFunctions).toHaveBeenCalledTimes(
|
|
26
|
+
expect(mockToolsService.addTools).toHaveBeenCalledTimes(1);
|
|
27
|
+
expect(mockToolsService.addFunctions).toHaveBeenCalledTimes(1);
|
|
28
28
|
|
|
29
29
|
// Verify all tools are registered
|
|
30
|
-
const addToolCalls = mockToolsService.
|
|
31
|
-
const toolNames = addToolCalls.map(
|
|
30
|
+
const addToolCalls = mockToolsService.addTools.mock.calls;
|
|
31
|
+
const toolNames = addToolCalls[0][0].map((args) => args.function.name);
|
|
32
32
|
|
|
33
33
|
expect(toolNames).toContain("setVariable");
|
|
34
34
|
expect(toolNames).toContain("getVariable");
|
|
@@ -37,12 +37,15 @@ describe("CustomVariables", () => {
|
|
|
37
37
|
expect(toolNames).toContain("deleteVariable");
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
it("should
|
|
41
|
-
mockToolsService.getTool.mockReturnValue({
|
|
40
|
+
it("should overwrite tools if they already exist", () => {
|
|
41
|
+
mockToolsService.getTool.mockReturnValue({
|
|
42
|
+
type: "function",
|
|
43
|
+
function: { name: "setVariable" },
|
|
44
|
+
} as any);
|
|
42
45
|
const newCustomVariables = new CustomVariables(mockToolsService);
|
|
43
46
|
|
|
44
|
-
// Should still be called
|
|
45
|
-
expect(mockToolsService.
|
|
47
|
+
// Should still be called once per instance creation
|
|
48
|
+
expect(mockToolsService.addTools).toHaveBeenCalledTimes(2);
|
|
46
49
|
});
|
|
47
50
|
});
|
|
48
51
|
|
|
@@ -51,7 +54,9 @@ describe("CustomVariables", () => {
|
|
|
51
54
|
|
|
52
55
|
beforeEach(() => {
|
|
53
56
|
const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
|
|
54
|
-
const setVariableCall = addFunctionsCalls.find(
|
|
57
|
+
const setVariableCall = addFunctionsCalls.find(
|
|
58
|
+
(call) => call[0].setVariable
|
|
59
|
+
);
|
|
55
60
|
setVariableFunction = setVariableCall[0].setVariable;
|
|
56
61
|
});
|
|
57
62
|
|
|
@@ -86,7 +91,9 @@ describe("CustomVariables", () => {
|
|
|
86
91
|
|
|
87
92
|
beforeEach(() => {
|
|
88
93
|
const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
|
|
89
|
-
const setVariableCall = addFunctionsCalls.find(
|
|
94
|
+
const setVariableCall = addFunctionsCalls.find(
|
|
95
|
+
(call) => call[0].setVariable
|
|
96
|
+
);
|
|
90
97
|
setVariableFunction = setVariableCall[0].setVariable;
|
|
91
98
|
});
|
|
92
99
|
|
|
@@ -122,8 +129,12 @@ describe("CustomVariables", () => {
|
|
|
122
129
|
|
|
123
130
|
beforeEach(() => {
|
|
124
131
|
const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
|
|
125
|
-
const setVariableCall = addFunctionsCalls.find(
|
|
126
|
-
|
|
132
|
+
const setVariableCall = addFunctionsCalls.find(
|
|
133
|
+
(call) => call[0].setVariable
|
|
134
|
+
);
|
|
135
|
+
const getVariableCall = addFunctionsCalls.find(
|
|
136
|
+
(call) => call[0].getVariable
|
|
137
|
+
);
|
|
127
138
|
setVariableFunction = setVariableCall[0].setVariable;
|
|
128
139
|
getVariableFunction = getVariableCall[0].getVariable;
|
|
129
140
|
});
|
|
@@ -166,8 +177,12 @@ describe("CustomVariables", () => {
|
|
|
166
177
|
|
|
167
178
|
beforeEach(() => {
|
|
168
179
|
const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
|
|
169
|
-
const setVariableCall = addFunctionsCalls.find(
|
|
170
|
-
|
|
180
|
+
const setVariableCall = addFunctionsCalls.find(
|
|
181
|
+
(call) => call[0].setVariable
|
|
182
|
+
);
|
|
183
|
+
const listVariablesCall = addFunctionsCalls.find(
|
|
184
|
+
(call) => call[0].listVariables
|
|
185
|
+
);
|
|
171
186
|
setVariableFunction = setVariableCall[0].setVariable;
|
|
172
187
|
listVariablesFunction = listVariablesCall[0].listVariables;
|
|
173
188
|
});
|
|
@@ -204,8 +219,12 @@ describe("CustomVariables", () => {
|
|
|
204
219
|
|
|
205
220
|
beforeEach(() => {
|
|
206
221
|
const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
|
|
207
|
-
const setVariableCall = addFunctionsCalls.find(
|
|
208
|
-
|
|
222
|
+
const setVariableCall = addFunctionsCalls.find(
|
|
223
|
+
(call) => call[0].setVariable
|
|
224
|
+
);
|
|
225
|
+
const deleteVariableCall = addFunctionsCalls.find(
|
|
226
|
+
(call) => call[0].deleteVariable
|
|
227
|
+
);
|
|
209
228
|
setVariableFunction = setVariableCall[0].setVariable;
|
|
210
229
|
deleteVariableFunction = deleteVariableCall[0].deleteVariable;
|
|
211
230
|
});
|
|
@@ -213,7 +232,9 @@ describe("CustomVariables", () => {
|
|
|
213
232
|
it("should delete existing variables", () => {
|
|
214
233
|
setVariableFunction("testVar", "test value");
|
|
215
234
|
const result = deleteVariableFunction("testVar");
|
|
216
|
-
expect(result).toContain(
|
|
235
|
+
expect(result).toContain(
|
|
236
|
+
'Variable "testVar" has been deleted successfully'
|
|
237
|
+
);
|
|
217
238
|
expect(customVariables.getVariableNames()).not.toContain("testVar");
|
|
218
239
|
});
|
|
219
240
|
|
|
@@ -229,11 +250,17 @@ describe("CustomVariables", () => {
|
|
|
229
250
|
});
|
|
230
251
|
|
|
231
252
|
describe("storeToolCallToVariable functionality", () => {
|
|
232
|
-
let storeToolCallFunction: (
|
|
253
|
+
let storeToolCallFunction: (
|
|
254
|
+
varName: string,
|
|
255
|
+
toolName: string,
|
|
256
|
+
toolArgs: string
|
|
257
|
+
) => Promise<string>;
|
|
233
258
|
|
|
234
259
|
beforeEach(() => {
|
|
235
260
|
const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
|
|
236
|
-
const storeToolCallCall = addFunctionsCalls.find(
|
|
261
|
+
const storeToolCallCall = addFunctionsCalls.find(
|
|
262
|
+
(call) => call[0].storeToolCallToVariable
|
|
263
|
+
);
|
|
237
264
|
storeToolCallFunction = storeToolCallCall[0].storeToolCallToVariable;
|
|
238
265
|
});
|
|
239
266
|
|
|
@@ -243,38 +270,56 @@ describe("CustomVariables", () => {
|
|
|
243
270
|
toolCallId: "test-call-id",
|
|
244
271
|
functionName: "testTool",
|
|
245
272
|
functionArgs: { param1: "value1" },
|
|
246
|
-
functionResp: { success: true, data: "test data" }
|
|
273
|
+
functionResp: { success: true, data: "test data" },
|
|
247
274
|
};
|
|
248
275
|
mockToolsService.callTool.mockResolvedValue(mockResult);
|
|
249
276
|
|
|
250
|
-
const result = await storeToolCallFunction(
|
|
277
|
+
const result = await storeToolCallFunction(
|
|
278
|
+
"resultVar",
|
|
279
|
+
"testTool",
|
|
280
|
+
'{"param1": "value1"}'
|
|
281
|
+
);
|
|
251
282
|
|
|
252
|
-
expect(result).toContain(
|
|
283
|
+
expect(result).toContain(
|
|
284
|
+
'Tool call result for "testTool" has been stored in variable "resultVar"'
|
|
285
|
+
);
|
|
253
286
|
expect(mockToolsService.callTool).toHaveBeenCalledWith({
|
|
254
287
|
id: expect.any(String),
|
|
255
288
|
type: "function",
|
|
256
289
|
function: {
|
|
257
290
|
name: "testTool",
|
|
258
|
-
arguments: { param1: "value1" }
|
|
259
|
-
}
|
|
291
|
+
arguments: { param1: "value1" },
|
|
292
|
+
},
|
|
260
293
|
});
|
|
261
294
|
});
|
|
262
295
|
|
|
263
296
|
it("should return error for invalid JSON arguments", async () => {
|
|
264
|
-
const result = await storeToolCallFunction(
|
|
297
|
+
const result = await storeToolCallFunction(
|
|
298
|
+
"resultVar",
|
|
299
|
+
"testTool",
|
|
300
|
+
"invalid json"
|
|
301
|
+
);
|
|
265
302
|
expect(result).toContain("Error: Invalid JSON in toolArgs parameter");
|
|
266
303
|
});
|
|
267
304
|
|
|
268
305
|
it("should return error for invalid variable names", async () => {
|
|
269
|
-
const result = await storeToolCallFunction(
|
|
306
|
+
const result = await storeToolCallFunction(
|
|
307
|
+
"invalid-name",
|
|
308
|
+
"testTool",
|
|
309
|
+
"{}"
|
|
310
|
+
);
|
|
270
311
|
expect(result).toContain("Error: Invalid variable name");
|
|
271
312
|
});
|
|
272
313
|
|
|
273
314
|
it("should handle tool execution errors", async () => {
|
|
274
|
-
mockToolsService.callTool.mockRejectedValue(
|
|
315
|
+
mockToolsService.callTool.mockRejectedValue(
|
|
316
|
+
new Error("Tool execution failed")
|
|
317
|
+
);
|
|
275
318
|
|
|
276
319
|
const result = await storeToolCallFunction("resultVar", "testTool", "{}");
|
|
277
|
-
expect(result).toContain(
|
|
320
|
+
expect(result).toContain(
|
|
321
|
+
"Error storing tool call result: Tool execution failed"
|
|
322
|
+
);
|
|
278
323
|
});
|
|
279
324
|
});
|
|
280
325
|
|
|
@@ -283,7 +328,9 @@ describe("CustomVariables", () => {
|
|
|
283
328
|
|
|
284
329
|
beforeEach(() => {
|
|
285
330
|
const addFunctionsCalls = mockToolsService.addFunctions.mock.calls;
|
|
286
|
-
const setVariableCall = addFunctionsCalls.find(
|
|
331
|
+
const setVariableCall = addFunctionsCalls.find(
|
|
332
|
+
(call) => call[0].setVariable
|
|
333
|
+
);
|
|
287
334
|
setVariableFunction = setVariableCall[0].setVariable;
|
|
288
335
|
});
|
|
289
336
|
|
|
@@ -294,14 +341,16 @@ describe("CustomVariables", () => {
|
|
|
294
341
|
const messages = [
|
|
295
342
|
{
|
|
296
343
|
role: "user" as const,
|
|
297
|
-
content: "{{greeting}} {{userName}}, how are you today?"
|
|
298
|
-
}
|
|
344
|
+
content: "{{greeting}} {{userName}}, how are you today?",
|
|
345
|
+
},
|
|
299
346
|
];
|
|
300
347
|
|
|
301
348
|
const processor = customVariables.createProcessor();
|
|
302
349
|
const modifiedMessages = [...messages];
|
|
303
350
|
await processor(messages, modifiedMessages);
|
|
304
|
-
expect(modifiedMessages[0].content).toBe(
|
|
351
|
+
expect(modifiedMessages[0].content).toBe(
|
|
352
|
+
"Hello Alice, how are you today?"
|
|
353
|
+
);
|
|
305
354
|
});
|
|
306
355
|
|
|
307
356
|
it("should handle multiple substitutions in single message", async () => {
|
|
@@ -312,8 +361,8 @@ describe("CustomVariables", () => {
|
|
|
312
361
|
const messages = [
|
|
313
362
|
{
|
|
314
363
|
role: "user" as const,
|
|
315
|
-
content: "{{var1}} and {{var2}} and {{var3}}"
|
|
316
|
-
}
|
|
364
|
+
content: "{{var1}} and {{var2}} and {{var3}}",
|
|
365
|
+
},
|
|
317
366
|
];
|
|
318
367
|
|
|
319
368
|
const processor = customVariables.createProcessor();
|
|
@@ -328,30 +377,34 @@ describe("CustomVariables", () => {
|
|
|
328
377
|
const messages = [
|
|
329
378
|
{
|
|
330
379
|
role: "user" as const,
|
|
331
|
-
content: "Configuration: {{config}}"
|
|
332
|
-
}
|
|
380
|
+
content: "Configuration: {{config}}",
|
|
381
|
+
},
|
|
333
382
|
];
|
|
334
383
|
|
|
335
384
|
const processor = customVariables.createProcessor();
|
|
336
385
|
const modifiedMessages = [...messages];
|
|
337
386
|
await processor(messages, modifiedMessages);
|
|
338
|
-
|
|
339
|
-
expect(modifiedMessages[0].content).toBe(
|
|
387
|
+
|
|
388
|
+
expect(modifiedMessages[0].content).toBe(
|
|
389
|
+
'Configuration: {"api":"v1","timeout":5000}'
|
|
390
|
+
);
|
|
340
391
|
});
|
|
341
392
|
|
|
342
393
|
it("should return error for undefined variables", async () => {
|
|
343
394
|
const messages = [
|
|
344
395
|
{
|
|
345
396
|
role: "user" as const,
|
|
346
|
-
content: "Hello {{undefinedVar}}"
|
|
347
|
-
}
|
|
397
|
+
content: "Hello {{undefinedVar}}",
|
|
398
|
+
},
|
|
348
399
|
];
|
|
349
400
|
|
|
350
401
|
const processor = customVariables.createProcessor();
|
|
351
402
|
const modifiedMessages = [...messages];
|
|
352
403
|
await processor(messages, modifiedMessages);
|
|
353
|
-
|
|
354
|
-
expect(modifiedMessages[0].content).toBe(
|
|
404
|
+
|
|
405
|
+
expect(modifiedMessages[0].content).toBe(
|
|
406
|
+
'{{ERROR: Variable "undefinedVar" is not defined}}'
|
|
407
|
+
);
|
|
355
408
|
});
|
|
356
409
|
|
|
357
410
|
it("should handle partial substitutions with mixed defined/undefined vars", async () => {
|
|
@@ -360,15 +413,17 @@ describe("CustomVariables", () => {
|
|
|
360
413
|
const messages = [
|
|
361
414
|
{
|
|
362
415
|
role: "user" as const,
|
|
363
|
-
content: "{{defined}} and {{undefined}}"
|
|
364
|
-
}
|
|
416
|
+
content: "{{defined}} and {{undefined}}",
|
|
417
|
+
},
|
|
365
418
|
];
|
|
366
419
|
|
|
367
420
|
const processor = customVariables.createProcessor();
|
|
368
421
|
const modifiedMessages = [...messages];
|
|
369
422
|
await processor(messages, modifiedMessages);
|
|
370
|
-
|
|
371
|
-
expect(modifiedMessages[0].content).toBe(
|
|
423
|
+
|
|
424
|
+
expect(modifiedMessages[0].content).toBe(
|
|
425
|
+
'value and {{ERROR: Variable "undefined" is not defined}}'
|
|
426
|
+
);
|
|
372
427
|
});
|
|
373
428
|
|
|
374
429
|
it("should preserve message structure while substituting content", async () => {
|
|
@@ -378,18 +433,18 @@ describe("CustomVariables", () => {
|
|
|
378
433
|
{
|
|
379
434
|
role: "assistant" as const,
|
|
380
435
|
content: "Original {{test}} content",
|
|
381
|
-
metadata: { id: "test-id" }
|
|
382
|
-
}
|
|
436
|
+
metadata: { id: "test-id" },
|
|
437
|
+
},
|
|
383
438
|
];
|
|
384
439
|
|
|
385
440
|
const processor = customVariables.createProcessor();
|
|
386
441
|
const modifiedMessages = [...messages];
|
|
387
442
|
await processor(messages, modifiedMessages);
|
|
388
|
-
|
|
443
|
+
|
|
389
444
|
expect(modifiedMessages[0]).toEqual({
|
|
390
445
|
role: "assistant",
|
|
391
446
|
content: "Original replaced content",
|
|
392
|
-
metadata: { id: "test-id" }
|
|
447
|
+
metadata: { id: "test-id" },
|
|
393
448
|
});
|
|
394
449
|
});
|
|
395
450
|
|
|
@@ -400,8 +455,8 @@ describe("CustomVariables", () => {
|
|
|
400
455
|
const messages = [
|
|
401
456
|
{
|
|
402
457
|
role: "user" as const,
|
|
403
|
-
content: "{{outerVar}} value"
|
|
404
|
-
}
|
|
458
|
+
content: "{{outerVar}} value",
|
|
459
|
+
},
|
|
405
460
|
];
|
|
406
461
|
|
|
407
462
|
const processor = customVariables.createProcessor();
|
|
@@ -417,8 +472,8 @@ describe("CustomVariables", () => {
|
|
|
417
472
|
const messages = [
|
|
418
473
|
{
|
|
419
474
|
role: "user" as const,
|
|
420
|
-
content: "Empty: '{{empty}}' Spaces: '{{spaces}}'"
|
|
421
|
-
}
|
|
475
|
+
content: "Empty: '{{empty}}' Spaces: '{{spaces}}'",
|
|
476
|
+
},
|
|
422
477
|
];
|
|
423
478
|
|
|
424
479
|
const processor = customVariables.createProcessor();
|
|
@@ -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
|
+
});
|