Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions docs/cli/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,7 @@ they appear in the UI.
| Show Color | `tools.shell.showColor` | Show color in shell output. | `false` |
| Approval Mode | `tools.approvalMode` | The default approval mode for tool execution. 'default' prompts for approval, 'auto_edit' auto-approves edit tools, and 'plan' is read-only mode. 'yolo' is not supported yet. | `"default"` |
| Use Ripgrep | `tools.useRipgrep` | Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance. | `true` |
| Enable Tool Output Truncation | `tools.enableToolOutputTruncation` | Enable truncation of large tool outputs. | `true` |
| Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Truncate tool output if it is larger than this many characters. Set to -1 to disable. | `4000000` |
| Tool Output Truncation Lines | `tools.truncateToolOutputLines` | The number of lines to keep when truncating tool output. | `1000` |
| Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Maximum characters to show when truncating large tool outputs. Set to 0 or negative to disable truncation. | `40000` |
| Disable LLM Correction | `tools.disableLLMCorrection` | Disable LLM-based error correction for edit tools. When enabled, tools will fail immediately if exact string matches are not found, instead of attempting to self-correct. | `true` |

### Security
Expand Down
16 changes: 3 additions & 13 deletions docs/get-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -720,20 +720,10 @@ their corresponding top-level category object in your `settings.json` file.
implementation. Provides faster search performance.
- **Default:** `true`

- **`tools.enableToolOutputTruncation`** (boolean):
- **Description:** Enable truncation of large tool outputs.
- **Default:** `true`
- **Requires restart:** Yes

- **`tools.truncateToolOutputThreshold`** (number):
- **Description:** Truncate tool output if it is larger than this many
characters. Set to -1 to disable.
- **Default:** `4000000`
- **Requires restart:** Yes

- **`tools.truncateToolOutputLines`** (number):
- **Description:** The number of lines to keep when truncating tool output.
- **Default:** `1000`
- **Description:** Maximum characters to show when truncating large tool
outputs. Set to 0 or negative to disable truncation.
- **Default:** `40000`
- **Requires restart:** Yes

