mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-28 12:42:42 -07:00
feat(ui): add showContextCompression setting and simplify message wording
This commit is contained in:
@@ -956,6 +956,7 @@ export async function loadCliConfig(
|
||||
model: resolvedModel,
|
||||
maxSessionTurns: settings.model?.maxSessionTurns,
|
||||
showContextWindowWarning: settings.ui?.showContextWindowWarning,
|
||||
showContextCompression: settings.ui?.showContextCompression,
|
||||
|
||||
listExtensions: argv.listExtensions || false,
|
||||
listSessions: argv.listSessions || false,
|
||||
|
||||
@@ -590,6 +590,15 @@ const SETTINGS_SCHEMA = {
|
||||
'Show a warning message when the context window limit is nearly reached.',
|
||||
showInDialog: true,
|
||||
},
|
||||
showContextCompression: {
|
||||
type: 'boolean',
|
||||
label: 'Show Context Compression Events',
|
||||
category: 'UI',
|
||||
requiresRestart: false,
|
||||
default: false,
|
||||
description: 'Show a message when the chat history is compressed.',
|
||||
showInDialog: true,
|
||||
},
|
||||
footer: {
|
||||
type: 'object',
|
||||
label: 'Footer',
|
||||
|
||||
@@ -47,8 +47,8 @@ describe('compressCommand', () => {
|
||||
isPending: true,
|
||||
beforePercentage: null,
|
||||
afterPercentage: null,
|
||||
threshold: null,
|
||||
compressionStatus: null,
|
||||
isManual: true,
|
||||
},
|
||||
};
|
||||
await compressCommand.action!(context, '');
|
||||
@@ -80,7 +80,7 @@ describe('compressCommand', () => {
|
||||
compressionStatus: null,
|
||||
beforePercentage: null,
|
||||
afterPercentage: null,
|
||||
threshold: null,
|
||||
isManual: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -97,7 +97,7 @@ describe('compressCommand', () => {
|
||||
compressionStatus: Core.CompressionStatus.COMPRESSED,
|
||||
beforePercentage: 20,
|
||||
afterPercentage: 10,
|
||||
threshold: 20,
|
||||
isManual: true,
|
||||
},
|
||||
},
|
||||
expect.any(Number),
|
||||
|
||||
@@ -47,8 +47,8 @@ export const compressCommand: SlashCommand = {
|
||||
isPending: true,
|
||||
beforePercentage: null,
|
||||
afterPercentage: null,
|
||||
threshold: null,
|
||||
compressionStatus: null,
|
||||
isManual: true,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -76,8 +76,8 @@ export const compressCommand: SlashCommand = {
|
||||
isPending: false,
|
||||
beforePercentage,
|
||||
afterPercentage,
|
||||
threshold,
|
||||
compressionStatus: compressed.compressionStatus,
|
||||
isManual: true,
|
||||
},
|
||||
} as HistoryItemCompression,
|
||||
Date.now(),
|
||||
|
||||
@@ -22,13 +22,8 @@ export interface CompressionDisplayProps {
|
||||
export function CompressionMessage({
|
||||
compression,
|
||||
}: CompressionDisplayProps): React.JSX.Element {
|
||||
const {
|
||||
isPending,
|
||||
beforePercentage,
|
||||
afterPercentage,
|
||||
threshold,
|
||||
compressionStatus,
|
||||
} = compression;
|
||||
const { isPending, beforePercentage, afterPercentage, compressionStatus } =
|
||||
compression;
|
||||
|
||||
const getCompressionText = () => {
|
||||
if (isPending) {
|
||||
@@ -37,7 +32,7 @@ export function CompressionMessage({
|
||||
|
||||
switch (compressionStatus) {
|
||||
case CompressionStatus.COMPRESSED:
|
||||
return `Context compressed (${beforePercentage}% ➔ ${afterPercentage}%). Adjust threshold (${threshold}%) in /settings.`;
|
||||
return `Context compressed (${beforePercentage}% ➔ ${afterPercentage}%).`;
|
||||
case CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT:
|
||||
return 'Compression was not beneficial for this history size.';
|
||||
case CompressionStatus.COMPRESSION_FAILED_TOKEN_COUNT_ERROR:
|
||||
|
||||
@@ -332,6 +332,7 @@ describe('useGeminiStream', () => {
|
||||
getIdeMode: vi.fn(() => false),
|
||||
getEnableHooks: vi.fn(() => false),
|
||||
getShowContextWindowWarning: vi.fn(() => false),
|
||||
getShowContextCompression: vi.fn(() => false),
|
||||
getContextWindowCompressionThreshold: vi.fn(() => 0.2),
|
||||
} as unknown as Config;
|
||||
|
||||
@@ -2583,11 +2584,12 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should add informational messages when ChatCompressed event is received', async () => {
|
||||
it('should add informational messages when ChatCompressed event is received and showContextCompression is true', async () => {
|
||||
vi.mocked(tokenLimit).mockReturnValue(10000);
|
||||
vi.mocked(
|
||||
mockConfig.getContextWindowCompressionThreshold,
|
||||
).mockReturnValue(0.2);
|
||||
vi.mocked(mockConfig.getShowContextCompression).mockReturnValue(true);
|
||||
// Setup mock to return a stream with ChatCompressed event
|
||||
mockSendMessageStream.mockReturnValue(
|
||||
(async function* () {
|
||||
@@ -2633,8 +2635,8 @@ describe('useGeminiStream', () => {
|
||||
isPending: false,
|
||||
beforePercentage: 10,
|
||||
afterPercentage: 5,
|
||||
threshold: 20,
|
||||
compressionStatus: 'compressed',
|
||||
isManual: false,
|
||||
},
|
||||
}),
|
||||
expect.any(Number),
|
||||
@@ -2642,6 +2644,58 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should NOT add informational messages when ChatCompressed event is received and showContextCompression is false', async () => {
|
||||
vi.mocked(tokenLimit).mockReturnValue(10000);
|
||||
vi.mocked(
|
||||
mockConfig.getContextWindowCompressionThreshold,
|
||||
).mockReturnValue(0.2);
|
||||
vi.mocked(mockConfig.getShowContextCompression).mockReturnValue(false);
|
||||
// Setup mock to return a stream with ChatCompressed event
|
||||
mockSendMessageStream.mockReturnValue(
|
||||
(async function* () {
|
||||
yield {
|
||||
type: ServerGeminiEventType.ChatCompressed,
|
||||
value: {
|
||||
originalTokenCount: 1000,
|
||||
newTokenCount: 500,
|
||||
compressionStatus: 'compressed',
|
||||
},
|
||||
};
|
||||
yield {
|
||||
type: ServerGeminiEventType.Content,
|
||||
value: 'Response after compression',
|
||||
};
|
||||
yield {
|
||||
type: ServerGeminiEventType.Finished,
|
||||
value: {
|
||||
finishReason: 'STOP',
|
||||
usageMetadata: {
|
||||
promptTokenCount: 10,
|
||||
candidatesTokenCount: 20,
|
||||
totalTokenCount: 30,
|
||||
},
|
||||
},
|
||||
};
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithDefaults();
|
||||
|
||||
// Submit a query
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('Test compression');
|
||||
});
|
||||
|
||||
// Check that NO compression message was added
|
||||
await waitFor(() => {
|
||||
expect(mockAddItem).not.toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'compression',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
reason: 'STOP',
|
||||
|
||||
@@ -1152,9 +1152,10 @@ export const useGeminiStream = (
|
||||
eventValue?.newTokenCount != null
|
||||
? Math.round((eventValue.newTokenCount / limit) * 100)
|
||||
: null;
|
||||
const threshold = Math.round(
|
||||
config.getContextWindowCompressionThreshold() * 100,
|
||||
);
|
||||
|
||||
if (!config.getShowContextCompression()) {
|
||||
return;
|
||||
}
|
||||
|
||||
addItem(
|
||||
{
|
||||
@@ -1163,8 +1164,8 @@ export const useGeminiStream = (
|
||||
isPending: false,
|
||||
beforePercentage,
|
||||
afterPercentage,
|
||||
threshold,
|
||||
compressionStatus: eventValue?.compressionStatus ?? null,
|
||||
isManual: false,
|
||||
},
|
||||
timestamp: new Date(userMessageTimestamp),
|
||||
} as HistoryItemWithoutId,
|
||||
|
||||
@@ -140,8 +140,8 @@ export interface CompressionProps {
|
||||
isPending: boolean;
|
||||
beforePercentage: number | null;
|
||||
afterPercentage: number | null;
|
||||
threshold: number | null;
|
||||
compressionStatus: CompressionStatus | null;
|
||||
isManual?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -674,6 +674,7 @@ export interface ConfigParameters {
|
||||
}>;
|
||||
enableConseca?: boolean;
|
||||
showContextWindowWarning?: boolean;
|
||||
showContextCompression?: boolean;
|
||||
billing?: {
|
||||
overageStrategy?: OverageStrategy;
|
||||
};
|
||||
@@ -710,6 +711,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
private readonly worktreeSettings: WorktreeSettings | undefined;
|
||||
readonly enableConseca: boolean;
|
||||
private readonly showContextWindowWarning: boolean;
|
||||
private readonly showContextCompression: boolean;
|
||||
|
||||
private readonly coreTools: string[] | undefined;
|
||||
private readonly mainAgentTools: string[] | undefined;
|
||||
@@ -1149,6 +1151,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
this.eventEmitter = params.eventEmitter;
|
||||
this.enableConseca = params.enableConseca ?? false;
|
||||
this.showContextWindowWarning = params.showContextWindowWarning ?? false;
|
||||
this.showContextCompression = params.showContextCompression ?? false;
|
||||
|
||||
// Initialize Safety Infrastructure
|
||||
const contextBuilder = new ContextBuilder(this);
|
||||
@@ -2060,6 +2063,10 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
return this.showContextWindowWarning;
|
||||
}
|
||||
|
||||
getShowContextCompression(): boolean {
|
||||
return this.showContextCompression;
|
||||
}
|
||||
|
||||
getMcpEnablementCallbacks(): McpEnablementCallbacks | undefined {
|
||||
return this.mcpEnablementCallbacks;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user