Skip to content

Commit 4e05db8

Browse files
committed
Make compression threshold editable in the UI.
1 parent c6a7107 commit 4e05db8

10 files changed

Lines changed: 41 additions & 109 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
@@ -245,13 +245,13 @@ their corresponding top-level category object in your `settings.json` file.
245245
example `{"run_shell_command": {"tokenBudget": 2000}}`
246246
- **Default:** `undefined`
247247

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

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

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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1545,7 +1545,7 @@ describe('loadCliConfig with includeDirectories', () => {
15451545
});
15461546
});
15471547

1548-
describe('loadCliConfig chatCompression', () => {
1548+
describe('loadCliConfig compressionThreshold', () => {
15491549
beforeEach(() => {
15501550
vi.resetAllMocks();
15511551
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
@@ -1558,28 +1558,24 @@ describe('loadCliConfig chatCompression', () => {
15581558
vi.restoreAllMocks();
15591559
});
15601560

1561-
it('should pass chatCompression settings to the core config', async () => {
1561+
it('should pass settings to the core config', async () => {
15621562
process.argv = ['node', 'script.js'];
15631563
const argv = await parseArguments({} as Settings);
15641564
const settings: Settings = {
15651565
model: {
1566-
chatCompression: {
1567-
contextPercentageThreshold: 0.5,
1568-
},
1566+
compressionThreshold: 0.5,
15691567
},
15701568
};
15711569
const config = await loadCliConfig(settings, 'test-session', argv);
1572-
expect(config.getChatCompression()).toEqual({
1573-
contextPercentageThreshold: 0.5,
1574-
});
1570+
expect(config.getCompressionThreshold()).toBe(0.5);
15751571
});
15761572

1577-
it('should have undefined chatCompression if not in settings', async () => {
1573+
it('should have undefined compressionThreshold if not in settings', async () => {
15781574
process.argv = ['node', 'script.js'];
15791575
const argv = await parseArguments({} as Settings);
15801576
const settings: Settings = {};
15811577
const config = await loadCliConfig(settings, 'test-session', argv);
1582-
expect(config.getChatCompression()).toBeUndefined();
1578+
expect(config.getCompressionThreshold()).toBeUndefined();
15831579
});
15841580
});
15851581

packages/cli/src/config/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ export async function loadCliConfig(
685685
noBrowser: !!process.env['NO_BROWSER'],
686686
summarizeToolOutput: settings.model?.summarizeToolOutput,
687687
ideMode,
688-
chatCompression: settings.model?.chatCompression,
688+
compressionThreshold: settings.model?.compressionThreshold,
689689
folderTrust,
690690
interactive,
691691
trustedFolder,

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

Lines changed: 15 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,15 +1104,15 @@ describe('Settings Loading and Merging', () => {
11041104
});
11051105
});
11061106

1107-
it('should merge chatCompression settings, with workspace taking precedence', () => {
1107+
it('should merge compressionThreshold settings, with workspace taking precedence', () => {
11081108
(mockFsExistsSync as Mock).mockReturnValue(true);
11091109
const userSettingsContent = {
11101110
general: {},
1111-
model: { chatCompression: { contextPercentageThreshold: 0.5 } },
1111+
model: { compressionThreshold: 0.5 },
11121112
};
11131113
const workspaceSettingsContent = {
11141114
general: {},
1115-
model: { chatCompression: { contextPercentageThreshold: 0.8 } },
1115+
model: { compressionThreshold: 0.8 },
11161116
};
11171117

11181118
(fs.readFileSync as Mock).mockImplementation(
@@ -1127,15 +1127,11 @@ describe('Settings Loading and Merging', () => {
11271127

11281128
const settings = loadSettings(MOCK_WORKSPACE_DIR);
11291129

1130-
expect(settings.user.settings.model?.chatCompression).toEqual({
1131-
contextPercentageThreshold: 0.5,
1132-
});
1133-
expect(settings.workspace.settings.model?.chatCompression).toEqual({
1134-
contextPercentageThreshold: 0.8,
1135-
});
1136-
expect(settings.merged.model?.chatCompression).toEqual({
1137-
contextPercentageThreshold: 0.8,
1138-
});
1130+
expect(settings.user.settings.model?.compressionThreshold).toEqual(0.5);
1131+
expect(settings.workspace.settings.model?.compressionThreshold).toEqual(
1132+
0.8,
1133+
);
1134+
expect(settings.merged.model?.compressionThreshold).toEqual(0.8);
11391135
});
11401136

11411137
it('should merge output format settings, with workspace taking precedence', () => {
@@ -1162,13 +1158,13 @@ describe('Settings Loading and Merging', () => {
11621158
expect(settings.merged.output?.format).toBe('json');
11631159
});
11641160

1165-
it('should handle chatCompression when only in user settings', () => {
1161+
it('should handle compressionThreshold when only in user settings', () => {
11661162
(mockFsExistsSync as Mock).mockImplementation(
11671163
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
11681164
);
11691165
const userSettingsContent = {
11701166
general: {},
1171-
model: { chatCompression: { contextPercentageThreshold: 0.5 } },
1167+
model: { compressionThreshold: 0.5 },
11721168
};
11731169
(fs.readFileSync as Mock).mockImplementation(
11741170
(p: fs.PathOrFileDescriptor) => {
@@ -1179,9 +1175,7 @@ describe('Settings Loading and Merging', () => {
11791175
);
11801176

11811177
const settings = loadSettings(MOCK_WORKSPACE_DIR);
1182-
expect(settings.merged.model?.chatCompression).toEqual({
1183-
contextPercentageThreshold: 0.5,
1184-
});
1178+
expect(settings.merged.model?.compressionThreshold).toEqual(0.5);
11851179
});
11861180

11871181
it('should have model as undefined if not in any settings file', () => {
@@ -1191,39 +1185,15 @@ describe('Settings Loading and Merging', () => {
11911185
expect(settings.merged.model).toBeUndefined();
11921186
});
11931187

1194-
it('should ignore chatCompression if contextPercentageThreshold is invalid', () => {
1195-
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
1196-
(mockFsExistsSync as Mock).mockImplementation(
1197-
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
1198-
);
1199-
const userSettingsContent = {
1200-
general: {},
1201-
model: { chatCompression: { contextPercentageThreshold: 1.5 } },
1202-
};
1203-
(fs.readFileSync as Mock).mockImplementation(
1204-
(p: fs.PathOrFileDescriptor) => {
1205-
if (p === USER_SETTINGS_PATH)
1206-
return JSON.stringify(userSettingsContent);
1207-
return '{}';
1208-
},
1209-
);
1210-
1211-
const settings = loadSettings(MOCK_WORKSPACE_DIR);
1212-
expect(settings.merged.model?.chatCompression).toEqual({
1213-
contextPercentageThreshold: 1.5,
1214-
});
1215-
warnSpy.mockRestore();
1216-
});
1217-
1218-
it('should deep merge chatCompression settings', () => {
1188+
it('should use user compressionThreshold if workspace does not define it', () => {
12191189
(mockFsExistsSync as Mock).mockReturnValue(true);
12201190
const userSettingsContent = {
12211191
general: {},
1222-
model: { chatCompression: { contextPercentageThreshold: 0.5 } },
1192+
model: { compressionThreshold: 0.5 },
12231193
};
12241194
const workspaceSettingsContent = {
12251195
general: {},
1226-
model: { chatCompression: {} },
1196+
model: {},
12271197
};
12281198

12291199
(fs.readFileSync as Mock).mockImplementation(
@@ -1238,9 +1208,7 @@ describe('Settings Loading and Merging', () => {
12381208

12391209
const settings = loadSettings(MOCK_WORKSPACE_DIR);
12401210

1241-
expect(settings.merged.model?.chatCompression).toEqual({
1242-
contextPercentageThreshold: 0.5,
1243-
});
1211+
expect(settings.merged.model?.compressionThreshold).toEqual(0.5);
12441212
});
12451213

12461214
it('should merge includeDirectories from all scopes', () => {
@@ -2025,9 +1993,6 @@ describe('Settings Loading and Merging', () => {
20251993
},
20261994
model: {
20271995
name: 'gemini-pro',
2028-
chatCompression: {
2029-
contextPercentageThreshold: 0.5,
2030-
},
20311996
},
20321997
mcpServers: {
20331998
'server-1': {
@@ -2046,9 +2011,6 @@ describe('Settings Loading and Merging', () => {
20462011
myTheme: {},
20472012
},
20482013
model: 'gemini-pro',
2049-
chatCompression: {
2050-
contextPercentageThreshold: 0.5,
2051-
},
20522014
mcpServers: {
20532015
'server-1': {
20542016
command: 'node server.js',
@@ -2088,9 +2050,6 @@ describe('Settings Loading and Merging', () => {
20882050
},
20892051
model: {
20902052
name: 'gemini-pro',
2091-
chatCompression: {
2092-
contextPercentageThreshold: 0.8,
2093-
},
20942053
},
20952054
context: {
20962055
fileName: 'CONTEXT.md',
@@ -2130,9 +2089,6 @@ describe('Settings Loading and Merging', () => {
21302089
theme: 'dark',
21312090
usageStatisticsEnabled: false,
21322091
model: 'gemini-pro',
2133-
chatCompression: {
2134-
contextPercentageThreshold: 0.8,
2135-
},
21362092
contextFileName: 'CONTEXT.md',
21372093
includeDirectories: ['/src'],
21382094
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
@@ -92,10 +92,6 @@ export interface BugCommandSettings {
9292
urlTemplate: string;
9393
}
9494

95-
export interface ChatCompressionSettings {
96-
contextPercentageThreshold?: number;
97-
}
98-
9995
export interface SummarizeToolOutputSettings {
10096
tokenBudget?: number;
10197
}
@@ -262,7 +258,7 @@ export interface ConfigParameters {
262258
folderTrust?: boolean;
263259
ideMode?: boolean;
264260
loadMemoryFromIncludeDirectories?: boolean;
265-
chatCompression?: ChatCompressionSettings;
261+
compressionThreshold?: number;
266262
interactive?: boolean;
267263
trustedFolder?: boolean;
268264
useRipgrep?: boolean;
@@ -359,7 +355,7 @@ export class Config {
359355
| undefined;
360356
private readonly experimentalZedIntegration: boolean = false;
361357
private readonly loadMemoryFromIncludeDirectories: boolean = false;
362-
private readonly chatCompression: ChatCompressionSettings | undefined;
358+
private readonly compressionThreshold: number | undefined;
363359
private readonly interactive: boolean;
364360
private readonly ptyInfo: string;
365361
private readonly trustedFolder: boolean | undefined;
@@ -462,7 +458,7 @@ export class Config {
462458
this.ideMode = params.ideMode ?? false;
463459
this.loadMemoryFromIncludeDirectories =
464460
params.loadMemoryFromIncludeDirectories ?? false;
465-
this.chatCompression = params.chatCompression;
461+
this.compressionThreshold = params.compressionThreshold;
466462
this.interactive = params.interactive ?? false;
467463
this.ptyInfo = params.ptyInfo ?? 'child_process';
468464
this.trustedFolder = params.trustedFolder;
@@ -1005,8 +1001,8 @@ export class Config {
10051001
this.fileSystemService = fileSystemService;
10061002
}
10071003

1008-
getChatCompression(): ChatCompressionSettings | undefined {
1009-
return this.chatCompression;
1004+
getCompressionThreshold(): number | undefined {
1005+
return this.compressionThreshold;
10101006
}
10111007

10121008
isInteractiveShellEnabled(): boolean {

packages/core/src/services/chatCompressionService.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ describe('findCompressSplitPoint', () => {
7070
expect(findCompressSplitPoint(history, 0.8)).toBe(4);
7171
});
7272

73-
it('should return earlier splitpoint if no valid ones are after threshhold', () => {
73+
it('should return earlier splitpoint if no valid ones are after threshold', () => {
7474
const history: Content[] = [
7575
{ role: 'user', parts: [{ text: 'This is the first message.' }] },
7676
{ role: 'model', parts: [{ text: 'This is the second message.' }] },
@@ -115,7 +115,7 @@ describe('ChatCompressionService', () => {
115115
getLastPromptTokenCount: vi.fn().mockReturnValue(500),
116116
} as unknown as GeminiChat;
117117
mockConfig = {
118-
getChatCompression: vi.fn(),
118+
getCompressionThreshold: vi.fn(),
119119
getContentGenerator: vi.fn(),
120120
} as unknown as Config;
121121

packages/core/src/services/chatCompressionService.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,7 @@ export class ChatCompressionService {
106106
// Don't compress if not forced and we are under the limit.
107107
if (!force) {
108108
const threshold =
109-
config.getChatCompression()?.contextPercentageThreshold ??
110-
DEFAULT_COMPRESSION_TOKEN_THRESHOLD;
109+
config.getCompressionThreshold() ?? DEFAULT_COMPRESSION_TOKEN_THRESHOLD;
111110
if (originalTokenCount < threshold * tokenLimit(model)) {
112111
return {
113112
newHistory: null,

0 commit comments

Comments
 (0)