@google/gemini-cli-core 0.4.0-preview.2 → 0.5.0-nightly.20250908.4693137b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/google-gemini-cli-core-0.3.4.tgz +0 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/src/code_assist/codeAssist.js +1 -1
- package/dist/src/code_assist/codeAssist.js.map +1 -1
- package/dist/src/code_assist/oauth-credential-storage.d.ts +27 -0
- package/dist/src/code_assist/oauth-credential-storage.js +112 -0
- package/dist/src/code_assist/oauth-credential-storage.js.map +1 -0
- package/dist/src/code_assist/oauth-credential-storage.test.d.ts +6 -0
- package/dist/src/code_assist/oauth-credential-storage.test.js +134 -0
- package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -0
- package/dist/src/code_assist/server.d.ts +1 -1
- package/dist/src/code_assist/server.js +24 -1
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +25 -0
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/code_assist/types.d.ts +17 -2
- package/dist/src/config/config.d.ts +13 -1
- package/dist/src/config/config.js +38 -22
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +9 -120
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/storage.d.ts +1 -0
- package/dist/src/config/storage.js +2 -1
- package/dist/src/config/storage.js.map +1 -1
- package/dist/src/core/client.d.ts +4 -10
- package/dist/src/core/client.js +18 -45
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +75 -288
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +1 -0
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.d.ts +4 -0
- package/dist/src/core/coreToolScheduler.js +63 -2
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +172 -5
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +2 -3
- package/dist/src/core/geminiChat.js +19 -5
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +78 -43
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/loggingContentGenerator.js +3 -6
- package/dist/src/core/loggingContentGenerator.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +8 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/prompts.d.ts +5 -0
- package/dist/src/core/prompts.js +63 -42
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/prompts.test.js +130 -1
- package/dist/src/core/prompts.test.js.map +1 -1
- package/dist/src/core/subagent.js +1 -3
- package/dist/src/core/subagent.js.map +1 -1
- package/dist/src/core/subagent.test.js +3 -5
- package/dist/src/core/subagent.test.js.map +1 -1
- package/dist/src/core/turn.d.ts +1 -0
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/ide/process-utils.d.ts +0 -1
- package/dist/src/ide/process-utils.js +36 -31
- package/dist/src/ide/process-utils.js.map +1 -1
- package/dist/src/mcp/oauth-provider.js +3 -3
- package/dist/src/mcp/oauth-provider.js.map +1 -1
- package/dist/src/mcp/oauth-provider.test.js +12 -12
- package/dist/src/mcp/oauth-provider.test.js.map +1 -1
- package/dist/src/mcp/oauth-token-storage.d.ts +8 -6
- package/dist/src/mcp/oauth-token-storage.js +24 -17
- package/dist/src/mcp/oauth-token-storage.js.map +1 -1
- package/dist/src/mcp/oauth-token-storage.test.js +18 -18
- package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +26 -2
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/types.js +4 -0
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/tools/mcp-client.js +5 -5
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/read-file.js +7 -2
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-file.test.js +29 -0
- package/dist/src/tools/read-file.test.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.test.js +13 -42
- package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/dist/google-gemini-cli-core-0.4.0-preview.1.tgz +0 -0
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { describe, it, expect, vi, beforeEach, afterEach
|
|
7
|
-
import { GoogleGenAI } from '@google/genai';
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
8
7
|
import { findIndexAfterFraction, isThinkingDefault, isThinkingSupported, GeminiClient, } from './client.js';
|
|
9
8
|
import { AuthType, } from './contentGenerator.js';
|
|
10
9
|
import {} from './geminiChat.js';
|
|
11
|
-
import { Config } from '../config/config.js';
|
|
12
10
|
import { CompressionStatus, GeminiEventType, Turn, } from './turn.js';
|
|
13
11
|
import { getCoreSystemPrompt } from './prompts.js';
|
|
14
12
|
import { DEFAULT_GEMINI_FLASH_MODEL } from '../config/models.js';
|
|
@@ -41,11 +39,7 @@ vi.mock('node:fs', () => {
|
|
|
41
39
|
};
|
|
42
40
|
});
|
|
43
41
|
// --- Mocks ---
|
|
44
|
-
const mockChatCreateFn = vi.fn();
|
|
45
|
-
const mockGenerateContentFn = vi.fn();
|
|
46
|
-
const mockEmbedContentFn = vi.fn();
|
|
47
42
|
const mockTurnRunFn = vi.fn();
|
|
48
|
-
vi.mock('@google/genai');
|
|
49
43
|
vi.mock('./turn', async (importOriginal) => {
|
|
50
44
|
const actual = await importOriginal();
|
|
51
45
|
// Define a mock class that has the same shape as the real Turn
|
|
@@ -168,33 +162,22 @@ describe('isThinkingDefault', () => {
|
|
|
168
162
|
});
|
|
169
163
|
});
|
|
170
164
|
describe('Gemini Client (client.ts)', () => {
|
|
165
|
+
let mockContentGenerator;
|
|
166
|
+
let mockConfig;
|
|
171
167
|
let client;
|
|
172
168
|
beforeEach(async () => {
|
|
173
169
|
vi.resetAllMocks();
|
|
174
170
|
// Disable 429 simulation for tests
|
|
175
171
|
setSimulate429(false);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
};
|
|
186
|
-
return mock;
|
|
187
|
-
});
|
|
188
|
-
mockChatCreateFn.mockResolvedValue({});
|
|
189
|
-
mockGenerateContentFn.mockResolvedValue({
|
|
190
|
-
candidates: [
|
|
191
|
-
{
|
|
192
|
-
content: {
|
|
193
|
-
parts: [{ text: '{"key": "value"}' }],
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
],
|
|
197
|
-
});
|
|
172
|
+
mockContentGenerator = {
|
|
173
|
+
generateContent: vi.fn().mockResolvedValue({
|
|
174
|
+
candidates: [{ content: { parts: [{ text: '{"key": "value"}' }] } }],
|
|
175
|
+
}),
|
|
176
|
+
generateContentStream: vi.fn(),
|
|
177
|
+
countTokens: vi.fn(),
|
|
178
|
+
embedContent: vi.fn(),
|
|
179
|
+
batchEmbedContents: vi.fn(),
|
|
180
|
+
};
|
|
198
181
|
// Because the GeminiClient constructor kicks off an async process (startChat)
|
|
199
182
|
// that depends on a fully-formed Config object, we need to mock the
|
|
200
183
|
// entire implementation of Config for these tests.
|
|
@@ -209,7 +192,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
209
192
|
vertexai: false,
|
|
210
193
|
authType: AuthType.USE_GEMINI,
|
|
211
194
|
};
|
|
212
|
-
|
|
195
|
+
mockConfig = {
|
|
213
196
|
getContentGeneratorConfig: vi
|
|
214
197
|
.fn()
|
|
215
198
|
.mockReturnValue(contentGeneratorConfig),
|
|
@@ -245,36 +228,15 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
245
228
|
storage: {
|
|
246
229
|
getProjectTempDir: vi.fn().mockReturnValue('/test/temp'),
|
|
247
230
|
},
|
|
231
|
+
getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
|
|
248
232
|
};
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
// and the constructor will use the mocked GoogleGenAI
|
|
253
|
-
client = new GeminiClient(new Config({ sessionId: 'test-session-id' }));
|
|
254
|
-
mockConfigObject.getGeminiClient.mockReturnValue(client);
|
|
255
|
-
await client.initialize(contentGeneratorConfig);
|
|
233
|
+
client = new GeminiClient(mockConfig);
|
|
234
|
+
await client.initialize();
|
|
235
|
+
vi.mocked(mockConfig.getGeminiClient).mockReturnValue(client);
|
|
256
236
|
});
|
|
257
237
|
afterEach(() => {
|
|
258
238
|
vi.restoreAllMocks();
|
|
259
239
|
});
|
|
260
|
-
// NOTE: The following tests for startChat were removed due to persistent issues with
|
|
261
|
-
// the @google/genai mock. Specifically, the mockChatCreateFn (representing instance.chats.create)
|
|
262
|
-
// was not being detected as called by the GeminiClient instance.
|
|
263
|
-
// This likely points to a subtle issue in how the GoogleGenerativeAI class constructor
|
|
264
|
-
// and its instance methods are mocked and then used by the class under test.
|
|
265
|
-
// For future debugging, ensure that the `this.client` in `GeminiClient` (which is an
|
|
266
|
-
// instance of the mocked GoogleGenerativeAI) correctly has its `chats.create` method
|
|
267
|
-
// pointing to `mockChatCreateFn`.
|
|
268
|
-
// it('startChat should call getCoreSystemPrompt with userMemory and pass to chats.create', async () => { ... });
|
|
269
|
-
// it('startChat should call getCoreSystemPrompt with empty string if userMemory is empty', async () => { ... });
|
|
270
|
-
// NOTE: The following tests for generateJson were removed due to persistent issues with
|
|
271
|
-
// the @google/genai mock, similar to the startChat tests. The mockGenerateContentFn
|
|
272
|
-
// (representing instance.models.generateContent) was not being detected as called, or the mock
|
|
273
|
-
// was not preventing an actual API call (leading to API key errors).
|
|
274
|
-
// For future debugging, ensure `this.client.models.generateContent` in `GeminiClient` correctly
|
|
275
|
-
// uses the `mockGenerateContentFn`.
|
|
276
|
-
// it('generateJson should call getCoreSystemPrompt with userMemory and pass to generateContent', async () => { ... });
|
|
277
|
-
// it('generateJson should call getCoreSystemPrompt with empty string if userMemory is empty', async () => { ... });
|
|
278
240
|
describe('generateEmbedding', () => {
|
|
279
241
|
const texts = ['hello world', 'goodbye world'];
|
|
280
242
|
const testEmbeddingModel = 'test-embedding-model';
|
|
@@ -283,16 +245,15 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
283
245
|
[0.1, 0.2, 0.3],
|
|
284
246
|
[0.4, 0.5, 0.6],
|
|
285
247
|
];
|
|
286
|
-
|
|
248
|
+
vi.mocked(mockContentGenerator.embedContent).mockResolvedValue({
|
|
287
249
|
embeddings: [
|
|
288
250
|
{ values: mockEmbeddings[0] },
|
|
289
251
|
{ values: mockEmbeddings[1] },
|
|
290
252
|
],
|
|
291
|
-
};
|
|
292
|
-
mockEmbedContentFn.mockResolvedValue(mockResponse);
|
|
253
|
+
});
|
|
293
254
|
const result = await client.generateEmbedding(texts);
|
|
294
|
-
expect(
|
|
295
|
-
expect(
|
|
255
|
+
expect(mockContentGenerator.embedContent).toHaveBeenCalledTimes(1);
|
|
256
|
+
expect(mockContentGenerator.embedContent).toHaveBeenCalledWith({
|
|
296
257
|
model: testEmbeddingModel,
|
|
297
258
|
contents: texts,
|
|
298
259
|
});
|
|
@@ -301,43 +262,38 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
301
262
|
it('should return an empty array if an empty array is passed', async () => {
|
|
302
263
|
const result = await client.generateEmbedding([]);
|
|
303
264
|
expect(result).toEqual([]);
|
|
304
|
-
expect(
|
|
265
|
+
expect(mockContentGenerator.embedContent).not.toHaveBeenCalled();
|
|
305
266
|
});
|
|
306
267
|
it('should throw an error if API response has no embeddings array', async () => {
|
|
307
|
-
|
|
268
|
+
vi.mocked(mockContentGenerator.embedContent).mockResolvedValue({});
|
|
308
269
|
await expect(client.generateEmbedding(texts)).rejects.toThrow('No embeddings found in API response.');
|
|
309
270
|
});
|
|
310
271
|
it('should throw an error if API response has an empty embeddings array', async () => {
|
|
311
|
-
|
|
272
|
+
vi.mocked(mockContentGenerator.embedContent).mockResolvedValue({
|
|
312
273
|
embeddings: [],
|
|
313
|
-
};
|
|
314
|
-
mockEmbedContentFn.mockResolvedValue(mockResponse);
|
|
274
|
+
});
|
|
315
275
|
await expect(client.generateEmbedding(texts)).rejects.toThrow('No embeddings found in API response.');
|
|
316
276
|
});
|
|
317
277
|
it('should throw an error if API returns a mismatched number of embeddings', async () => {
|
|
318
|
-
|
|
278
|
+
vi.mocked(mockContentGenerator.embedContent).mockResolvedValue({
|
|
319
279
|
embeddings: [{ values: [1, 2, 3] }], // Only one for two texts
|
|
320
|
-
};
|
|
321
|
-
mockEmbedContentFn.mockResolvedValue(mockResponse);
|
|
280
|
+
});
|
|
322
281
|
await expect(client.generateEmbedding(texts)).rejects.toThrow('API returned a mismatched number of embeddings. Expected 2, got 1.');
|
|
323
282
|
});
|
|
324
283
|
it('should throw an error if any embedding has nullish values', async () => {
|
|
325
|
-
|
|
284
|
+
vi.mocked(mockContentGenerator.embedContent).mockResolvedValue({
|
|
326
285
|
embeddings: [{ values: [1, 2, 3] }, { values: undefined }], // Second one is bad
|
|
327
|
-
};
|
|
328
|
-
mockEmbedContentFn.mockResolvedValue(mockResponse);
|
|
286
|
+
});
|
|
329
287
|
await expect(client.generateEmbedding(texts)).rejects.toThrow('API returned an empty embedding for input text at index 1: "goodbye world"');
|
|
330
288
|
});
|
|
331
289
|
it('should throw an error if any embedding has an empty values array', async () => {
|
|
332
|
-
|
|
290
|
+
vi.mocked(mockContentGenerator.embedContent).mockResolvedValue({
|
|
333
291
|
embeddings: [{ values: [] }, { values: [1, 2, 3] }], // First one is bad
|
|
334
|
-
};
|
|
335
|
-
mockEmbedContentFn.mockResolvedValue(mockResponse);
|
|
292
|
+
});
|
|
336
293
|
await expect(client.generateEmbedding(texts)).rejects.toThrow('API returned an empty embedding for input text at index 0: "hello world"');
|
|
337
294
|
});
|
|
338
295
|
it('should propagate errors from the API call', async () => {
|
|
339
|
-
|
|
340
|
-
mockEmbedContentFn.mockRejectedValue(apiError);
|
|
296
|
+
vi.mocked(mockContentGenerator.embedContent).mockRejectedValue(new Error('API Failure'));
|
|
341
297
|
await expect(client.generateEmbedding(texts)).rejects.toThrow('API Failure');
|
|
342
298
|
});
|
|
343
299
|
});
|
|
@@ -346,14 +302,11 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
346
302
|
const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
|
|
347
303
|
const schema = { type: 'string' };
|
|
348
304
|
const abortSignal = new AbortController().signal;
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
generateContent: mockGenerateContentFn,
|
|
353
|
-
};
|
|
354
|
-
client['contentGenerator'] = mockGenerator;
|
|
305
|
+
vi.mocked(mockContentGenerator.countTokens).mockResolvedValue({
|
|
306
|
+
totalTokens: 1,
|
|
307
|
+
});
|
|
355
308
|
await client.generateJson(contents, schema, abortSignal, DEFAULT_GEMINI_FLASH_MODEL);
|
|
356
|
-
expect(
|
|
309
|
+
expect(mockContentGenerator.generateContent).toHaveBeenCalledWith({
|
|
357
310
|
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
358
311
|
config: {
|
|
359
312
|
abortSignal,
|
|
@@ -374,13 +327,11 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
374
327
|
const abortSignal = new AbortController().signal;
|
|
375
328
|
const customModel = 'custom-json-model';
|
|
376
329
|
const customConfig = { temperature: 0.9, topK: 20 };
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
};
|
|
381
|
-
client['contentGenerator'] = mockGenerator;
|
|
330
|
+
vi.mocked(mockContentGenerator.countTokens).mockResolvedValue({
|
|
331
|
+
totalTokens: 1,
|
|
332
|
+
});
|
|
382
333
|
await client.generateJson(contents, schema, abortSignal, customModel, customConfig);
|
|
383
|
-
expect(
|
|
334
|
+
expect(mockContentGenerator.generateContent).toHaveBeenCalledWith({
|
|
384
335
|
model: customModel,
|
|
385
336
|
config: {
|
|
386
337
|
abortSignal,
|
|
@@ -432,16 +383,12 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
432
383
|
});
|
|
433
384
|
});
|
|
434
385
|
describe('tryCompressChat', () => {
|
|
435
|
-
const mockCountTokens = vi.fn();
|
|
436
386
|
const mockSendMessage = vi.fn();
|
|
437
387
|
const mockGetHistory = vi.fn();
|
|
438
388
|
beforeEach(() => {
|
|
439
389
|
vi.mock('./tokenLimits', () => ({
|
|
440
390
|
tokenLimit: vi.fn(),
|
|
441
391
|
}));
|
|
442
|
-
client['contentGenerator'] = {
|
|
443
|
-
countTokens: mockCountTokens,
|
|
444
|
-
};
|
|
445
392
|
client['chat'] = {
|
|
446
393
|
getHistory: mockGetHistory,
|
|
447
394
|
addHistory: vi.fn(),
|
|
@@ -458,23 +405,17 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
458
405
|
setHistory: vi.fn(),
|
|
459
406
|
sendMessage: vi.fn().mockResolvedValue({ text: 'Summary' }),
|
|
460
407
|
};
|
|
461
|
-
|
|
462
|
-
.fn()
|
|
408
|
+
vi.mocked(mockContentGenerator.countTokens)
|
|
463
409
|
.mockResolvedValueOnce({ totalTokens: 1000 })
|
|
464
410
|
.mockResolvedValueOnce({ totalTokens: 5000 });
|
|
465
|
-
const mockGenerator = {
|
|
466
|
-
countTokens: mockCountTokens,
|
|
467
|
-
};
|
|
468
411
|
client['chat'] = mockChat;
|
|
469
|
-
client['contentGenerator'] = mockGenerator;
|
|
470
412
|
client['startChat'] = vi.fn().mockResolvedValue({ ...mockChat });
|
|
471
|
-
return { client, mockChat
|
|
413
|
+
return { client, mockChat };
|
|
472
414
|
}
|
|
473
415
|
describe('when compression inflates the token count', () => {
|
|
474
|
-
it('uses the truncated history for compression');
|
|
475
416
|
it('allows compression to be forced/manual after a failure', async () => {
|
|
476
|
-
const { client
|
|
477
|
-
|
|
417
|
+
const { client } = setup();
|
|
418
|
+
vi.mocked(mockContentGenerator.countTokens).mockResolvedValue({
|
|
478
419
|
totalTokens: 1000,
|
|
479
420
|
});
|
|
480
421
|
await client.tryCompressChat('prompt-id-4'); // Fails
|
|
@@ -487,6 +428,9 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
487
428
|
});
|
|
488
429
|
it('yields the result even if the compression inflated the tokens', async () => {
|
|
489
430
|
const { client } = setup();
|
|
431
|
+
vi.mocked(mockContentGenerator.countTokens).mockResolvedValue({
|
|
432
|
+
totalTokens: 1000,
|
|
433
|
+
});
|
|
490
434
|
const result = await client.tryCompressChat('prompt-id-4', true);
|
|
491
435
|
expect(result).toEqual({
|
|
492
436
|
compressionStatus: CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT,
|
|
@@ -501,7 +445,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
501
445
|
});
|
|
502
446
|
it('restores the history back to the original', async () => {
|
|
503
447
|
vi.mocked(tokenLimit).mockReturnValue(1000);
|
|
504
|
-
|
|
448
|
+
vi.mocked(mockContentGenerator.countTokens).mockResolvedValue({
|
|
505
449
|
totalTokens: 999,
|
|
506
450
|
});
|
|
507
451
|
const originalHistory = [
|
|
@@ -517,11 +461,11 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
517
461
|
expect(client['chat']?.setHistory).toHaveBeenCalledWith(originalHistory);
|
|
518
462
|
});
|
|
519
463
|
it('will not attempt to compress context after a failure', async () => {
|
|
520
|
-
const { client
|
|
464
|
+
const { client } = setup();
|
|
521
465
|
await client.tryCompressChat('prompt-id-4');
|
|
522
466
|
const result = await client.tryCompressChat('prompt-id-5');
|
|
523
467
|
// it counts tokens for {original, compressed} and then never again
|
|
524
|
-
expect(
|
|
468
|
+
expect(mockContentGenerator.countTokens).toHaveBeenCalledTimes(2);
|
|
525
469
|
expect(result).toEqual({
|
|
526
470
|
compressionStatus: CompressionStatus.NOOP,
|
|
527
471
|
newTokenCount: 0,
|
|
@@ -531,7 +475,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
531
475
|
});
|
|
532
476
|
it('attempts to compress with a maxOutputTokens set to the original token count', async () => {
|
|
533
477
|
vi.mocked(tokenLimit).mockReturnValue(1000);
|
|
534
|
-
|
|
478
|
+
vi.mocked(mockContentGenerator.countTokens).mockResolvedValue({
|
|
535
479
|
totalTokens: 999,
|
|
536
480
|
});
|
|
537
481
|
mockGetHistory.mockReturnValue([
|
|
@@ -555,7 +499,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
555
499
|
mockGetHistory.mockReturnValue([
|
|
556
500
|
{ role: 'user', parts: [{ text: '...history...' }] },
|
|
557
501
|
]);
|
|
558
|
-
|
|
502
|
+
vi.mocked(mockContentGenerator.countTokens).mockResolvedValue({
|
|
559
503
|
totalTokens: MOCKED_TOKEN_LIMIT * 0.699, // TOKEN_THRESHOLD_FOR_SUMMARIZATION = 0.7
|
|
560
504
|
});
|
|
561
505
|
const initialChat = client.getChat();
|
|
@@ -582,7 +526,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
582
526
|
]);
|
|
583
527
|
const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
|
|
584
528
|
const newTokenCount = 100;
|
|
585
|
-
|
|
529
|
+
vi.mocked(mockContentGenerator.countTokens)
|
|
586
530
|
.mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
|
|
587
531
|
.mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
|
|
588
532
|
// Mock the summary response from the chat
|
|
@@ -608,7 +552,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
608
552
|
]);
|
|
609
553
|
const originalTokenCount = MOCKED_TOKEN_LIMIT * MOCKED_CONTEXT_PERCENTAGE_THRESHOLD;
|
|
610
554
|
const newTokenCount = 100;
|
|
611
|
-
|
|
555
|
+
vi.mocked(mockContentGenerator.countTokens)
|
|
612
556
|
.mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
|
|
613
557
|
.mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
|
|
614
558
|
// Mock the summary response from the chat
|
|
@@ -653,7 +597,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
653
597
|
]);
|
|
654
598
|
const originalTokenCount = 1000 * 0.7;
|
|
655
599
|
const newTokenCount = 100;
|
|
656
|
-
|
|
600
|
+
vi.mocked(mockContentGenerator.countTokens)
|
|
657
601
|
.mockResolvedValueOnce({ totalTokens: originalTokenCount }) // First call for the check
|
|
658
602
|
.mockResolvedValueOnce({ totalTokens: newTokenCount }); // Second call for the new history
|
|
659
603
|
// Mock the summary response from the chat
|
|
@@ -687,7 +631,7 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
687
631
|
]);
|
|
688
632
|
const originalTokenCount = 10; // Well below threshold
|
|
689
633
|
const newTokenCount = 5;
|
|
690
|
-
|
|
634
|
+
vi.mocked(mockContentGenerator.countTokens)
|
|
691
635
|
.mockResolvedValueOnce({ totalTokens: originalTokenCount })
|
|
692
636
|
.mockResolvedValueOnce({ totalTokens: newTokenCount });
|
|
693
637
|
// Mock the summary response from the chat
|
|
@@ -708,9 +652,14 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
708
652
|
expect(newChat).not.toBe(initialChat);
|
|
709
653
|
});
|
|
710
654
|
it('should use current model from config for token counting after sendMessage', async () => {
|
|
711
|
-
const initialModel =
|
|
712
|
-
|
|
713
|
-
|
|
655
|
+
const initialModel = mockConfig.getModel();
|
|
656
|
+
// mock the model has been changed between calls of `countTokens`
|
|
657
|
+
const firstCurrentModel = initialModel + '-changed-1';
|
|
658
|
+
const secondCurrentModel = initialModel + '-changed-2';
|
|
659
|
+
vi.mocked(mockConfig.getModel)
|
|
660
|
+
.mockReturnValueOnce(firstCurrentModel)
|
|
661
|
+
.mockReturnValueOnce(secondCurrentModel);
|
|
662
|
+
vi.mocked(mockContentGenerator.countTokens)
|
|
714
663
|
.mockResolvedValueOnce({ totalTokens: 100000 })
|
|
715
664
|
.mockResolvedValueOnce({ totalTokens: 5000 });
|
|
716
665
|
const mockSendMessage = vi.fn().mockResolvedValue({ text: 'Summary' });
|
|
@@ -723,25 +672,15 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
723
672
|
setHistory: vi.fn(),
|
|
724
673
|
sendMessage: mockSendMessage,
|
|
725
674
|
};
|
|
726
|
-
const mockGenerator = {
|
|
727
|
-
countTokens: mockCountTokens,
|
|
728
|
-
};
|
|
729
|
-
// mock the model has been changed between calls of `countTokens`
|
|
730
|
-
const firstCurrentModel = initialModel + '-changed-1';
|
|
731
|
-
const secondCurrentModel = initialModel + '-changed-2';
|
|
732
|
-
vi.spyOn(client['config'], 'getModel')
|
|
733
|
-
.mockReturnValueOnce(firstCurrentModel)
|
|
734
|
-
.mockReturnValueOnce(secondCurrentModel);
|
|
735
675
|
client['chat'] = mockChat;
|
|
736
|
-
client['contentGenerator'] = mockGenerator;
|
|
737
676
|
client['startChat'] = vi.fn().mockResolvedValue(mockChat);
|
|
738
677
|
const result = await client.tryCompressChat('prompt-id-4', true);
|
|
739
|
-
expect(
|
|
740
|
-
expect(
|
|
678
|
+
expect(mockContentGenerator.countTokens).toHaveBeenCalledTimes(2);
|
|
679
|
+
expect(mockContentGenerator.countTokens).toHaveBeenNthCalledWith(1, {
|
|
741
680
|
model: firstCurrentModel,
|
|
742
681
|
contents: mockChatHistory,
|
|
743
682
|
});
|
|
744
|
-
expect(
|
|
683
|
+
expect(mockContentGenerator.countTokens).toHaveBeenNthCalledWith(2, {
|
|
745
684
|
model: secondCurrentModel,
|
|
746
685
|
contents: expect.any(Array),
|
|
747
686
|
});
|
|
@@ -755,20 +694,9 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
755
694
|
describe('sendMessageStream', () => {
|
|
756
695
|
it('emits a compression event when the context was automatically compressed', async () => {
|
|
757
696
|
// Arrange
|
|
758
|
-
|
|
697
|
+
mockTurnRunFn.mockReturnValue((async function* () {
|
|
759
698
|
yield { type: 'content', value: 'Hello' };
|
|
760
|
-
})();
|
|
761
|
-
mockTurnRunFn.mockReturnValue(mockStream);
|
|
762
|
-
const mockChat = {
|
|
763
|
-
addHistory: vi.fn(),
|
|
764
|
-
getHistory: vi.fn().mockReturnValue([]),
|
|
765
|
-
};
|
|
766
|
-
client['chat'] = mockChat;
|
|
767
|
-
const mockGenerator = {
|
|
768
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
769
|
-
generateContent: mockGenerateContentFn,
|
|
770
|
-
};
|
|
771
|
-
client['contentGenerator'] = mockGenerator;
|
|
699
|
+
})());
|
|
772
700
|
const compressionInfo = {
|
|
773
701
|
compressionStatus: CompressionStatus.COMPRESSED,
|
|
774
702
|
originalTokenCount: 1000,
|
|
@@ -798,16 +726,6 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
798
726
|
yield { type: 'content', value: 'Hello' };
|
|
799
727
|
})();
|
|
800
728
|
mockTurnRunFn.mockReturnValue(mockStream);
|
|
801
|
-
const mockChat = {
|
|
802
|
-
addHistory: vi.fn(),
|
|
803
|
-
getHistory: vi.fn().mockReturnValue([]),
|
|
804
|
-
};
|
|
805
|
-
client['chat'] = mockChat;
|
|
806
|
-
const mockGenerator = {
|
|
807
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
808
|
-
generateContent: mockGenerateContentFn,
|
|
809
|
-
};
|
|
810
|
-
client['contentGenerator'] = mockGenerator;
|
|
811
729
|
const compressionInfo = {
|
|
812
730
|
compressionStatus,
|
|
813
731
|
originalTokenCount: 1000,
|
|
@@ -846,21 +764,15 @@ describe('Gemini Client (client.ts)', () => {
|
|
|
846
764
|
],
|
|
847
765
|
},
|
|
848
766
|
});
|
|
849
|
-
vi.
|
|
850
|
-
|
|
767
|
+
vi.mocked(mockConfig.getIdeMode).mockReturnValue(true);
|
|
768
|
+
mockTurnRunFn.mockReturnValue((async function* () {
|
|
851
769
|
yield { type: 'content', value: 'Hello' };
|
|
852
|
-
})();
|
|
853
|
-
mockTurnRunFn.mockReturnValue(mockStream);
|
|
770
|
+
})());
|
|
854
771
|
const mockChat = {
|
|
855
772
|
addHistory: vi.fn(),
|
|
856
773
|
getHistory: vi.fn().mockReturnValue([]),
|
|
857
774
|
};
|
|
858
775
|
client['chat'] = mockChat;
|
|
859
|
-
const mockGenerator = {
|
|
860
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
861
|
-
generateContent: mockGenerateContentFn,
|
|
862
|
-
};
|
|
863
|
-
client['contentGenerator'] = mockGenerator;
|
|
864
776
|
const initialRequest = [{ text: 'Hi' }];
|
|
865
777
|
// Act
|
|
866
778
|
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
@@ -908,11 +820,6 @@ ${JSON.stringify({
|
|
|
908
820
|
getHistory: vi.fn().mockReturnValue([]),
|
|
909
821
|
};
|
|
910
822
|
client['chat'] = mockChat;
|
|
911
|
-
const mockGenerator = {
|
|
912
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
913
|
-
generateContent: mockGenerateContentFn,
|
|
914
|
-
};
|
|
915
|
-
client['contentGenerator'] = mockGenerator;
|
|
916
823
|
const initialRequest = [{ text: 'Hi' }];
|
|
917
824
|
// Act
|
|
918
825
|
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
@@ -948,11 +855,6 @@ ${JSON.stringify({
|
|
|
948
855
|
getHistory: vi.fn().mockReturnValue([]),
|
|
949
856
|
};
|
|
950
857
|
client['chat'] = mockChat;
|
|
951
|
-
const mockGenerator = {
|
|
952
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
953
|
-
generateContent: mockGenerateContentFn,
|
|
954
|
-
};
|
|
955
|
-
client['contentGenerator'] = mockGenerator;
|
|
956
858
|
const initialRequest = [{ text: 'Hi' }];
|
|
957
859
|
// Act
|
|
958
860
|
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
@@ -1008,11 +910,6 @@ ${JSON.stringify({
|
|
|
1008
910
|
getHistory: vi.fn().mockReturnValue([]),
|
|
1009
911
|
};
|
|
1010
912
|
client['chat'] = mockChat;
|
|
1011
|
-
const mockGenerator = {
|
|
1012
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1013
|
-
generateContent: mockGenerateContentFn,
|
|
1014
|
-
};
|
|
1015
|
-
client['contentGenerator'] = mockGenerator;
|
|
1016
913
|
const initialRequest = [{ text: 'Hi' }];
|
|
1017
914
|
// Act
|
|
1018
915
|
const stream = client.sendMessageStream(initialRequest, new AbortController().signal, 'prompt-id-ide');
|
|
@@ -1046,11 +943,6 @@ ${JSON.stringify({
|
|
|
1046
943
|
getHistory: vi.fn().mockReturnValue([]),
|
|
1047
944
|
};
|
|
1048
945
|
client['chat'] = mockChat;
|
|
1049
|
-
const mockGenerator = {
|
|
1050
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1051
|
-
generateContent: mockGenerateContentFn,
|
|
1052
|
-
};
|
|
1053
|
-
client['contentGenerator'] = mockGenerator;
|
|
1054
946
|
// Act
|
|
1055
947
|
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-1');
|
|
1056
948
|
// Consume the stream manually to get the final return value.
|
|
@@ -1083,11 +975,6 @@ ${JSON.stringify({
|
|
|
1083
975
|
getHistory: vi.fn().mockReturnValue([]),
|
|
1084
976
|
};
|
|
1085
977
|
client['chat'] = mockChat;
|
|
1086
|
-
const mockGenerator = {
|
|
1087
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1088
|
-
generateContent: mockGenerateContentFn,
|
|
1089
|
-
};
|
|
1090
|
-
client['contentGenerator'] = mockGenerator;
|
|
1091
978
|
// Use a signal that never gets aborted
|
|
1092
979
|
const abortController = new AbortController();
|
|
1093
980
|
const signal = abortController.signal;
|
|
@@ -1150,11 +1037,6 @@ ${JSON.stringify({
|
|
|
1150
1037
|
getHistory: vi.fn().mockReturnValue([]),
|
|
1151
1038
|
};
|
|
1152
1039
|
client['chat'] = mockChat;
|
|
1153
|
-
const mockGenerator = {
|
|
1154
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1155
|
-
generateContent: mockGenerateContentFn,
|
|
1156
|
-
};
|
|
1157
|
-
client['contentGenerator'] = mockGenerator;
|
|
1158
1040
|
// Act & Assert
|
|
1159
1041
|
// Run up to the limit
|
|
1160
1042
|
for (let i = 0; i < MAX_SESSION_TURNS; i++) {
|
|
@@ -1193,11 +1075,6 @@ ${JSON.stringify({
|
|
|
1193
1075
|
getHistory: vi.fn().mockReturnValue([]),
|
|
1194
1076
|
};
|
|
1195
1077
|
client['chat'] = mockChat;
|
|
1196
|
-
const mockGenerator = {
|
|
1197
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1198
|
-
generateContent: mockGenerateContentFn,
|
|
1199
|
-
};
|
|
1200
|
-
client['contentGenerator'] = mockGenerator;
|
|
1201
1078
|
// Use a signal that never gets aborted
|
|
1202
1079
|
const abortController = new AbortController();
|
|
1203
1080
|
const signal = abortController.signal;
|
|
@@ -1261,11 +1138,6 @@ ${JSON.stringify({
|
|
|
1261
1138
|
]),
|
|
1262
1139
|
};
|
|
1263
1140
|
client['chat'] = mockChat;
|
|
1264
|
-
const mockGenerator = {
|
|
1265
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1266
|
-
generateContent: mockGenerateContentFn,
|
|
1267
|
-
};
|
|
1268
|
-
client['contentGenerator'] = mockGenerator;
|
|
1269
1141
|
});
|
|
1270
1142
|
const testCases = [
|
|
1271
1143
|
{
|
|
@@ -1475,10 +1347,6 @@ ${JSON.stringify({
|
|
|
1475
1347
|
sendMessage: vi.fn().mockResolvedValue({ text: 'summary' }),
|
|
1476
1348
|
};
|
|
1477
1349
|
client['chat'] = mockChat;
|
|
1478
|
-
const mockGenerator = {
|
|
1479
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1480
|
-
};
|
|
1481
|
-
client['contentGenerator'] = mockGenerator;
|
|
1482
1350
|
vi.spyOn(client['config'], 'getIdeMode').mockReturnValue(true);
|
|
1483
1351
|
vi.mocked(ideContext.getIdeContext).mockReturnValue({
|
|
1484
1352
|
workspaceState: {
|
|
@@ -1742,11 +1610,6 @@ ${JSON.stringify({
|
|
|
1742
1610
|
getHistory: vi.fn().mockReturnValue([]),
|
|
1743
1611
|
};
|
|
1744
1612
|
client['chat'] = mockChat;
|
|
1745
|
-
const mockGenerator = {
|
|
1746
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1747
|
-
generateContent: mockGenerateContentFn,
|
|
1748
|
-
};
|
|
1749
|
-
client['contentGenerator'] = mockGenerator;
|
|
1750
1613
|
// Act
|
|
1751
1614
|
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-error');
|
|
1752
1615
|
for await (const _ of stream) {
|
|
@@ -1772,11 +1635,6 @@ ${JSON.stringify({
|
|
|
1772
1635
|
getHistory: vi.fn().mockReturnValue([]),
|
|
1773
1636
|
};
|
|
1774
1637
|
client['chat'] = mockChat;
|
|
1775
|
-
const mockGenerator = {
|
|
1776
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 0 }),
|
|
1777
|
-
generateContent: mockGenerateContentFn,
|
|
1778
|
-
};
|
|
1779
|
-
client['contentGenerator'] = mockGenerator;
|
|
1780
1638
|
// Act
|
|
1781
1639
|
const stream = client.sendMessageStream([{ text: 'Hi' }], new AbortController().signal, 'prompt-id-error');
|
|
1782
1640
|
for await (const _ of stream) {
|
|
@@ -1791,14 +1649,8 @@ ${JSON.stringify({
|
|
|
1791
1649
|
const contents = [{ role: 'user', parts: [{ text: 'hello' }] }];
|
|
1792
1650
|
const generationConfig = { temperature: 0.5 };
|
|
1793
1651
|
const abortSignal = new AbortController().signal;
|
|
1794
|
-
// Mock countTokens
|
|
1795
|
-
const mockGenerator = {
|
|
1796
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 1 }),
|
|
1797
|
-
generateContent: mockGenerateContentFn,
|
|
1798
|
-
};
|
|
1799
|
-
client['contentGenerator'] = mockGenerator;
|
|
1800
1652
|
await client.generateContent(contents, generationConfig, abortSignal, DEFAULT_GEMINI_FLASH_MODEL);
|
|
1801
|
-
expect(
|
|
1653
|
+
expect(mockContentGenerator.generateContent).toHaveBeenCalledWith({
|
|
1802
1654
|
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
1803
1655
|
config: {
|
|
1804
1656
|
abortSignal,
|
|
@@ -1814,18 +1666,13 @@ ${JSON.stringify({
|
|
|
1814
1666
|
const contents = [{ role: 'user', parts: [{ text: 'test' }] }];
|
|
1815
1667
|
const currentModel = initialModel + '-changed';
|
|
1816
1668
|
vi.spyOn(client['config'], 'getModel').mockReturnValueOnce(currentModel);
|
|
1817
|
-
const mockGenerator = {
|
|
1818
|
-
countTokens: vi.fn().mockResolvedValue({ totalTokens: 1 }),
|
|
1819
|
-
generateContent: mockGenerateContentFn,
|
|
1820
|
-
};
|
|
1821
|
-
client['contentGenerator'] = mockGenerator;
|
|
1822
1669
|
await client.generateContent(contents, {}, new AbortController().signal, DEFAULT_GEMINI_FLASH_MODEL);
|
|
1823
|
-
expect(
|
|
1670
|
+
expect(mockContentGenerator.generateContent).not.toHaveBeenCalledWith({
|
|
1824
1671
|
model: initialModel,
|
|
1825
1672
|
config: expect.any(Object),
|
|
1826
1673
|
contents,
|
|
1827
1674
|
});
|
|
1828
|
-
expect(
|
|
1675
|
+
expect(mockContentGenerator.generateContent).toHaveBeenCalledWith({
|
|
1829
1676
|
model: DEFAULT_GEMINI_FLASH_MODEL,
|
|
1830
1677
|
config: expect.any(Object),
|
|
1831
1678
|
contents,
|
|
@@ -1848,65 +1695,5 @@ ${JSON.stringify({
|
|
|
1848
1695
|
expect(mockFallbackHandler).toHaveBeenCalledWith(currentModel, fallbackModel, undefined);
|
|
1849
1696
|
});
|
|
1850
1697
|
});
|
|
1851
|
-
describe('setHistory', () => {
|
|
1852
|
-
it('should strip thought signatures when stripThoughts is true', () => {
|
|
1853
|
-
const mockChat = {
|
|
1854
|
-
setHistory: vi.fn(),
|
|
1855
|
-
};
|
|
1856
|
-
client['chat'] = mockChat;
|
|
1857
|
-
const historyWithThoughts = [
|
|
1858
|
-
{
|
|
1859
|
-
role: 'user',
|
|
1860
|
-
parts: [{ text: 'hello' }],
|
|
1861
|
-
},
|
|
1862
|
-
{
|
|
1863
|
-
role: 'model',
|
|
1864
|
-
parts: [
|
|
1865
|
-
{ text: 'thinking...', thoughtSignature: 'thought-123' },
|
|
1866
|
-
{
|
|
1867
|
-
functionCall: { name: 'test', args: {} },
|
|
1868
|
-
thoughtSignature: 'thought-456',
|
|
1869
|
-
},
|
|
1870
|
-
],
|
|
1871
|
-
},
|
|
1872
|
-
];
|
|
1873
|
-
client.setHistory(historyWithThoughts, { stripThoughts: true });
|
|
1874
|
-
const expectedHistory = [
|
|
1875
|
-
{
|
|
1876
|
-
role: 'user',
|
|
1877
|
-
parts: [{ text: 'hello' }],
|
|
1878
|
-
},
|
|
1879
|
-
{
|
|
1880
|
-
role: 'model',
|
|
1881
|
-
parts: [
|
|
1882
|
-
{ text: 'thinking...' },
|
|
1883
|
-
{ functionCall: { name: 'test', args: {} } },
|
|
1884
|
-
],
|
|
1885
|
-
},
|
|
1886
|
-
];
|
|
1887
|
-
expect(mockChat.setHistory).toHaveBeenCalledWith(expectedHistory);
|
|
1888
|
-
});
|
|
1889
|
-
it('should not strip thought signatures when stripThoughts is false', () => {
|
|
1890
|
-
const mockChat = {
|
|
1891
|
-
setHistory: vi.fn(),
|
|
1892
|
-
};
|
|
1893
|
-
client['chat'] = mockChat;
|
|
1894
|
-
const historyWithThoughts = [
|
|
1895
|
-
{
|
|
1896
|
-
role: 'user',
|
|
1897
|
-
parts: [{ text: 'hello' }],
|
|
1898
|
-
},
|
|
1899
|
-
{
|
|
1900
|
-
role: 'model',
|
|
1901
|
-
parts: [
|
|
1902
|
-
{ text: 'thinking...', thoughtSignature: 'thought-123' },
|
|
1903
|
-
{ text: 'ok', thoughtSignature: 'thought-456' },
|
|
1904
|
-
],
|
|
1905
|
-
},
|
|
1906
|
-
];
|
|
1907
|
-
client.setHistory(historyWithThoughts, { stripThoughts: false });
|
|
1908
|
-
expect(mockChat.setHistory).toHaveBeenCalledWith(historyWithThoughts);
|
|
1909
|
-
});
|
|
1910
|
-
});
|
|
1911
1698
|
});
|
|
1912
1699
|
//# sourceMappingURL=client.test.js.map
|