Skip to content

Commit 9c54e62

Browse files
joshualittdanpalmer
authored andcommitted
feat(core): Migrate chatCompressionService to model configs. (google-gemini#12863)
1 parent 330c6db commit 9c54e62

File tree

6 files changed

+177
-53
lines changed

6 files changed

+177
-53
lines changed

docs/get-started/configuration.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,26 @@ their corresponding top-level category object in your `settings.json` file.
490490
"next-speaker-checker": {
491491
"extends": "gemini-2.5-flash-base",
492492
"modelConfig": {}
493+
},
494+
"chat-compression-3-pro": {
495+
"modelConfig": {
496+
"model": "gemini-3-pro-preview"
497+
}
498+
},
499+
"chat-compression-2.5-pro": {
500+
"modelConfig": {
501+
"model": "gemini-2.5-pro"
502+
}
503+
},
504+
"chat-compression-2.5-flash": {
505+
"modelConfig": {
506+
"model": "gemini-2.5-flash"
507+
}
508+
},
509+
"chat-compression-2.5-flash-lite": {
510+
"modelConfig": {
511+
"model": "gemini-2.5-flash-lite"
512+
}
493513
}
494514
}
495515
```

packages/core/src/config/defaultModelConfigs.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,5 +183,25 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
183183
extends: 'gemini-2.5-flash-base',
184184
modelConfig: {},
185185
},
186+
'chat-compression-3-pro': {
187+
modelConfig: {
188+
model: 'gemini-3-pro-preview',
189+
},
190+
},
191+
'chat-compression-2.5-pro': {
192+
modelConfig: {
193+
model: 'gemini-2.5-pro',
194+
},
195+
},
196+
'chat-compression-2.5-flash': {
197+
modelConfig: {
198+
model: 'gemini-2.5-flash',
199+
},
200+
},
201+
'chat-compression-2.5-flash-lite': {
202+
modelConfig: {
203+
model: 'gemini-2.5-flash-lite',
204+
},
205+
},
186206
},
187207
};

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

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
88
import {
99
ChatCompressionService,
1010
findCompressSplitPoint,
11+
modelStringToModelConfigAlias,
1112
} from './chatCompressionService.js';
1213
import type { Content, GenerateContentResponse } from '@google/genai';
1314
import { CompressionStatus } from '../core/turn.js';
1415
import { tokenLimit } from '../core/tokenLimits.js';
1516
import type { GeminiChat } from '../core/geminiChat.js';
1617
import type { Config } from '../config/config.js';
1718
import { getInitialChatHistory } from '../utils/environmentContext.js';
18-
import type { ContentGenerator } from '../core/contentGenerator.js';
19+
import { DEFAULT_GEMINI_MODEL } from '../config/models.js';
1920

2021
vi.mock('../core/tokenLimits.js');
2122
vi.mock('../telemetry/loggers.js');
@@ -101,11 +102,34 @@ describe('findCompressSplitPoint', () => {
101102
});
102103
});
103104

105+
describe('modelStringToModelConfigAlias', () => {
106+
it('should return the default model for unexpected aliases', () => {
107+
expect(modelStringToModelConfigAlias('gemini-flash-flash')).toBe(
108+
DEFAULT_GEMINI_MODEL,
109+
);
110+
});
111+
112+
it('should handle valid names', () => {
113+
expect(modelStringToModelConfigAlias('gemini-3-pro-preview')).toBe(
114+
'chat-compression-3-pro',
115+
);
116+
expect(modelStringToModelConfigAlias('gemini-2.5-pro')).toBe(
117+
'chat-compression-2.5-pro',
118+
);
119+
expect(modelStringToModelConfigAlias('gemini-2.5-flash')).toBe(
120+
'chat-compression-2.5-flash',
121+
);
122+
expect(modelStringToModelConfigAlias('gemini-2.5-flash-lite')).toBe(
123+
'chat-compression-2.5-flash-lite',
124+
);
125+
});
126+
});
127+
104128
describe('ChatCompressionService', () => {
105129
let service: ChatCompressionService;
106130
let mockChat: GeminiChat;
107131
let mockConfig: Config;
108-
const mockModel = 'gemini-pro';
132+
const mockModel = 'gemini-2.5-pro';
109133
const mockPromptId = 'test-prompt-id';
110134

111135
beforeEach(() => {
@@ -114,9 +138,22 @@ describe('ChatCompressionService', () => {
114138
getHistory: vi.fn(),
115139
getLastPromptTokenCount: vi.fn().mockReturnValue(500),
116140
} as unknown as GeminiChat;
141+
142+
const mockGenerateContent = vi.fn().mockResolvedValue({
143+
candidates: [
144+
{
145+
content: {
146+
parts: [{ text: 'Summary' }],
147+
},
148+
},
149+
],
150+
} as unknown as GenerateContentResponse);
151+
117152
mockConfig = {
118153
getCompressionThreshold: vi.fn(),
119-
getContentGenerator: vi.fn(),
154+
getBaseLlmClient: vi.fn().mockReturnValue({
155+
generateContent: mockGenerateContent,
156+
}),
120157
isInteractive: vi.fn().mockReturnValue(false),
121158
} as unknown as Config;
122159

@@ -190,18 +227,6 @@ describe('ChatCompressionService', () => {
190227
vi.mocked(mockChat.getHistory).mockReturnValue(history);
191228
vi.mocked(mockChat.getLastPromptTokenCount).mockReturnValue(800);
192229
vi.mocked(tokenLimit).mockReturnValue(1000);
193-
const mockGenerateContent = vi.fn().mockResolvedValue({
194-
candidates: [
195-
{
196-
content: {
197-
parts: [{ text: 'Summary' }],
198-
},
199-
},
200-
],
201-
} as unknown as GenerateContentResponse);
202-
vi.mocked(mockConfig.getContentGenerator).mockReturnValue({
203-
generateContent: mockGenerateContent,
204-
} as unknown as ContentGenerator);
205230

206231
const result = await service.compress(
207232
mockChat,
@@ -215,7 +240,7 @@ describe('ChatCompressionService', () => {
215240
expect(result.info.compressionStatus).toBe(CompressionStatus.COMPRESSED);
216241
expect(result.newHistory).not.toBeNull();
217242
expect(result.newHistory![0].parts![0].text).toBe('Summary');
218-
expect(mockGenerateContent).toHaveBeenCalled();
243+
expect(mockConfig.getBaseLlmClient().generateContent).toHaveBeenCalled();
219244
});
220245

221246
it('should force compress even if under threshold', async () => {
@@ -229,19 +254,6 @@ describe('ChatCompressionService', () => {
229254
vi.mocked(mockChat.getLastPromptTokenCount).mockReturnValue(100);
230255
vi.mocked(tokenLimit).mockReturnValue(1000);
231256

232-
const mockGenerateContent = vi.fn().mockResolvedValue({
233-
candidates: [
234-
{
235-
content: {
236-
parts: [{ text: 'Summary' }],
237-
},
238-
},
239-
],
240-
} as unknown as GenerateContentResponse);
241-
vi.mocked(mockConfig.getContentGenerator).mockReturnValue({
242-
generateContent: mockGenerateContent,
243-
} as unknown as ContentGenerator);
244-
245257
const result = await service.compress(
246258
mockChat,
247259
mockPromptId,
@@ -265,7 +277,7 @@ describe('ChatCompressionService', () => {
265277
vi.mocked(tokenLimit).mockReturnValue(1000);
266278

267279
const longSummary = 'a'.repeat(1000); // Long summary to inflate token count
268-
const mockGenerateContent = vi.fn().mockResolvedValue({
280+
vi.mocked(mockConfig.getBaseLlmClient().generateContent).mockResolvedValue({
269281
candidates: [
270282
{
271283
content: {
@@ -274,9 +286,6 @@ describe('ChatCompressionService', () => {
274286
},
275287
],
276288
} as unknown as GenerateContentResponse);
277-
vi.mocked(mockConfig.getContentGenerator).mockReturnValue({
278-
generateContent: mockGenerateContent,
279-
} as unknown as ContentGenerator);
280289

281290
const result = await service.compress(
282291
mockChat,

packages/core/src/services/chatCompressionService.ts

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ import { getResponseText } from '../utils/partUtils.js';
1414
import { logChatCompression } from '../telemetry/loggers.js';
1515
import { makeChatCompressionEvent } from '../telemetry/types.js';
1616
import { getInitialChatHistory } from '../utils/environmentContext.js';
17+
import {
18+
DEFAULT_GEMINI_FLASH_LITE_MODEL,
19+
DEFAULT_GEMINI_FLASH_MODEL,
20+
DEFAULT_GEMINI_MODEL,
21+
PREVIEW_GEMINI_MODEL,
22+
} from '../config/models.js';
1723

1824
/**
1925
* Default threshold for compression token count as a fraction of the model's
@@ -75,6 +81,21 @@ export function findCompressSplitPoint(
7581
return lastSplitPoint;
7682
}
7783

84+
export function modelStringToModelConfigAlias(model: string): string {
85+
switch (model) {
86+
case PREVIEW_GEMINI_MODEL:
87+
return 'chat-compression-3-pro';
88+
case DEFAULT_GEMINI_MODEL:
89+
return 'chat-compression-2.5-pro';
90+
case DEFAULT_GEMINI_FLASH_MODEL:
91+
return 'chat-compression-2.5-flash';
92+
case DEFAULT_GEMINI_FLASH_LITE_MODEL:
93+
return 'chat-compression-2.5-flash-lite';
94+
default:
95+
return DEFAULT_GEMINI_MODEL;
96+
}
97+
}
98+
7899
export class ChatCompressionService {
79100
async compress(
80101
chat: GeminiChat,
@@ -139,26 +160,24 @@ export class ChatCompressionService {
139160
};
140161
}
141162

142-
const summaryResponse = await config.getContentGenerator().generateContent(
143-
{
144-
model,
145-
contents: [
146-
...historyToCompress,
147-
{
148-
role: 'user',
149-
parts: [
150-
{
151-
text: 'First, reason in your scratchpad. Then, generate the <state_snapshot>.',
152-
},
153-
],
154-
},
155-
],
156-
config: {
157-
systemInstruction: { text: getCompressionPrompt() },
163+
const summaryResponse = await config.getBaseLlmClient().generateContent({
164+
modelConfigKey: { model: modelStringToModelConfigAlias(model) },
165+
contents: [
166+
...historyToCompress,
167+
{
168+
role: 'user',
169+
parts: [
170+
{
171+
text: 'First, reason in your scratchpad. Then, generate the <state_snapshot>.',
172+
},
173+
],
158174
},
159-
},
175+
],
176+
systemInstruction: { text: getCompressionPrompt() },
160177
promptId,
161-
);
178+
// TODO(joshualitt): wire up a sensible abort signal,
179+
abortSignal: new AbortController().signal,
180+
});
162181
const summary = getResponseText(summaryResponse) ?? '';
163182

164183
const extraHistory: Content[] = [

packages/core/src/services/test-data/resolved-aliases.golden.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,5 +198,21 @@
198198
"temperature": 0,
199199
"topP": 1
200200
}
201+
},
202+
"chat-compression-3-pro": {
203+
"model": "gemini-3-pro-preview",
204+
"generateContentConfig": {}
205+
},
206+
"chat-compression-2.5-pro": {
207+
"model": "gemini-2.5-pro",
208+
"generateContentConfig": {}
209+
},
210+
"chat-compression-2.5-flash": {
211+
"model": "gemini-2.5-flash",
212+
"generateContentConfig": {}
213+
},
214+
"chat-compression-2.5-flash-lite": {
215+
"model": "gemini-2.5-flash-lite",
216+
"generateContentConfig": {}
201217
}
202218
}

0 commit comments

Comments
 (0)