mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-18 01:51:20 -07:00
fix(patch): cherry-pick 3332703 to release/v0.12.0-preview.5-pr-12317 to patch version v0.12.0-preview.5 and create version 0.12.0-preview.6 (#12334)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
This commit is contained in:
@@ -473,21 +473,6 @@ a few things you can try in order of recommendation:
|
||||
"loadMemoryFromIncludeDirectories": true
|
||||
```
|
||||
|
||||
- **`chatCompression`** (object):
|
||||
- **Description:** Controls the settings for chat history compression, both
|
||||
automatic and when manually invoked through the /compress command.
|
||||
- **Properties:**
|
||||
- **`contextPercentageThreshold`** (number): A value between 0 and 1 that
|
||||
specifies the token threshold for compression as a percentage of the
|
||||
model's total token limit. For example, a value of `0.6` will trigger
|
||||
compression when the chat history exceeds 60% of the token limit.
|
||||
- **Example:**
|
||||
```json
|
||||
"chatCompression": {
|
||||
"contextPercentageThreshold": 0.6
|
||||
}
|
||||
```
|
||||
|
||||
- **`showLineNumbers`** (boolean):
|
||||
- **Description:** Controls whether line numbers are displayed in code blocks
|
||||
in the CLI output.
|
||||
|
||||
@@ -245,13 +245,13 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
example `{"run_shell_command": {"tokenBudget": 2000}}`
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`model.chatCompression.contextPercentageThreshold`** (number):
|
||||
- **`model.compressionThreshold`** (number):
|
||||
- **Description:** Sets the threshold for chat history compression as a
|
||||
percentage of the model's total token limit. This is a value between 0 and 1
|
||||
fraction of the model's total token limit. This is a value between 0 and 1
|
||||
that applies to both automatic compression and the manual `/compress`
|
||||
command. For example, a value of `0.6` will trigger compression when the
|
||||
chat history exceeds 60% of the token limit.
|
||||
- **Default:** `0.7`
|
||||
- **Default:** `0.2`
|
||||
|
||||
- **`model.skipNextSpeakerCheck`** (boolean):
|
||||
- **Description:** Skip the next speaker check.
|
||||
|
||||
@@ -1545,7 +1545,7 @@ describe('loadCliConfig with includeDirectories', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadCliConfig chatCompression', () => {
|
||||
describe('loadCliConfig compressionThreshold', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(os.homedir).mockReturnValue('/mock/home/user');
|
||||
@@ -1558,28 +1558,24 @@ describe('loadCliConfig chatCompression', () => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should pass chatCompression settings to the core config', async () => {
|
||||
it('should pass settings to the core config', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {
|
||||
model: {
|
||||
chatCompression: {
|
||||
contextPercentageThreshold: 0.5,
|
||||
},
|
||||
compressionThreshold: 0.5,
|
||||
},
|
||||
};
|
||||
const config = await loadCliConfig(settings, 'test-session', argv);
|
||||
expect(config.getChatCompression()).toEqual({
|
||||
contextPercentageThreshold: 0.5,
|
||||
});
|
||||
expect(config.getCompressionThreshold()).toBe(0.5);
|
||||
});
|
||||
|
||||
it('should have undefined chatCompression if not in settings', async () => {
|
||||
it('should have undefined compressionThreshold if not in settings', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const argv = await parseArguments({} as Settings);
|
||||
const settings: Settings = {};
|
||||
const config = await loadCliConfig(settings, 'test-session', argv);
|
||||
expect(config.getChatCompression()).toBeUndefined();
|
||||
expect(config.getCompressionThreshold()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -684,7 +684,7 @@ export async function loadCliConfig(
|
||||
noBrowser: !!process.env['NO_BROWSER'],
|
||||
summarizeToolOutput: settings.model?.summarizeToolOutput,
|
||||
ideMode,
|
||||
chatCompression: settings.model?.chatCompression,
|
||||
compressionThreshold: settings.model?.compressionThreshold,
|
||||
folderTrust,
|
||||
interactive,
|
||||
trustedFolder,
|
||||
|
||||
@@ -1104,15 +1104,15 @@ describe('Settings Loading and Merging', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should merge chatCompression settings, with workspace taking precedence', () => {
|
||||
it('should merge compressionThreshold settings, with workspace taking precedence', () => {
|
||||
(mockFsExistsSync as Mock).mockReturnValue(true);
|
||||
const userSettingsContent = {
|
||||
general: {},
|
||||
model: { chatCompression: { contextPercentageThreshold: 0.5 } },
|
||||
model: { compressionThreshold: 0.5 },
|
||||
};
|
||||
const workspaceSettingsContent = {
|
||||
general: {},
|
||||
model: { chatCompression: { contextPercentageThreshold: 0.8 } },
|
||||
model: { compressionThreshold: 0.8 },
|
||||
};
|
||||
|
||||
(fs.readFileSync as Mock).mockImplementation(
|
||||
@@ -1127,15 +1127,11 @@ describe('Settings Loading and Merging', () => {
|
||||
|
||||
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
|
||||
expect(settings.user.settings.model?.chatCompression).toEqual({
|
||||
contextPercentageThreshold: 0.5,
|
||||
});
|
||||
expect(settings.workspace.settings.model?.chatCompression).toEqual({
|
||||
contextPercentageThreshold: 0.8,
|
||||
});
|
||||
expect(settings.merged.model?.chatCompression).toEqual({
|
||||
contextPercentageThreshold: 0.8,
|
||||
});
|
||||
expect(settings.user.settings.model?.compressionThreshold).toEqual(0.5);
|
||||
expect(settings.workspace.settings.model?.compressionThreshold).toEqual(
|
||||
0.8,
|
||||
);
|
||||
expect(settings.merged.model?.compressionThreshold).toEqual(0.8);
|
||||
});
|
||||
|
||||
it('should merge output format settings, with workspace taking precedence', () => {
|
||||
@@ -1162,13 +1158,13 @@ describe('Settings Loading and Merging', () => {
|
||||
expect(settings.merged.output?.format).toBe('json');
|
||||
});
|
||||
|
||||
it('should handle chatCompression when only in user settings', () => {
|
||||
it('should handle compressionThreshold when only in user settings', () => {
|
||||
(mockFsExistsSync as Mock).mockImplementation(
|
||||
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
|
||||
);
|
||||
const userSettingsContent = {
|
||||
general: {},
|
||||
model: { chatCompression: { contextPercentageThreshold: 0.5 } },
|
||||
model: { compressionThreshold: 0.5 },
|
||||
};
|
||||
(fs.readFileSync as Mock).mockImplementation(
|
||||
(p: fs.PathOrFileDescriptor) => {
|
||||
@@ -1179,9 +1175,7 @@ describe('Settings Loading and Merging', () => {
|
||||
);
|
||||
|
||||
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
expect(settings.merged.model?.chatCompression).toEqual({
|
||||
contextPercentageThreshold: 0.5,
|
||||
});
|
||||
expect(settings.merged.model?.compressionThreshold).toEqual(0.5);
|
||||
});
|
||||
|
||||
it('should have model as undefined if not in any settings file', () => {
|
||||
@@ -1191,39 +1185,15 @@ describe('Settings Loading and Merging', () => {
|
||||
expect(settings.merged.model).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should ignore chatCompression if contextPercentageThreshold is invalid', () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
(mockFsExistsSync as Mock).mockImplementation(
|
||||
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
|
||||
);
|
||||
const userSettingsContent = {
|
||||
general: {},
|
||||
model: { chatCompression: { contextPercentageThreshold: 1.5 } },
|
||||
};
|
||||
(fs.readFileSync as Mock).mockImplementation(
|
||||
(p: fs.PathOrFileDescriptor) => {
|
||||
if (p === USER_SETTINGS_PATH)
|
||||
return JSON.stringify(userSettingsContent);
|
||||
return '{}';
|
||||
},
|
||||
);
|
||||
|
||||
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
expect(settings.merged.model?.chatCompression).toEqual({
|
||||
contextPercentageThreshold: 1.5,
|
||||
});
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should deep merge chatCompression settings', () => {
|
||||
it('should use user compressionThreshold if workspace does not define it', () => {
|
||||
(mockFsExistsSync as Mock).mockReturnValue(true);
|
||||
const userSettingsContent = {
|
||||
general: {},
|
||||
model: { chatCompression: { contextPercentageThreshold: 0.5 } },
|
||||
model: { compressionThreshold: 0.5 },
|
||||
};
|
||||
const workspaceSettingsContent = {
|
||||
general: {},
|
||||
model: { chatCompression: {} },
|
||||
model: {},
|
||||
};
|
||||
|
||||
(fs.readFileSync as Mock).mockImplementation(
|
||||
@@ -1238,9 +1208,7 @@ describe('Settings Loading and Merging', () => {
|
||||
|
||||
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
|
||||
expect(settings.merged.model?.chatCompression).toEqual({
|
||||
contextPercentageThreshold: 0.5,
|
||||
});
|
||||
expect(settings.merged.model?.compressionThreshold).toEqual(0.5);
|
||||
});
|
||||
|
||||
it('should merge includeDirectories from all scopes', () => {
|
||||
@@ -2025,9 +1993,6 @@ describe('Settings Loading and Merging', () => {
|
||||
},
|
||||
model: {
|
||||
name: 'gemini-pro',
|
||||
chatCompression: {
|
||||
contextPercentageThreshold: 0.5,
|
||||
},
|
||||
},
|
||||
mcpServers: {
|
||||
'server-1': {
|
||||
@@ -2046,9 +2011,6 @@ describe('Settings Loading and Merging', () => {
|
||||
myTheme: {},
|
||||
},
|
||||
model: 'gemini-pro',
|
||||
chatCompression: {
|
||||
contextPercentageThreshold: 0.5,
|
||||
},
|
||||
mcpServers: {
|
||||
'server-1': {
|
||||
command: 'node server.js',
|
||||
@@ -2088,9 +2050,6 @@ describe('Settings Loading and Merging', () => {
|
||||
},
|
||||
model: {
|
||||
name: 'gemini-pro',
|
||||
chatCompression: {
|
||||
contextPercentageThreshold: 0.8,
|
||||
},
|
||||
},
|
||||
context: {
|
||||
fileName: 'CONTEXT.md',
|
||||
@@ -2130,9 +2089,6 @@ describe('Settings Loading and Merging', () => {
|
||||
theme: 'dark',
|
||||
usageStatisticsEnabled: false,
|
||||
model: 'gemini-pro',
|
||||
chatCompression: {
|
||||
contextPercentageThreshold: 0.8,
|
||||
},
|
||||
contextFileName: 'CONTEXT.md',
|
||||
includeDirectories: ['/src'],
|
||||
sandbox: true,
|
||||
|
||||
@@ -64,7 +64,7 @@ const MIGRATION_MAP: Record<string, string> = {
|
||||
autoAccept: 'tools.autoAccept',
|
||||
autoConfigureMaxOldSpaceSize: 'advanced.autoConfigureMemory',
|
||||
bugCommand: 'advanced.bugCommand',
|
||||
chatCompression: 'model.chatCompression',
|
||||
chatCompression: 'model.compressionThreshold',
|
||||
checkpointing: 'general.checkpointing',
|
||||
coreTools: 'tools.core',
|
||||
contextFileName: 'context.fileName',
|
||||
|
||||
@@ -14,7 +14,6 @@ import type {
|
||||
BugCommandSettings,
|
||||
TelemetrySettings,
|
||||
AuthType,
|
||||
ChatCompressionSettings,
|
||||
} from '@google/gemini-cli-core';
|
||||
import {
|
||||
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
|
||||
@@ -578,14 +577,15 @@ const SETTINGS_SCHEMA = {
|
||||
description: 'Settings for summarizing tool output.',
|
||||
showInDialog: false,
|
||||
},
|
||||
chatCompression: {
|
||||
type: 'object',
|
||||
label: 'Chat Compression',
|
||||
compressionThreshold: {
|
||||
type: 'number',
|
||||
label: 'Compression Threshold',
|
||||
category: 'Model',
|
||||
requiresRestart: false,
|
||||
default: undefined as ChatCompressionSettings | undefined,
|
||||
description: 'Chat compression settings.',
|
||||
showInDialog: false,
|
||||
default: 0.2 as number,
|
||||
description:
|
||||
'The fraction of context usage at which to trigger context compression (e.g. 0.2, 0.3).',
|
||||
showInDialog: true,
|
||||
},
|
||||
skipNextSpeakerCheck: {
|
||||
type: 'boolean',
|
||||
|
||||
@@ -92,10 +92,6 @@ export interface BugCommandSettings {
|
||||
urlTemplate: string;
|
||||
}
|
||||
|
||||
export interface ChatCompressionSettings {
|
||||
contextPercentageThreshold?: number;
|
||||
}
|
||||
|
||||
export interface SummarizeToolOutputSettings {
|
||||
tokenBudget?: number;
|
||||
}
|
||||
@@ -261,7 +257,7 @@ export interface ConfigParameters {
|
||||
folderTrust?: boolean;
|
||||
ideMode?: boolean;
|
||||
loadMemoryFromIncludeDirectories?: boolean;
|
||||
chatCompression?: ChatCompressionSettings;
|
||||
compressionThreshold?: number;
|
||||
interactive?: boolean;
|
||||
trustedFolder?: boolean;
|
||||
useRipgrep?: boolean;
|
||||
@@ -357,7 +353,7 @@ export class Config {
|
||||
| undefined;
|
||||
private readonly experimentalZedIntegration: boolean = false;
|
||||
private readonly loadMemoryFromIncludeDirectories: boolean = false;
|
||||
private readonly chatCompression: ChatCompressionSettings | undefined;
|
||||
private readonly compressionThreshold: number | undefined;
|
||||
private readonly interactive: boolean;
|
||||
private readonly ptyInfo: string;
|
||||
private readonly trustedFolder: boolean | undefined;
|
||||
@@ -460,7 +456,7 @@ export class Config {
|
||||
this.ideMode = params.ideMode ?? false;
|
||||
this.loadMemoryFromIncludeDirectories =
|
||||
params.loadMemoryFromIncludeDirectories ?? false;
|
||||
this.chatCompression = params.chatCompression;
|
||||
this.compressionThreshold = params.compressionThreshold;
|
||||
this.interactive = params.interactive ?? false;
|
||||
this.ptyInfo = params.ptyInfo ?? 'child_process';
|
||||
this.trustedFolder = params.trustedFolder;
|
||||
@@ -992,8 +988,8 @@ export class Config {
|
||||
this.fileSystemService = fileSystemService;
|
||||
}
|
||||
|
||||
getChatCompression(): ChatCompressionSettings | undefined {
|
||||
return this.chatCompression;
|
||||
getCompressionThreshold(): number | undefined {
|
||||
return this.compressionThreshold;
|
||||
}
|
||||
|
||||
isInteractiveShellEnabled(): boolean {
|
||||
|
||||
@@ -72,7 +72,7 @@ describe('findCompressSplitPoint', () => {
|
||||
expect(findCompressSplitPoint(history, 0.8)).toBe(4);
|
||||
});
|
||||
|
||||
it('should return earlier splitpoint if no valid ones are after threshhold', () => {
|
||||
it('should return earlier splitpoint if no valid ones are after threshold', () => {
|
||||
const history: Content[] = [
|
||||
{ role: 'user', parts: [{ text: 'This is the first message.' }] },
|
||||
{ role: 'model', parts: [{ text: 'This is the second message.' }] },
|
||||
@@ -116,7 +116,7 @@ describe('ChatCompressionService', () => {
|
||||
getHistory: vi.fn(),
|
||||
} as unknown as GeminiChat;
|
||||
mockConfig = {
|
||||
getChatCompression: vi.fn(),
|
||||
getCompressionThreshold: vi.fn(),
|
||||
getContentGenerator: vi.fn(),
|
||||
} as unknown as Config;
|
||||
|
||||
|
||||
@@ -107,8 +107,7 @@ export class ChatCompressionService {
|
||||
// Don't compress if not forced and we are under the limit.
|
||||
if (!force) {
|
||||
const threshold =
|
||||
config.getChatCompression()?.contextPercentageThreshold ??
|
||||
DEFAULT_COMPRESSION_TOKEN_THRESHOLD;
|
||||
config.getCompressionThreshold() ?? DEFAULT_COMPRESSION_TOKEN_THRESHOLD;
|
||||
if (originalTokenCount < threshold * tokenLimit(model)) {
|
||||
return {
|
||||
newHistory: null,
|
||||
|
||||
Reference in New Issue
Block a user