@machina.ai/cell-cli-core 1.6.1-rc1 → 1.8.2-rc1
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/dist/index.d.ts +4 -3
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/src/agents/codebase-investigator.d.ts +11 -0
- package/dist/src/agents/codebase-investigator.js +73 -0
- package/dist/src/agents/codebase-investigator.js.map +1 -0
- package/dist/src/agents/executor.d.ts +88 -0
- package/dist/src/agents/executor.js +417 -0
- package/dist/src/agents/executor.js.map +1 -0
- package/dist/src/agents/executor.test.d.ts +6 -0
- package/dist/src/agents/executor.test.js +419 -0
- package/dist/src/agents/executor.test.js.map +1 -0
- package/dist/src/agents/invocation.d.ts +43 -0
- package/dist/src/agents/invocation.js +100 -0
- package/dist/src/agents/invocation.js.map +1 -0
- package/dist/src/agents/invocation.test.d.ts +6 -0
- package/dist/src/agents/invocation.test.js +206 -0
- package/dist/src/agents/invocation.test.js.map +1 -0
- package/dist/src/agents/registry.d.ts +35 -0
- package/dist/src/agents/registry.js +58 -0
- package/dist/src/agents/registry.js.map +1 -0
- package/dist/src/agents/registry.test.d.ts +6 -0
- package/dist/src/agents/registry.test.js +146 -0
- package/dist/src/agents/registry.test.js.map +1 -0
- package/dist/src/agents/schema-utils.d.ts +39 -0
- package/dist/src/agents/schema-utils.js +57 -0
- package/dist/src/agents/schema-utils.js.map +1 -0
- package/dist/src/agents/schema-utils.test.d.ts +6 -0
- package/dist/src/agents/schema-utils.test.js +144 -0
- package/dist/src/agents/schema-utils.test.js.map +1 -0
- package/dist/src/agents/subagent-tool-wrapper.d.ts +36 -0
- package/dist/src/agents/subagent-tool-wrapper.js +47 -0
- package/dist/src/agents/subagent-tool-wrapper.js.map +1 -0
- package/dist/src/agents/subagent-tool-wrapper.test.d.ts +6 -0
- package/dist/src/agents/subagent-tool-wrapper.test.js +105 -0
- package/dist/src/agents/subagent-tool-wrapper.test.js.map +1 -0
- package/dist/src/agents/types.d.ts +116 -0
- package/dist/src/agents/types.js +17 -0
- package/dist/src/agents/types.js.map +1 -0
- package/dist/src/agents/utils.d.ts +15 -0
- package/dist/src/agents/utils.js +29 -0
- package/dist/src/agents/utils.js.map +1 -0
- package/dist/src/agents/utils.test.d.ts +6 -0
- package/dist/src/agents/utils.test.js +87 -0
- package/dist/src/agents/utils.test.js.map +1 -0
- package/dist/src/config/config.d.ts +15 -8
- package/dist/src/config/config.js +43 -14
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/constants.d.ts +11 -0
- package/dist/src/config/constants.js +16 -0
- package/dist/src/config/constants.js.map +1 -0
- package/dist/src/core/baseLlmClient.d.ts +4 -0
- package/dist/src/core/baseLlmClient.js +24 -23
- package/dist/src/core/baseLlmClient.js.map +1 -1
- package/dist/src/core/baseLlmClient.test.js +76 -13
- package/dist/src/core/baseLlmClient.test.js.map +1 -1
- package/dist/src/core/client.d.ts +3 -2
- package/dist/src/core/client.js +37 -48
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +277 -119
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +33 -23
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +4 -3
- package/dist/src/core/geminiChat.js +55 -61
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +241 -29
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/logger.test.js +16 -16
- package/dist/src/core/logger.test.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +11 -11
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/prompts.js +3 -2
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/turn.d.ts +1 -4
- package/dist/src/core/turn.js +2 -12
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/ide/detect-ide.d.ts +45 -14
- package/dist/src/ide/detect-ide.js +32 -69
- package/dist/src/ide/detect-ide.js.map +1 -1
- package/dist/src/ide/detect-ide.test.js +29 -46
- package/dist/src/ide/detect-ide.test.js.map +1 -1
- package/dist/src/ide/ide-client.d.ts +4 -4
- package/dist/src/ide/ide-client.js +30 -29
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ide-client.test.js +8 -21
- package/dist/src/ide/ide-client.test.js.map +1 -1
- package/dist/src/ide/ide-installer.d.ts +2 -2
- package/dist/src/ide/ide-installer.js +7 -9
- package/dist/src/ide/ide-installer.js.map +1 -1
- package/dist/src/ide/ide-installer.test.js +20 -13
- package/dist/src/ide/ide-installer.test.js.map +1 -1
- package/dist/src/index.d.ts +5 -2
- package/dist/src/index.js +5 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/oauth-provider.d.ts +4 -1
- package/dist/src/mcp/oauth-provider.js +31 -25
- package/dist/src/mcp/oauth-provider.js.map +1 -1
- package/dist/src/mcp/sa-impersonation-provider.d.ts +33 -0
- package/dist/src/mcp/sa-impersonation-provider.js +130 -0
- package/dist/src/mcp/sa-impersonation-provider.js.map +1 -0
- package/dist/src/mcp/sa-impersonation-provider.test.d.ts +6 -0
- package/dist/src/mcp/sa-impersonation-provider.test.js +117 -0
- package/dist/src/mcp/sa-impersonation-provider.test.js.map +1 -0
- package/dist/src/policy/policy-engine.js +11 -2
- package/dist/src/policy/policy-engine.js.map +1 -1
- package/dist/src/policy/policy-engine.test.js +45 -0
- package/dist/src/policy/policy-engine.test.js.map +1 -1
- package/dist/src/routing/strategies/compositeStrategy.js +4 -3
- package/dist/src/routing/strategies/compositeStrategy.js.map +1 -1
- package/dist/src/services/chatRecordingService.d.ts +1 -1
- package/dist/src/services/chatRecordingService.js +1 -1
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/fileSystemService.d.ts +9 -0
- package/dist/src/services/fileSystemService.js +11 -0
- package/dist/src/services/fileSystemService.js.map +1 -1
- package/dist/src/services/shellExecutionService.d.ts +2 -0
- package/dist/src/services/shellExecutionService.js +48 -7
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +13 -4
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/telemetry/activity-detector.d.ts +41 -0
- package/dist/src/telemetry/activity-detector.js +61 -0
- package/dist/src/telemetry/activity-detector.js.map +1 -0
- package/dist/src/telemetry/activity-detector.test.d.ts +6 -0
- package/dist/src/telemetry/activity-detector.test.js +136 -0
- package/dist/src/telemetry/activity-detector.test.js.map +1 -0
- package/dist/src/telemetry/activity-types.d.ts +19 -0
- package/dist/src/telemetry/activity-types.js +21 -0
- package/dist/src/telemetry/activity-types.js.map +1 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +6 -2
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +155 -102
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.d.ts +1 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +178 -33
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +108 -100
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +116 -100
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/config.d.ts +31 -0
- package/dist/src/telemetry/config.js +74 -0
- package/dist/src/telemetry/config.js.map +1 -0
- package/dist/src/telemetry/config.test.d.ts +6 -0
- package/dist/src/telemetry/config.test.js +124 -0
- package/dist/src/telemetry/config.test.js.map +1 -0
- package/dist/src/telemetry/constants.d.ts +6 -12
- package/dist/src/telemetry/constants.js +7 -12
- package/dist/src/telemetry/constants.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +5 -1
- package/dist/src/telemetry/index.js +10 -0
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +3 -1
- package/dist/src/telemetry/loggers.js +78 -11
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.circular.js +3 -3
- package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +126 -11
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/metrics.d.ts +309 -11
- package/dist/src/telemetry/metrics.js +424 -110
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/metrics.test.js +538 -15
- package/dist/src/telemetry/metrics.test.js.map +1 -1
- package/dist/src/telemetry/sdk.js +1 -1
- package/dist/src/telemetry/sdk.js.map +1 -1
- package/dist/src/telemetry/sdk.test.js +13 -0
- package/dist/src/telemetry/sdk.test.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +19 -3
- package/dist/src/telemetry/types.js +37 -6
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.d.ts +1 -1
- package/dist/src/telemetry/uiTelemetry.js +2 -3
- package/dist/src/telemetry/uiTelemetry.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +13 -13
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
- package/dist/src/test-utils/mock-tool.d.ts +28 -3
- package/dist/src/test-utils/mock-tool.js +71 -1
- package/dist/src/test-utils/mock-tool.js.map +1 -1
- package/dist/src/tools/glob.js +2 -1
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/ls.js +1 -1
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +2 -12
- package/dist/src/tools/mcp-client.js +22 -65
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +9 -154
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/message-bus-integration.test.d.ts +6 -0
- package/dist/src/tools/message-bus-integration.test.js +183 -0
- package/dist/src/tools/message-bus-integration.test.js.map +1 -0
- package/dist/src/tools/shell.js +5 -2
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/smart-edit.d.ts +19 -0
- package/dist/src/tools/smart-edit.js +105 -3
- package/dist/src/tools/smart-edit.js.map +1 -1
- package/dist/src/tools/smart-edit.test.js +83 -5
- package/dist/src/tools/smart-edit.test.js.map +1 -1
- package/dist/src/tools/tool-error.d.ts +1 -0
- package/dist/src/tools/tool-error.js +1 -0
- package/dist/src/tools/tool-error.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +10 -10
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +11 -3
- package/dist/src/tools/tools.js +94 -3
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/write-todos.d.ts +25 -0
- package/dist/src/tools/write-todos.js +150 -0
- package/dist/src/tools/write-todos.js.map +1 -0
- package/dist/src/tools/write-todos.test.d.ts +6 -0
- package/dist/src/tools/write-todos.test.js +89 -0
- package/dist/src/tools/write-todos.test.js.map +1 -0
- package/dist/src/utils/bfsFileSearch.d.ts +1 -1
- package/dist/src/utils/flashFallback.test.js +2 -2
- package/dist/src/utils/flashFallback.test.js.map +1 -1
- package/dist/src/utils/getFolderStructure.d.ts +1 -1
- package/dist/src/utils/getFolderStructure.js +1 -1
- package/dist/src/utils/getFolderStructure.js.map +1 -1
- package/dist/src/utils/llm-edit-fixer.js +11 -1
- package/dist/src/utils/llm-edit-fixer.js.map +1 -1
- package/dist/src/utils/llm-edit-fixer.test.js +81 -0
- package/dist/src/utils/llm-edit-fixer.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.d.ts +1 -1
- package/dist/src/utils/memoryDiscovery.js +1 -1
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.js +13 -20
- package/dist/src/utils/memoryImportProcessor.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.test.js +14 -0
- package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
- package/dist/src/utils/retry.d.ts +3 -1
- package/dist/src/utils/retry.js +20 -5
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/retry.test.js +31 -2
- package/dist/src/utils/retry.test.js.map +1 -1
- package/dist/src/utils/schemaValidator.js +11 -1
- package/dist/src/utils/schemaValidator.js.map +1 -1
- package/dist/src/utils/schemaValidator.test.d.ts +6 -0
- package/dist/src/utils/schemaValidator.test.js +113 -0
- package/dist/src/utils/schemaValidator.test.js.map +1 -0
- package/dist/src/utils/shell-utils.js +5 -1
- package/dist/src/utils/shell-utils.js.map +1 -1
- package/dist/src/utils/shell-utils.test.js +5 -0
- package/dist/src/utils/shell-utils.test.js.map +1 -1
- package/dist/src/utils/terminalSerializer.d.ts +1 -4
- package/dist/src/utils/terminalSerializer.js +3 -3
- package/dist/src/utils/terminalSerializer.js.map +1 -1
- package/dist/src/utils/thoughtUtils.d.ts +21 -0
- package/dist/src/utils/thoughtUtils.js +39 -0
- package/dist/src/utils/thoughtUtils.js.map +1 -0
- package/dist/src/utils/thoughtUtils.test.d.ts +6 -0
- package/dist/src/utils/thoughtUtils.test.js +78 -0
- package/dist/src/utils/thoughtUtils.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/dist/src/test-utils/tools.d.ts +0 -45
- package/dist/src/test-utils/tools.js +0 -105
- package/dist/src/test-utils/tools.js.map +0 -1
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import { describe, it, expect, vi, beforeEach, afterEach, } from 'vitest';
|
|
7
|
-
import {
|
|
7
|
+
import { findCompressSplitPoint, isThinkingDefault, isThinkingSupported, GeminiClient, } from './client.js';
|
|
8
8
|
import { AuthType, } from './contentGenerator.js';
|
|
9
9
|
import {} from './geminiChat.js';
|
|
10
10
|
import { CompressionStatus, GeminiEventType, Turn, } from './turn.js';
|
|
@@ -15,6 +15,7 @@ import { setSimulate429 } from '../utils/testUtils.js';
|
|
|
15
15
|
import { tokenLimit } from './tokenLimits.js';
|
|
16
16
|
import { ideContextStore } from '../ide/ideContext.js';
|
|
17
17
|
import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
|
|
18
|
+
import { uiTelemetryService } from '../telemetry/uiTelemetry.js';
|
|
18
19
|
// Mock fs module to prevent actual file system operations during tests
|
|
19
20
|
const mockFileSystem = new Map();
|
|
20
21
|
vi.mock('node:fs', () => {
|
|
@@ -76,6 +77,12 @@ vi.mock('../telemetry/index.js', () => ({
|
|
|
76
77
|
logApiError: vi.fn(),
|
|
77
78
|
}));
|
|
78
79
|
vi.mock('../ide/ideContext.js');
|
|
80
|
+
vi.mock('../telemetry/uiTelemetry.js', () => ({
|
|
81
|
+
uiTelemetryService: {
|
|
82
|
+
setLastPromptTokenCount: vi.fn(),
|
|
83
|
+
getLastPromptTokenCount: vi.fn(),
|
|
84
|
+
},
|
|
85
|
+
}));
|
|
79
86
|
/**
|
|
80
87
|
* Array.fromAsync ponyfill, which will be available in es 2024.
|
|
81
88
|
*
|
|
@@ -88,42 +95,60 @@ async function fromAsync(promise) {
|
|
|
88
95
|
}
|
|
89
96
|
return results;
|
|
90
97
|
}
|
|
91
|
-
describe('
|
|
92
|
-
const history = [
|
|
93
|
-
{ role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66
|
|
94
|
-
{ role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68
|
|
95
|
-
{ role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66
|
|
96
|
-
{ role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68
|
|
97
|
-
{ role: 'user', parts: [{ text: 'This is the fifth message.' }] }, // JSON length: 65
|
|
98
|
-
];
|
|
99
|
-
// Total length: 333
|
|
98
|
+
describe('findCompressSplitPoint', () => {
|
|
100
99
|
it('should throw an error for non-positive numbers', () => {
|
|
101
|
-
expect(() =>
|
|
100
|
+
expect(() => findCompressSplitPoint([], 0)).toThrow('Fraction must be between 0 and 1');
|
|
102
101
|
});
|
|
103
102
|
it('should throw an error for a fraction greater than or equal to 1', () => {
|
|
104
|
-
expect(() =>
|
|
103
|
+
expect(() => findCompressSplitPoint([], 1)).toThrow('Fraction must be between 0 and 1');
|
|
104
|
+
});
|
|
105
|
+
it('should handle an empty history', () => {
|
|
106
|
+
expect(findCompressSplitPoint([], 0.5)).toBe(0);
|
|
105
107
|
});
|
|
106
108
|
it('should handle a fraction in the middle', () => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
const history = [
|
|
110
|
+
{ role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66 (19%)
|
|
111
|
+
{ role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68 (40%)
|
|
112
|
+
{ role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66 (60%)
|
|
113
|
+
{ role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68 (80%)
|
|
114
|
+
{ role: 'user', parts: [{ text: 'This is the fifth message.' }] }, // JSON length: 65 (100%)
|
|
115
|
+
];
|
|
116
|
+
expect(findCompressSplitPoint(history, 0.5)).toBe(4);
|
|
113
117
|
});
|
|
114
|
-
it('should handle a fraction
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
it('should handle a fraction of last index', () => {
|
|
119
|
+
const history = [
|
|
120
|
+
{ role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66 (19%)
|
|
121
|
+
{ role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68 (40%)
|
|
122
|
+
{ role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66 (60%)
|
|
123
|
+
{ role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68 (80%)
|
|
124
|
+
{ role: 'user', parts: [{ text: 'This is the fifth message.' }] }, // JSON length: 65 (100%)
|
|
125
|
+
];
|
|
126
|
+
expect(findCompressSplitPoint(history, 0.9)).toBe(4);
|
|
121
127
|
});
|
|
122
|
-
it('should handle
|
|
123
|
-
|
|
128
|
+
it('should handle a fraction of after last index', () => {
|
|
129
|
+
const history = [
|
|
130
|
+
{ role: 'user', parts: [{ text: 'This is the first message.' }] }, // JSON length: 66 (24%%)
|
|
131
|
+
{ role: 'model', parts: [{ text: 'This is the second message.' }] }, // JSON length: 68 (50%)
|
|
132
|
+
{ role: 'user', parts: [{ text: 'This is the third message.' }] }, // JSON length: 66 (74%)
|
|
133
|
+
{ role: 'model', parts: [{ text: 'This is the fourth message.' }] }, // JSON length: 68 (100%)
|
|
134
|
+
];
|
|
135
|
+
expect(findCompressSplitPoint(history, 0.8)).toBe(4);
|
|
136
|
+
});
|
|
137
|
+
it('should return earlier splitpoint if no valid ones are after threshhold', () => {
|
|
138
|
+
const history = [
|
|
139
|
+
{ role: 'user', parts: [{ text: 'This is the first message.' }] },
|
|
140
|
+
{ role: 'model', parts: [{ text: 'This is the second message.' }] },
|
|
141
|
+
{ role: 'user', parts: [{ text: 'This is the third message.' }] },
|
|
142
|
+
{ role: 'model', parts: [{ functionCall: {} }] },
|
|
143
|
+
];
|
|
144
|
+
// Can't return 4 because the previous item has a function call.
|
|
145
|
+
expect(findCompressSplitPoint(history, 0.99)).toBe(2);
|
|
124
146
|
});
|
|
125
147
|
it('should handle a history with only one item', () => {
|
|
126
|
-
|
|
148
|
+
const historyWithEmptyParts = [
|
|
149
|
+
{ role: 'user', parts: [{ text: 'Message 1' }] },
|
|
150
|
+
];
|
|
151
|
+
expect(findCompressSplitPoint(historyWithEmptyParts, 0.5)).toBe(0);
|
|
127
152
|
});
|
|
128
153
|
it('should handle history with weird parts', () => {
|
|
129
154
|
const historyWithEmptyParts = [
|
|
@@ -131,7 +156,7 @@ describe('findIndexAfterFraction', () => {
|
|
|
131
156
|
{ role: 'model', parts: [{ fileData: { fileUri: 'derp' } }] },
|
|
132
157
|
{ role: 'user', parts: [{ text: 'Message 2' }] },
|
|
133
158
|
];
|
|
134
|
-
expect(
|
|
159
|
+
expect(findCompressSplitPoint(historyWithEmptyParts, 0.5)).toBe(2);
|
|
135
160
|
});
|
|
136
161
|
});
|
|
137
162
|
describe('isThinkingSupported', () => {
|
|
@@ -168,6 +193,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
168
193
|
let mockGenerateContentFn;
|
|
169
194
|
beforeEach(async () => {
|
|
170
195
|
vi.resetAllMocks();
|
|
196
|
+
vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
|
|
171
197
|
mockGenerateContentFn = vi.fn().mockResolvedValue({
|
|
172
198
|
candidates: [{ content: { parts: [{ text: '{"key": "value"}' }] } }],
|
|
173
199
|
});
|
|
@@ -176,7 +202,6 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
176
202
|
mockContentGenerator = {
|
|
177
203
|
generateContent: mockGenerateContentFn,
|
|
178
204
|
generateContentStream: vi.fn(),
|
|
179
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 100 }),
|
|
180
205
|
batchEmbedContents: vi.fn(),
|
|
181
206
|
};
|
|
182
207
|
// Because the GeminiClient constructor kicks off an async process (startChat)
|
|
@@ -299,72 +324,128 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
299
324
|
function setup({ chatHistory = [
|
|
300
325
|
{ role: 'user', parts: [{ text: 'Long conversation' }] },
|
|
301
326
|
{ role: 'model', parts: [{ text: 'Long response' }] },
|
|
302
|
-
], } = {}) {
|
|
303
|
-
const
|
|
304
|
-
getHistory: vi.fn()
|
|
327
|
+
], originalTokenCount = 1000, summaryText = 'This is a summary.', } = {}) {
|
|
328
|
+
const mockOriginalChat = {
|
|
329
|
+
getHistory: vi.fn((_curated) => chatHistory),
|
|
305
330
|
setHistory: vi.fn(),
|
|
306
331
|
};
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
332
|
+
client['chat'] = mockOriginalChat;
|
|
333
|
+
vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
|
|
334
|
+
mockGenerateContentFn.mockResolvedValue({
|
|
335
|
+
candidates: [
|
|
336
|
+
{
|
|
337
|
+
content: {
|
|
338
|
+
role: 'model',
|
|
339
|
+
parts: [{ text: summaryText }],
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
],
|
|
343
|
+
});
|
|
344
|
+
// Calculate what the new history will be
|
|
345
|
+
const splitPoint = findCompressSplitPoint(chatHistory, 0.7); // 1 - 0.3
|
|
346
|
+
const historyToKeep = chatHistory.slice(splitPoint);
|
|
347
|
+
// This is the history that the new chat will have.
|
|
348
|
+
// It includes the default startChat history + the extra history from tryCompressChat
|
|
349
|
+
const newCompressedHistory = [
|
|
350
|
+
// Mocked envParts + canned response from startChat
|
|
351
|
+
{
|
|
352
|
+
role: 'user',
|
|
353
|
+
parts: [{ text: 'Mocked env context' }],
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
role: 'model',
|
|
357
|
+
parts: [{ text: 'Got it. Thanks for the context!' }],
|
|
358
|
+
},
|
|
359
|
+
// extraHistory from tryCompressChat
|
|
360
|
+
{
|
|
361
|
+
role: 'user',
|
|
362
|
+
parts: [{ text: summaryText }],
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
role: 'model',
|
|
366
|
+
parts: [{ text: 'Got it. Thanks for the additional context!' }],
|
|
367
|
+
},
|
|
368
|
+
...historyToKeep,
|
|
369
|
+
];
|
|
370
|
+
const mockNewChat = {
|
|
371
|
+
getHistory: vi.fn().mockReturnValue(newCompressedHistory),
|
|
372
|
+
setHistory: vi.fn(),
|
|
373
|
+
};
|
|
374
|
+
client['startChat'] = vi
|
|
375
|
+
.fn()
|
|
376
|
+
.mockResolvedValue(mockNewChat);
|
|
377
|
+
const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
|
|
378
|
+
const estimatedNewTokenCount = Math.floor(totalChars / 4);
|
|
379
|
+
return {
|
|
380
|
+
client,
|
|
381
|
+
mockOriginalChat,
|
|
382
|
+
mockNewChat,
|
|
383
|
+
estimatedNewTokenCount,
|
|
384
|
+
};
|
|
313
385
|
}
|
|
314
386
|
describe('when compression inflates the token count', () => {
|
|
315
387
|
it('allows compression to be forced/manual after a failure', async () => {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
388
|
+
// Call 1 (Fails): Setup with a long summary to inflate tokens
|
|
389
|
+
const longSummary = 'long summary '.repeat(100);
|
|
390
|
+
const { client, estimatedNewTokenCount: inflatedTokenCount } = setup({
|
|
391
|
+
originalTokenCount: 100,
|
|
392
|
+
summaryText: longSummary,
|
|
319
393
|
});
|
|
394
|
+
expect(inflatedTokenCount).toBeGreaterThan(100); // Ensure setup is correct
|
|
320
395
|
await client.tryCompressChat('prompt-id-4', false); // Fails
|
|
321
|
-
|
|
396
|
+
// Call 2 (Forced): Re-setup with a short summary
|
|
397
|
+
const shortSummary = 'short';
|
|
398
|
+
const { estimatedNewTokenCount: compressedTokenCount } = setup({
|
|
399
|
+
originalTokenCount: 100,
|
|
400
|
+
summaryText: shortSummary,
|
|
401
|
+
});
|
|
402
|
+
expect(compressedTokenCount).toBeLessThanOrEqual(100); // Ensure setup is correct
|
|
403
|
+
const result = await client.tryCompressChat('prompt-id-4', true); // Forced
|
|
322
404
|
expect(result).toEqual({
|
|
323
405
|
compressionStatus: CompressionStatus.COMPRESSED,
|
|
324
|
-
newTokenCount:
|
|
325
|
-
originalTokenCount:
|
|
406
|
+
newTokenCount: compressedTokenCount,
|
|
407
|
+
originalTokenCount: 100,
|
|
326
408
|
});
|
|
327
409
|
});
|
|
328
410
|
it('yields the result even if the compression inflated the tokens', async () => {
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
|
|
411
|
+
const longSummary = 'long summary '.repeat(100);
|
|
412
|
+
const { client, estimatedNewTokenCount } = setup({
|
|
413
|
+
originalTokenCount: 100,
|
|
414
|
+
summaryText: longSummary,
|
|
332
415
|
});
|
|
416
|
+
expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
|
|
333
417
|
const result = await client.tryCompressChat('prompt-id-4', false);
|
|
334
418
|
expect(result).toEqual({
|
|
335
419
|
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
336
|
-
newTokenCount:
|
|
337
|
-
originalTokenCount:
|
|
420
|
+
newTokenCount: estimatedNewTokenCount,
|
|
421
|
+
originalTokenCount: 100,
|
|
338
422
|
});
|
|
423
|
+
// IMPORTANT: The change in client.ts means setLastPromptTokenCount is NOT called on failure
|
|
424
|
+
expect(uiTelemetryService.setLastPromptTokenCount).not.toHaveBeenCalled();
|
|
339
425
|
});
|
|
340
426
|
it('does not manipulate the source chat', async () => {
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
it('restores the history back to the original', async () => {
|
|
346
|
-
vi.mocked(tokenLimit).mockReturnValue(1000);
|
|
347
|
-
vi.mocked(mockContentGenerator.countTokens).mockResolvedValue({
|
|
348
|
-
totalTokens: 999,
|
|
349
|
-
});
|
|
350
|
-
const originalHistory = [
|
|
351
|
-
{ role: 'user', parts: [{ text: 'what is your wisdom?' }] },
|
|
352
|
-
{ role: 'model', parts: [{ text: 'some wisdom' }] },
|
|
353
|
-
{ role: 'user', parts: [{ text: 'ahh that is a good a wisdom' }] },
|
|
354
|
-
];
|
|
355
|
-
const { client } = setup({
|
|
356
|
-
chatHistory: originalHistory,
|
|
427
|
+
const longSummary = 'long summary '.repeat(100);
|
|
428
|
+
const { client, mockOriginalChat, estimatedNewTokenCount } = setup({
|
|
429
|
+
originalTokenCount: 100,
|
|
430
|
+
summaryText: longSummary,
|
|
357
431
|
});
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
432
|
+
expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
|
|
433
|
+
await client.tryCompressChat('prompt-id-4', false);
|
|
434
|
+
// On failure, the chat should NOT be replaced
|
|
435
|
+
expect(client['chat']).toBe(mockOriginalChat);
|
|
361
436
|
});
|
|
362
437
|
it('will not attempt to compress context after a failure', async () => {
|
|
363
|
-
const
|
|
364
|
-
|
|
438
|
+
const longSummary = 'long summary '.repeat(100);
|
|
439
|
+
const { client, estimatedNewTokenCount } = setup({
|
|
440
|
+
originalTokenCount: 100,
|
|
441
|
+
summaryText: longSummary,
|
|
442
|
+
});
|
|
443
|
+
expect(estimatedNewTokenCount).toBeGreaterThan(100); // Ensure setup is correct
|
|
444
|
+
await client.tryCompressChat('prompt-id-4', false); // This fails and sets hasFailedCompressionAttempt = true
|
|
445
|
+
// This call should now be a NOOP
|
|
365
446
|
const result = await client.tryCompressChat('prompt-id-5', false);
|
|
366
|
-
//
|
|
367
|
-
expect(
|
|
447
|
+
// generateContent (for summary) should only have been called once
|
|
448
|
+
expect(mockGenerateContentFn).toHaveBeenCalledTimes(1);
|
|
368
449
|
expect(result).toEqual({
|
|
369
450
|
compressionStatus: CompressionStatus.NOOP,
|
|
370
451
|
newTokenCount: 0,
|
|
@@ -378,17 +459,16 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
378
459
|
mockGetHistory.mockReturnValue([
|
|
379
460
|
{ role: 'user', parts: [{ text: '...history...' }] },
|
|
380
461
|
]);
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
});
|
|
462
|
+
const originalTokenCount = MOCKED_TOKEN_LIMIT * 0.699;
|
|
463
|
+
vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
|
|
384
464
|
const initialChat = client.getChat();
|
|
385
465
|
const result = await client.tryCompressChat('prompt-id-2', false);
|
|
386
466
|
const newChat = client.getChat();
|
|
387
467
|
expect(tokenLimit).toHaveBeenCalled();
|
|
388
468
|
expect(result).toEqual({
|
|
389
469
|
compressionStatus: CompressionStatus.NOOP,
|
|
390
|
-
newTokenCount:
|
|
391
|
-
originalTokenCount
|
|
470
|
+
newTokenCount: originalTokenCount,
|
|
471
|
+
originalTokenCount,
|
|
392
472
|
});
|
|
393
473
|
expect(newChat).toBe(initialChat);
|
|
394
474
|
});
|
|
@@ -400,21 +480,40 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
400
480
|
vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
|
|
401
481
|
contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
|
|
402
482
|
});
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
]);
|
|
483
|
+
const history = [{ role: 'user', parts: [{ text: '...history...' }] }];
|
|
484
|
+
mockGetHistory.mockReturnValue(history);
|
|
406
485
|
const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
486
|
+
vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
|
|
487
|
+
// We need to control the estimated new token count.
|
|
488
|
+
// We mock startChat to return a chat with a known history.
|
|
489
|
+
const summaryText = 'This is a summary.';
|
|
490
|
+
const splitPoint = findCompressSplitPoint(history, 0.7);
|
|
491
|
+
const historyToKeep = history.slice(splitPoint);
|
|
492
|
+
const newCompressedHistory = [
|
|
493
|
+
{ role: 'user', parts: [{ text: 'Mocked env context' }] },
|
|
494
|
+
{ role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
|
|
495
|
+
{ role: 'user', parts: [{ text: summaryText }] },
|
|
496
|
+
{
|
|
497
|
+
role: 'model',
|
|
498
|
+
parts: [{ text: 'Got it. Thanks for the additional context!' }],
|
|
499
|
+
},
|
|
500
|
+
...historyToKeep,
|
|
501
|
+
];
|
|
502
|
+
const mockNewChat = {
|
|
503
|
+
getHistory: vi.fn().mockReturnValue(newCompressedHistory),
|
|
504
|
+
};
|
|
505
|
+
client['startChat'] = vi
|
|
506
|
+
.fn()
|
|
507
|
+
.mockResolvedValue(mockNewChat);
|
|
508
|
+
const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
|
|
509
|
+
const newTokenCount = Math.floor(totalChars / 4);
|
|
411
510
|
// Mock the summary response from the chat
|
|
412
511
|
mockGenerateContentFn.mockResolvedValue({
|
|
413
512
|
candidates: [
|
|
414
513
|
{
|
|
415
514
|
content: {
|
|
416
515
|
role: 'model',
|
|
417
|
-
parts: [{ text:
|
|
516
|
+
parts: [{ text: summaryText }],
|
|
418
517
|
},
|
|
419
518
|
},
|
|
420
519
|
],
|
|
@@ -424,6 +523,8 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
424
523
|
tokens_before: originalTokenCount,
|
|
425
524
|
tokens_after: newTokenCount,
|
|
426
525
|
}));
|
|
526
|
+
expect(uiTelemetryService.setLastPromptTokenCount).toHaveBeenCalledWith(newTokenCount);
|
|
527
|
+
expect(uiTelemetryService.setLastPromptTokenCount).toHaveBeenCalledTimes(1);
|
|
427
528
|
});
|
|
428
529
|
it('should trigger summarization if token count is at threshold with contextPercentageThreshold setting', async () => {
|
|
429
530
|
const MOCKED_TOKEN_LIMIT = 1000;
|
|
@@ -432,21 +533,39 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
432
533
|
vi.spyOn(client['config'], 'getChatCompression').mockReturnValue({
|
|
433
534
|
contextPercentageThreshold: MOCKED_CONTEXT_PERCENTAGE_THRESHOLD,
|
|
434
535
|
});
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
]);
|
|
536
|
+
const history = [{ role: 'user', parts: [{ text: '...history...' }] }];
|
|
537
|
+
mockGetHistory.mockReturnValue(history);
|
|
438
538
|
const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
539
|
+
vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
|
|
540
|
+
// Mock summary and new chat
|
|
541
|
+
const summaryText = 'This is a summary.';
|
|
542
|
+
const splitPoint = findCompressSplitPoint(history, 0.7);
|
|
543
|
+
const historyToKeep = history.slice(splitPoint);
|
|
544
|
+
const newCompressedHistory = [
|
|
545
|
+
{ role: 'user', parts: [{ text: 'Mocked env context' }] },
|
|
546
|
+
{ role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
|
|
547
|
+
{ role: 'user', parts: [{ text: summaryText }] },
|
|
548
|
+
{
|
|
549
|
+
role: 'model',
|
|
550
|
+
parts: [{ text: 'Got it. Thanks for the additional context!' }],
|
|
551
|
+
},
|
|
552
|
+
...historyToKeep,
|
|
553
|
+
];
|
|
554
|
+
const mockNewChat = {
|
|
555
|
+
getHistory: vi.fn().mockReturnValue(newCompressedHistory),
|
|
556
|
+
};
|
|
557
|
+
client['startChat'] = vi
|
|
558
|
+
.fn()
|
|
559
|
+
.mockResolvedValue(mockNewChat);
|
|
560
|
+
const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
|
|
561
|
+
const newTokenCount = Math.floor(totalChars / 4);
|
|
443
562
|
// Mock the summary response from the chat
|
|
444
563
|
mockGenerateContentFn.mockResolvedValue({
|
|
445
564
|
candidates: [
|
|
446
565
|
{
|
|
447
566
|
content: {
|
|
448
567
|
role: 'model',
|
|
449
|
-
parts: [{ text:
|
|
568
|
+
parts: [{ text: summaryText }],
|
|
450
569
|
},
|
|
451
570
|
},
|
|
452
571
|
],
|
|
@@ -468,7 +587,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
468
587
|
it('should not compress across a function call response', async () => {
|
|
469
588
|
const MOCKED_TOKEN_LIMIT = 1000;
|
|
470
589
|
vi.mocked(tokenLimit).mockReturnValue(MOCKED_TOKEN_LIMIT);
|
|
471
|
-
|
|
590
|
+
const history = [
|
|
472
591
|
{ role: 'user', parts: [{ text: '...history 1...' }] },
|
|
473
592
|
{ role: 'model', parts: [{ text: '...history 2...' }] },
|
|
474
593
|
{ role: 'user', parts: [{ text: '...history 3...' }] },
|
|
@@ -485,19 +604,43 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
485
604
|
{ role: 'model', parts: [{ text: '...history 10...' }] },
|
|
486
605
|
// Instead we will break here.
|
|
487
606
|
{ role: 'user', parts: [{ text: '...history 10...' }] },
|
|
488
|
-
]
|
|
607
|
+
];
|
|
608
|
+
mockGetHistory.mockReturnValue(history);
|
|
489
609
|
const originalTokenCount = 1000 * 0.7;
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
610
|
+
vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
|
|
611
|
+
// Mock summary and new chat
|
|
612
|
+
const summaryText = 'This is a summary.';
|
|
613
|
+
const splitPoint = findCompressSplitPoint(history, 0.7); // This should be 10
|
|
614
|
+
expect(splitPoint).toBe(10); // Verify split point logic
|
|
615
|
+
const historyToKeep = history.slice(splitPoint); // Should keep last user message
|
|
616
|
+
expect(historyToKeep).toEqual([
|
|
617
|
+
{ role: 'user', parts: [{ text: '...history 10...' }] },
|
|
618
|
+
]);
|
|
619
|
+
const newCompressedHistory = [
|
|
620
|
+
{ role: 'user', parts: [{ text: 'Mocked env context' }] },
|
|
621
|
+
{ role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
|
|
622
|
+
{ role: 'user', parts: [{ text: summaryText }] },
|
|
623
|
+
{
|
|
624
|
+
role: 'model',
|
|
625
|
+
parts: [{ text: 'Got it. Thanks for the additional context!' }],
|
|
626
|
+
},
|
|
627
|
+
...historyToKeep,
|
|
628
|
+
];
|
|
629
|
+
const mockNewChat = {
|
|
630
|
+
getHistory: vi.fn().mockReturnValue(newCompressedHistory),
|
|
631
|
+
};
|
|
632
|
+
client['startChat'] = vi
|
|
633
|
+
.fn()
|
|
634
|
+
.mockResolvedValue(mockNewChat);
|
|
635
|
+
const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
|
|
636
|
+
const newTokenCount = Math.floor(totalChars / 4);
|
|
494
637
|
// Mock the summary response from the chat
|
|
495
638
|
mockGenerateContentFn.mockResolvedValue({
|
|
496
639
|
candidates: [
|
|
497
640
|
{
|
|
498
641
|
content: {
|
|
499
642
|
role: 'model',
|
|
500
|
-
parts: [{ text:
|
|
643
|
+
parts: [{ text: summaryText }],
|
|
501
644
|
},
|
|
502
645
|
},
|
|
503
646
|
],
|
|
@@ -515,35 +658,53 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
515
658
|
});
|
|
516
659
|
// Assert that the chat was reset
|
|
517
660
|
expect(newChat).not.toBe(initialChat);
|
|
518
|
-
// 1. standard start context message
|
|
519
|
-
// 2. standard canned
|
|
520
|
-
// 3. compressed summary message
|
|
521
|
-
// 4. standard canned
|
|
522
|
-
// 5. The last user message (
|
|
661
|
+
// 1. standard start context message (env)
|
|
662
|
+
// 2. standard canned model response
|
|
663
|
+
// 3. compressed summary message (user)
|
|
664
|
+
// 4. standard canned model response
|
|
665
|
+
// 5. The last user message (historyToKeep)
|
|
523
666
|
expect(newChat.getHistory().length).toEqual(5);
|
|
524
667
|
});
|
|
525
668
|
it('should always trigger summarization when force is true, regardless of token count', async () => {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
669
|
+
const history = [{ role: 'user', parts: [{ text: '...history...' }] }];
|
|
670
|
+
mockGetHistory.mockReturnValue(history);
|
|
671
|
+
const originalTokenCount = 100; // Well below threshold, but > estimated new count
|
|
672
|
+
vi.mocked(uiTelemetryService.getLastPromptTokenCount).mockReturnValue(originalTokenCount);
|
|
673
|
+
// Mock summary and new chat
|
|
674
|
+
const summaryText = 'This is a summary.';
|
|
675
|
+
const splitPoint = findCompressSplitPoint(history, 0.7);
|
|
676
|
+
const historyToKeep = history.slice(splitPoint);
|
|
677
|
+
const newCompressedHistory = [
|
|
678
|
+
{ role: 'user', parts: [{ text: 'Mocked env context' }] },
|
|
679
|
+
{ role: 'model', parts: [{ text: 'Got it. Thanks for the context!' }] },
|
|
680
|
+
{ role: 'user', parts: [{ text: summaryText }] },
|
|
681
|
+
{
|
|
682
|
+
role: 'model',
|
|
683
|
+
parts: [{ text: 'Got it. Thanks for the additional context!' }],
|
|
684
|
+
},
|
|
685
|
+
...historyToKeep,
|
|
686
|
+
];
|
|
687
|
+
const mockNewChat = {
|
|
688
|
+
getHistory: vi.fn().mockReturnValue(newCompressedHistory),
|
|
689
|
+
};
|
|
690
|
+
client['startChat'] = vi
|
|
691
|
+
.fn()
|
|
692
|
+
.mockResolvedValue(mockNewChat);
|
|
693
|
+
const totalChars = newCompressedHistory.reduce((total, content) => total + JSON.stringify(content).length, 0);
|
|
694
|
+
const newTokenCount = Math.floor(totalChars / 4);
|
|
534
695
|
// Mock the summary response from the chat
|
|
535
696
|
mockGenerateContentFn.mockResolvedValue({
|
|
536
697
|
candidates: [
|
|
537
698
|
{
|
|
538
699
|
content: {
|
|
539
700
|
role: 'model',
|
|
540
|
-
parts: [{ text:
|
|
701
|
+
parts: [{ text: summaryText }],
|
|
541
702
|
},
|
|
542
703
|
},
|
|
543
704
|
],
|
|
544
705
|
});
|
|
545
706
|
const initialChat = client.getChat();
|
|
546
|
-
const result = await client.tryCompressChat('prompt-id-1',
|
|
707
|
+
const result = await client.tryCompressChat('prompt-id-1', true); // force = true
|
|
547
708
|
const newChat = client.getChat();
|
|
548
709
|
expect(mockGenerateContentFn).toHaveBeenCalled();
|
|
549
710
|
expect(result).toEqual({
|
|
@@ -581,9 +742,6 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
581
742
|
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
582
743
|
},
|
|
583
744
|
{ compressionStatus: CompressionStatus.NOOP },
|
|
584
|
-
{
|
|
585
|
-
compressionStatus: CompressionStatus.COMPRESSION_FAILED_TOKEN_COUNT_ERROR,
|
|
586
|
-
},
|
|
587
745
|
])('does not emit a compression event when the status is $compressionStatus', async ({ compressionStatus }) => {
|
|
588
746
|
// Arrange
|
|
589
747
|
const mockStream = (async function* () {
|