Skip to content

Commit cf20ea3

Browse files
scidominogemini-cli-robot
authored andcommitted
Make compression threshold editable in the UI. (#12317)
# Conflicts: # packages/cli/src/config/config.test.ts # packages/core/src/services/chatCompressionService.test.ts # packages/core/src/services/chatCompressionService.ts
1 parent f4f3727 commit cf20ea3

10 files changed

Lines changed: 558 additions & 100 deletions

File tree

docs/get-started/configuration-v1.md

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -473,21 +473,6 @@ a few things you can try in order of recommendation:
473473
"loadMemoryFromIncludeDirectories": true
474474
```
475475

476-
- **`chatCompression`** (object):
477-
- **Description:** Controls the settings for chat history compression, both
478-
automatic and when manually invoked through the /compress command.
479-
- **Properties:**
480-
- **`contextPercentageThreshold`** (number): A value between 0 and 1 that
481-
specifies the token threshold for compression as a percentage of the
482-
model's total token limit. For example, a value of `0.6` will trigger
483-
compression when the chat history exceeds 60% of the token limit.
484-
- **Example:**
485-
```json
486-
"chatCompression": {
487-
"contextPercentageThreshold": 0.6
488-
}
489-
```
490-
491476
- **`showLineNumbers`** (boolean):
492477
- **Description:** Controls whether line numbers are displayed in code blocks
493478
in the CLI output.

docs/get-started/configuration.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,13 +244,13 @@ their corresponding top-level category object in your `settings.json` file.
244244
example `{"run_shell_command": {"tokenBudget": 2000}}`
245245
- **Default:** `undefined`
246246

247-
- **`model.chatCompression.contextPercentageThreshold`** (number):
247+
- **`model.compressionThreshold`** (number):
248248
- **Description:** Sets the threshold for chat history compression as a
249-
percentage of the model's total token limit. This is a value between 0 and 1
249+
fraction of the model's total token limit. This is a value between 0 and 1
250250
that applies to both automatic compression and the manual `/compress`
251251
command. For example, a value of `0.6` will trigger compression when the
252252
chat history exceeds 60% of the token limit.
253-
- **Default:** `0.7`
253+
- **Default:** `0.2`
254254

255255
- **`model.skipNextSpeakerCheck`** (boolean):
256256
- **Description:** Skip the next speaker check.

packages/cli/src/config/config.test.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,9 +1572,13 @@ describe('loadCliConfig with includeDirectories', () => {
15721572
});
15731573
});
15741574

1575+
<<<<<<< HEAD
15751576
describe('loadCliConfig chatCompression', () => {
15761577
const originalArgv = process.argv;
15771578

1579+
=======
1580+
describe('loadCliConfig compressionThreshold', () => {
1581+
>>>>>>> 3332703f (Make compression threshold editable in the UI. (#12317))
15781582
beforeEach(() => {
15791583
vi.resetAllMocks();
15801584
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
@@ -1587,28 +1591,36 @@ describe('loadCliConfig chatCompression', () => {
15871591
vi.restoreAllMocks();
15881592
});
15891593

1590-
it('should pass chatCompression settings to the core config', async () => {
1594+
it('should pass settings to the core config', async () => {
15911595
process.argv = ['node', 'script.js'];
15921596
const argv = await parseArguments({} as Settings);
15931597
const settings: Settings = {
15941598
model: {
1595-
chatCompression: {
1596-
contextPercentageThreshold: 0.5,
1597-
},
1599+
compressionThreshold: 0.5,
15981600
},
15991601
};
1602+
<<<<<<< HEAD
16001603
const config = await loadCliConfig(settings, [], 'test-session', argv);
16011604
expect(config.getChatCompression()).toEqual({
16021605
contextPercentageThreshold: 0.5,
16031606
});
1607+
=======
1608+
const config = await loadCliConfig(settings, 'test-session', argv);
1609+
expect(config.getCompressionThreshold()).toBe(0.5);
1610+
>>>>>>> 3332703f (Make compression threshold editable in the UI. (#12317))
16041611
});
16051612

1606-
it('should have undefined chatCompression if not in settings', async () => {
1613+
it('should have undefined compressionThreshold if not in settings', async () => {
16071614
process.argv = ['node', 'script.js'];
16081615
const argv = await parseArguments({} as Settings);
16091616
const settings: Settings = {};
1617+
<<<<<<< HEAD
16101618
const config = await loadCliConfig(settings, [], 'test-session', argv);
16111619
expect(config.getChatCompression()).toBeUndefined();
1620+
=======
1621+
const config = await loadCliConfig(settings, 'test-session', argv);
1622+
expect(config.getCompressionThreshold()).toBeUndefined();
1623+
>>>>>>> 3332703f (Make compression threshold editable in the UI. (#12317))
16121624
});
16131625
});
16141626

packages/cli/src/config/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ export async function loadCliConfig(
610610
noBrowser: !!process.env['NO_BROWSER'],
611611
summarizeToolOutput: settings.model?.summarizeToolOutput,
612612
ideMode,
613-
chatCompression: settings.model?.chatCompression,
613+
compressionThreshold: settings.model?.compressionThreshold,
614614
folderTrust,
615615
interactive,
616616
trustedFolder,

packages/cli/src/config/settings.test.ts

Lines changed: 15 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,15 +1051,15 @@ describe('Settings Loading and Merging', () => {
10511051
});
10521052
});
10531053

1054-
it('should merge chatCompression settings, with workspace taking precedence', () => {
1054+
it('should merge compressionThreshold settings, with workspace taking precedence', () => {
10551055
(mockFsExistsSync as Mock).mockReturnValue(true);
10561056
const userSettingsContent = {
10571057
general: {},
1058-
model: { chatCompression: { contextPercentageThreshold: 0.5 } },
1058+
model: { compressionThreshold: 0.5 },
10591059
};
10601060
const workspaceSettingsContent = {
10611061
general: {},
1062-
model: { chatCompression: { contextPercentageThreshold: 0.8 } },
1062+
model: { compressionThreshold: 0.8 },
10631063
};
10641064

10651065
(fs.readFileSync as Mock).mockImplementation(
@@ -1074,15 +1074,11 @@ describe('Settings Loading and Merging', () => {
10741074

10751075
const settings = loadSettings(MOCK_WORKSPACE_DIR);
10761076

1077-
expect(settings.user.settings.model?.chatCompression).toEqual({
1078-
contextPercentageThreshold: 0.5,
1079-
});
1080-
expect(settings.workspace.settings.model?.chatCompression).toEqual({
1081-
contextPercentageThreshold: 0.8,
1082-
});
1083-
expect(settings.merged.model?.chatCompression).toEqual({
1084-
contextPercentageThreshold: 0.8,
1085-
});
1077+
expect(settings.user.settings.model?.compressionThreshold).toEqual(0.5);
1078+
expect(settings.workspace.settings.model?.compressionThreshold).toEqual(
1079+
0.8,
1080+
);
1081+
expect(settings.merged.model?.compressionThreshold).toEqual(0.8);
10861082
});
10871083

10881084
it('should merge output format settings, with workspace taking precedence', () => {
@@ -1109,13 +1105,13 @@ describe('Settings Loading and Merging', () => {
11091105
expect(settings.merged.output?.format).toBe('json');
11101106
});
11111107

1112-
it('should handle chatCompression when only in user settings', () => {
1108+
it('should handle compressionThreshold when only in user settings', () => {
11131109
(mockFsExistsSync as Mock).mockImplementation(
11141110
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
11151111
);
11161112
const userSettingsContent = {
11171113
general: {},
1118-
model: { chatCompression: { contextPercentageThreshold: 0.5 } },
1114+
model: { compressionThreshold: 0.5 },
11191115
};
11201116
(fs.readFileSync as Mock).mockImplementation(
11211117
(p: fs.PathOrFileDescriptor) => {
@@ -1126,9 +1122,7 @@ describe('Settings Loading and Merging', () => {
11261122
);
11271123

11281124
const settings = loadSettings(MOCK_WORKSPACE_DIR);
1129-
expect(settings.merged.model?.chatCompression).toEqual({
1130-
contextPercentageThreshold: 0.5,
1131-
});
1125+
expect(settings.merged.model?.compressionThreshold).toEqual(0.5);
11321126
});
11331127

11341128
it('should have model as undefined if not in any settings file', () => {
@@ -1138,39 +1132,15 @@ describe('Settings Loading and Merging', () => {
11381132
expect(settings.merged.model).toBeUndefined();
11391133
});
11401134

1141-
it('should ignore chatCompression if contextPercentageThreshold is invalid', () => {
1142-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
1143-
(mockFsExistsSync as Mock).mockImplementation(
1144-
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
1145-
);
1146-
const userSettingsContent = {
1147-
general: {},
1148-
model: { chatCompression: { contextPercentageThreshold: 1.5 } },
1149-
};
1150-
(fs.readFileSync as Mock).mockImplementation(
1151-
(p: fs.PathOrFileDescriptor) => {
1152-
if (p === USER_SETTINGS_PATH)
1153-
return JSON.stringify(userSettingsContent);
1154-
return '{}';
1155-
},
1156-
);
1157-
1158-
const settings = loadSettings(MOCK_WORKSPACE_DIR);
1159-
expect(settings.merged.model?.chatCompression).toEqual({
1160-
contextPercentageThreshold: 1.5,
1161-
});
1162-
warnSpy.mockRestore();
1163-
});
1164-
1165-
it('should deep merge chatCompression settings', () => {
1135+
it('should use user compressionThreshold if workspace does not define it', () => {
11661136
(mockFsExistsSync as Mock).mockReturnValue(true);
11671137
const userSettingsContent = {
11681138
general: {},
1169-
model: { chatCompression: { contextPercentageThreshold: 0.5 } },
1139+
model: { compressionThreshold: 0.5 },
11701140
};
11711141
const workspaceSettingsContent = {
11721142
general: {},
1173-
model: { chatCompression: {} },
1143+
model: {},
11741144
};
11751145

11761146
(fs.readFileSync as Mock).mockImplementation(
@@ -1185,9 +1155,7 @@ describe('Settings Loading and Merging', () => {
11851155

11861156
const settings = loadSettings(MOCK_WORKSPACE_DIR);
11871157

1188-
expect(settings.merged.model?.chatCompression).toEqual({
1189-
contextPercentageThreshold: 0.5,
1190-
});
1158+
expect(settings.merged.model?.compressionThreshold).toEqual(0.5);
11911159
});
11921160

11931161
it('should merge includeDirectories from all scopes', () => {
@@ -1972,9 +1940,6 @@ describe('Settings Loading and Merging', () => {
19721940
},
19731941
model: {
19741942
name: 'gemini-pro',
1975-
chatCompression: {
1976-
contextPercentageThreshold: 0.5,
1977-
},
19781943
},
19791944
mcpServers: {
19801945
'server-1': {
@@ -1993,9 +1958,6 @@ describe('Settings Loading and Merging', () => {
19931958
myTheme: {},
19941959
},
19951960
model: 'gemini-pro',
1996-
chatCompression: {
1997-
contextPercentageThreshold: 0.5,
1998-
},
19991961
mcpServers: {
20001962
'server-1': {
20011963
command: 'node server.js',
@@ -2035,9 +1997,6 @@ describe('Settings Loading and Merging', () => {
20351997
},
20361998
model: {
20371999
name: 'gemini-pro',
2038-
chatCompression: {
2039-
contextPercentageThreshold: 0.8,
2040-
},
20412000
},
20422001
context: {
20432002
fileName: 'CONTEXT.md',
@@ -2077,9 +2036,6 @@ describe('Settings Loading and Merging', () => {
20772036
theme: 'dark',
20782037
usageStatisticsEnabled: false,
20792038
model: 'gemini-pro',
2080-
chatCompression: {
2081-
contextPercentageThreshold: 0.8,
2082-
},
20832039
contextFileName: 'CONTEXT.md',
20842040
includeDirectories: ['/src'],
20852041
sandbox: true,

packages/cli/src/config/settings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const MIGRATION_MAP: Record<string, string> = {
6464
autoAccept: 'tools.autoAccept',
6565
autoConfigureMaxOldSpaceSize: 'advanced.autoConfigureMemory',
6666
bugCommand: 'advanced.bugCommand',
67-
chatCompression: 'model.chatCompression',
67+
chatCompression: 'model.compressionThreshold',
6868
checkpointing: 'general.checkpointing',
6969
coreTools: 'tools.core',
7070
contextFileName: 'context.fileName',

packages/cli/src/config/settingsSchema.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import type {
1414
BugCommandSettings,
1515
TelemetrySettings,
1616
AuthType,
17-
ChatCompressionSettings,
1817
} from '@google/gemini-cli-core';
1918
import {
2019
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
@@ -578,14 +577,15 @@ const SETTINGS_SCHEMA = {
578577
description: 'Settings for summarizing tool output.',
579578
showInDialog: false,
580579
},
581-
chatCompression: {
582-
type: 'object',
583-
label: 'Chat Compression',
580+
compressionThreshold: {
581+
type: 'number',
582+
label: 'Compression Threshold',
584583
category: 'Model',
585584
requiresRestart: false,
586-
default: undefined as ChatCompressionSettings | undefined,
587-
description: 'Chat compression settings.',
588-
showInDialog: false,
585+
default: 0.2 as number,
586+
description:
587+
'The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3).',
588+
showInDialog: true,
589589
},
590590
skipNextSpeakerCheck: {
591591
type: 'boolean',

packages/core/src/config/config.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,6 @@ export interface BugCommandSettings {
9696
urlTemplate: string;
9797
}
9898

99-
export interface ChatCompressionSettings {
100-
contextPercentageThreshold?: number;
101-
}
102-
10399
export interface SummarizeToolOutputSettings {
104100
tokenBudget?: number;
105101
}
@@ -261,7 +257,7 @@ export interface ConfigParameters {
261257
folderTrust?: boolean;
262258
ideMode?: boolean;
263259
loadMemoryFromIncludeDirectories?: boolean;
264-
chatCompression?: ChatCompressionSettings;
260+
compressionThreshold?: number;
265261
interactive?: boolean;
266262
trustedFolder?: boolean;
267263
useRipgrep?: boolean;
@@ -354,7 +350,7 @@ export class Config {
354350
| undefined;
355351
private readonly experimentalZedIntegration: boolean = false;
356352
private readonly loadMemoryFromIncludeDirectories: boolean = false;
357-
private readonly chatCompression: ChatCompressionSettings | undefined;
353+
private readonly compressionThreshold: number | undefined;
358354
private readonly interactive: boolean;
359355
private readonly ptyInfo: string;
360356
private readonly trustedFolder: boolean | undefined;
@@ -453,7 +449,7 @@ export class Config {
453449
this.ideMode = params.ideMode ?? false;
454450
this.loadMemoryFromIncludeDirectories =
455451
params.loadMemoryFromIncludeDirectories ?? false;
456-
this.chatCompression = params.chatCompression;
452+
this.compressionThreshold = params.compressionThreshold;
457453
this.interactive = params.interactive ?? false;
458454
this.ptyInfo = params.ptyInfo ?? 'child_process';
459455
this.trustedFolder = params.trustedFolder;
@@ -977,8 +973,8 @@ export class Config {
977973
this.fileSystemService = fileSystemService;
978974
}
979975

980-
getChatCompression(): ChatCompressionSettings | undefined {
981-
return this.chatCompression;
976+
getCompressionThreshold(): number | undefined {
977+
return this.compressionThreshold;
982978
}
983979

984980
isInteractiveShellEnabled(): boolean {

0 commit comments

Comments
 (0)