- **`tools.disableLLMCorrection`** (boolean):
Expand Down
2 changes: 0 additions & 2 deletions packages/a2a-server/src/utils/testing_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import type {
import {
ApprovalMode,
DEFAULT_GEMINI_MODEL,
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
GeminiClient,
HookSystem,
Expand Down Expand Up @@ -47,7 +46,6 @@ export function createMockConfig(
} as Storage,
getTruncateToolOutputThreshold: () =>
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
getTruncateToolOutputLines: () => DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
getActiveModel: vi.fn().mockReturnValue(DEFAULT_GEMINI_MODEL),
getDebugMode: vi.fn().mockReturnValue(false),
getContentGeneratorConfig: vi.fn().mockReturnValue({ model: 'gemini-pro' }),
Expand Down
2 changes: 0 additions & 2 deletions packages/cli/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -824,8 +824,6 @@ export async function loadCliConfig(
skipNextSpeakerCheck: settings.model?.skipNextSpeakerCheck,
enablePromptCompletion: settings.general?.enablePromptCompletion,
truncateToolOutputThreshold: settings.tools?.truncateToolOutputThreshold,
truncateToolOutputLines: settings.tools?.truncateToolOutputLines,
enableToolOutputTruncation: settings.tools?.enableToolOutputTruncation,
eventEmitter: coreEvents,
useWriteTodos: argv.useWriteTodos ?? settings.useWriteTodos,
output: {
Expand Down
21 changes: 1 addition & 20 deletions packages/cli/src/config/settingsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
// --------------------------------------------------------------------------

import {
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
DEFAULT_MODEL_CONFIGS,
type MCPServerConfig,
Expand Down Expand Up @@ -1158,32 +1157,14 @@ const SETTINGS_SCHEMA = {
'Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance.',
showInDialog: true,
},
enableToolOutputTruncation: {
type: 'boolean',
label: 'Enable Tool Output Truncation',
category: 'General',
requiresRestart: true,
default: true,
description: 'Enable truncation of large tool outputs.',
showInDialog: true,
},
truncateToolOutputThreshold: {
type: 'number',
label: 'Tool Output Truncation Threshold',
category: 'General',
requiresRestart: true,
default: DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
description:
'Truncate tool output if it is larger than this many characters. Set to -1 to disable.',
showInDialog: true,
},
truncateToolOutputLines: {
type: 'number',
label: 'Tool Output Truncation Lines',
category: 'General',
requiresRestart: true,
default: DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
description: 'The number of lines to keep when truncating tool output.',
'Maximum characters to show when truncating large tool outputs. Set to 0 or negative to disable truncation.',
showInDialog: true,
},
disableLLMCorrection: {
Expand Down
1 change: 0 additions & 1 deletion packages/cli/src/config/settings_repro.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ describe('Settings Repro', () => {
showColor: true,
enableInteractiveShell: true,
},
truncateToolOutputLines: 100,
},
experimental: {
useModelRouter: false,
Expand Down
2 changes: 0 additions & 2 deletions packages/cli/src/ui/components/SettingsDialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,6 @@ describe('SettingsDialog', () => {
},
tools: {
truncateToolOutputThreshold: 50000,
truncateToolOutputLines: 1000,
},
context: {
discoveryMaxDirs: 500,
Expand Down Expand Up @@ -1473,7 +1472,6 @@ describe('SettingsDialog', () => {
enableInteractiveShell: true,
useRipgrep: true,
truncateToolOutputThreshold: 25000,
truncateToolOutputLines: 500,
},
security: {
folderTrust: {
Expand Down
2 changes: 0 additions & 2 deletions packages/cli/src/ui/hooks/useToolScheduler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import type {
AnyToolInvocation,
} from '@google/gemini-cli-core';
import {
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
ToolConfirmationOutcome,
ApprovalMode,
Expand Down Expand Up @@ -70,7 +69,6 @@ const mockConfig = {
getProjectTempDir: () => '/tmp',
},
getTruncateToolOutputThreshold: () => DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
getTruncateToolOutputLines: () => DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
getAllowedTools: vi.fn(() => []),
getActiveModel: () => PREVIEW_GEMINI_MODEL,
getContentGeneratorConfig: () => ({
Expand Down
5 changes: 1 addition & 4 deletions packages/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ export {
type AnsiLine,
type AnsiToken,
} from './src/utils/terminalSerializer.js';
export {
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
} from './src/config/config.js';
export { DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD } from './src/config/config.js';
export { detectIdeFromEnv } from './src/ide/detect-ide.js';
export {
logExtensionEnable,
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1181,8 +1181,8 @@ describe('Server Config (config.ts)', () => {
1000,
);
// 4 * (32000 - 1000) = 4 * 31000 = 124000
// default is 4_000_000
expect(config.getTruncateToolOutputThreshold()).toBe(124000);
// default is 40_000, so min(124000, 40000) = 40000
expect(config.getTruncateToolOutputThreshold()).toBe(40_000);
});

it('should return the default threshold when the calculated value is larger', () => {
Expand All @@ -1192,8 +1192,8 @@ describe('Server Config (config.ts)', () => {
500_000,
);
// 4 * (2_000_000 - 500_000) = 4 * 1_500_000 = 6_000_000
// default is 4_000_000
expect(config.getTruncateToolOutputThreshold()).toBe(4_000_000);
// default is 40_000
expect(config.getTruncateToolOutputThreshold()).toBe(40_000);
});

it('should use a custom truncateToolOutputThreshold if provided', () => {
Expand Down
18 changes: 1 addition & 17 deletions packages/core/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,7 @@ export {
DEFAULT_MEMORY_FILE_FILTERING_OPTIONS,
};

export const DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD = 4_000_000;
export const DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES = 1000;
export const DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD = 40_000;

export class MCPServerConfig {
constructor(
Expand Down Expand Up @@ -443,8 +442,6 @@ export interface ConfigParameters {
extensionManagement?: boolean;
enablePromptCompletion?: boolean;
truncateToolOutputThreshold?: number;
truncateToolOutputLines?: number;
enableToolOutputTruncation?: boolean;
eventEmitter?: EventEmitter;
useWriteTodos?: boolean;
policyEngineConfig?: PolicyEngineConfig;
Expand Down Expand Up @@ -589,9 +586,7 @@ export class Config {
private readonly extensionManagement: boolean = true;
private readonly enablePromptCompletion: boolean = false;
private readonly truncateToolOutputThreshold: number;
private readonly truncateToolOutputLines: number;
private compressionTruncationCounter = 0;
private readonly enableToolOutputTruncation: boolean;
private initialized: boolean = false;
readonly storage: Storage;
private readonly fileExclusions: FileExclusions;
Expand Down Expand Up @@ -782,9 +777,6 @@ export class Config {
this.truncateToolOutputThreshold =
params.truncateToolOutputThreshold ??
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD;
this.truncateToolOutputLines =
params.truncateToolOutputLines ?? DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES;
this.enableToolOutputTruncation = params.enableToolOutputTruncation ?? true;
// // TODO(joshualitt): Re-evaluate the todo tool for 3 family.
this.useWriteTodos = isPreviewModel(this.model)
? false
Expand Down Expand Up @@ -2097,10 +2089,6 @@ export class Config {
return this.enablePromptCompletion;
}

getEnableToolOutputTruncation(): boolean {
return this.enableToolOutputTruncation;
}

getTruncateToolOutputThreshold(): number {
return Math.min(
// Estimate remaining context window in characters (1 token ~= 4 chars).
Expand All @@ -2110,10 +2098,6 @@ export class Config {
);
}

getTruncateToolOutputLines(): number {
return this.truncateToolOutputLines;
}

getNextCompressionTruncationId(): number {
return ++this.compressionTruncationCounter;
}
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/core/coreToolScheduler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import type {
MessageBus,
} from '../index.js';
import {
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
BaseDeclarativeTool,
BaseToolInvocation,
Expand Down Expand Up @@ -271,7 +270,6 @@ function createMockConfig(overrides: Partial<Config> = {}): Config {
},
getTruncateToolOutputThreshold: () =>
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
getTruncateToolOutputLines: () => DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
getToolRegistry: () => defaultToolRegistry,
getActiveModel: () => DEFAULT_GEMINI_MODEL,
getGeminiClient: () => null,
Expand Down
5 changes: 1 addition & 4 deletions packages/core/src/scheduler/tool-executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ describe('ToolExecutor', () => {
// Default mock implementation
vi.mocked(fileUtils.saveTruncatedToolOutput).mockResolvedValue({
outputFile: '/tmp/truncated_output.txt',
totalLines: 100,
});
vi.mocked(fileUtils.formatTruncatedToolOutput).mockReturnValue(
'TruncatedContent...',
Expand Down Expand Up @@ -180,9 +179,7 @@ describe('ToolExecutor', () => {

it('should truncate large shell output', async () => {
// 1. Setup Config for Truncation
vi.spyOn(config, 'getEnableToolOutputTruncation').mockReturnValue(true);
vi.spyOn(config, 'getTruncateToolOutputThreshold').mockReturnValue(10);
vi.spyOn(config, 'getTruncateToolOutputLines').mockReturnValue(5);

const mockTool = new MockTool({ name: SHELL_TOOL_NAME });
const invocation = mockTool.build({});
Expand Down Expand Up @@ -227,7 +224,7 @@ describe('ToolExecutor', () => {
expect(fileUtils.formatTruncatedToolOutput).toHaveBeenCalledWith(
longOutput,
'/tmp/truncated_output.txt',
5, // lines
10, // threshold (maxChars)
);

expect(result.status).toBe('success');
Expand Down
16 changes: 4 additions & 12 deletions packages/core/src/scheduler/tool-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,18 +204,11 @@ export class ToolExecutor {
const toolName = call.request.name;
const callId = call.request.callId;

if (
typeof content === 'string' &&
toolName === SHELL_TOOL_NAME &&
this.config.getEnableToolOutputTruncation() &&
this.config.getTruncateToolOutputThreshold() > 0 &&
this.config.getTruncateToolOutputLines() > 0
) {
const originalContentLength = content.length;
if (typeof content === 'string' && toolName === SHELL_TOOL_NAME) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forget, why do we need thr truncation logic at the tool executor level instead of inside of the shell tool?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we plan to expand this to other tools like grep and web_search which have large outputs, will explore this a bit and see if should proceed or just remove it.

const threshold = this.config.getTruncateToolOutputThreshold();
const lines = this.config.getTruncateToolOutputLines();

if (content.length > threshold) {
if (threshold > 0 && content.length > threshold) {
const originalContentLength = content.length;
const { outputFile: savedPath } = await saveTruncatedToolOutput(
content,
toolName,
Expand All @@ -224,7 +217,7 @@ export class ToolExecutor {
this.config.getSessionId(),
);
outputFile = savedPath;
content = formatTruncatedToolOutput(content, outputFile, lines);
content = formatTruncatedToolOutput(content, outputFile, threshold);

logToolOutputTruncated(
this.config,
Expand All @@ -233,7 +226,6 @@ export class ToolExecutor {
originalContentLength,
truncatedContentLength: content.length,
threshold,
lines,
}),
);
}
Expand Down
8 changes: 5 additions & 3 deletions packages/core/src/services/chatCompressionService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ describe('ChatCompressionService', () => {
getMessageBus: vi.fn().mockReturnValue(undefined),
getHookSystem: () => undefined,
getNextCompressionTruncationId: vi.fn().mockReturnValue(1),
getTruncateToolOutputThreshold: vi.fn().mockReturnValue(40000),
storage: {
getProjectTempDir: vi.fn().mockReturnValue(testTempDir),
},
Expand Down Expand Up @@ -581,10 +582,10 @@ describe('ChatCompressionService', () => {
const truncatedPart = shellResponse!.parts![0].functionResponse;
const content = truncatedPart?.response?.['output'] as string;

// DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD = 40000 -> head=8000 (20%), tail=32000 (80%)
expect(content).toContain(
'Output too large. Showing the last 4,000 characters of the output.',
'Showing first 8,000 and last 32,000 characters',
);
// It's a single line, so NO [LINE WIDTH TRUNCATED]
});

it('should use character-based truncation for massive single-line raw strings', async () => {
Expand Down Expand Up @@ -645,8 +646,9 @@ describe('ChatCompressionService', () => {
const truncatedPart = rawResponse!.parts![0].functionResponse;
const content = truncatedPart?.response?.['output'] as string;

// DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD = 40000 -> head=8000 (20%), tail=32000 (80%)
expect(content).toContain(
'Output too large. Showing the last 4,000 characters of the output.',
'Showing first 8,000 and last 32,000 characters',
);
});

Expand Down
8 changes: 1 addition & 7 deletions packages/core/src/services/chatCompressionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,6 @@ export const COMPRESSION_PRESERVE_THRESHOLD = 0.3;
*/
export const COMPRESSION_FUNCTION_RESPONSE_TOKEN_BUDGET = 50_000;

/**
* The number of lines to keep when truncating a function response during compression.
*/
export const COMPRESSION_TRUNCATE_LINES = 30;

/**
* Returns the index of the oldest item to keep when compressing. May return
* contents.length which indicates that everything should be compressed.
Expand Down Expand Up @@ -189,11 +184,10 @@ async function truncateHistoryToBudget(
config.storage.getProjectTempDir(),
);

// Prepare a honest, readable snippet of the tail.
const truncatedMessage = formatTruncatedToolOutput(
contentStr,
outputFile,
COMPRESSION_TRUNCATE_LINES,
config.getTruncateToolOutputThreshold(),
);

newParts.unshift({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1213,10 +1213,6 @@ export class ClearcutLogger {
EventMetadataKey.GEMINI_CLI_TOOL_OUTPUT_TRUNCATED_THRESHOLD,
value: JSON.stringify(event.threshold),
},
{
gemini_cli_key: EventMetadataKey.GEMINI_CLI_TOOL_OUTPUT_TRUNCATED_LINES,
value: JSON.stringify(event.lines),
},
];

const logEvent = this.createLogEvent(
Expand Down
Loading
Loading