@google/gemini-cli-core 0.18.0-nightly.20251120.2231497b1 → 0.18.0-preview.0
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/src/agents/executor.js +12 -21
- package/dist/src/agents/executor.js.map +1 -1
- package/dist/src/agents/executor.test.js +53 -57
- package/dist/src/agents/executor.test.js.map +1 -1
- package/dist/src/agents/registry.d.ts +4 -0
- package/dist/src/agents/registry.js +23 -0
- package/dist/src/agents/registry.js.map +1 -1
- package/dist/src/agents/registry.test.js +30 -16
- package/dist/src/agents/registry.test.js.map +1 -1
- package/dist/src/code_assist/oauth-credential-storage.test.js +1 -0
- package/dist/src/code_assist/oauth-credential-storage.test.js.map +1 -1
- package/dist/src/code_assist/oauth2.test.js +5 -4
- package/dist/src/code_assist/oauth2.test.js.map +1 -1
- package/dist/src/config/config.d.ts +5 -0
- package/dist/src/config/config.js +10 -0
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +1 -0
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/defaultModelConfigs.js +31 -4
- package/dist/src/config/defaultModelConfigs.js.map +1 -1
- package/dist/src/core/client.d.ts +1 -2
- package/dist/src/core/client.js +11 -23
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +14 -38
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.d.ts +0 -2
- package/dist/src/core/coreToolScheduler.js +1 -3
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +1 -17
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +10 -5
- package/dist/src/core/geminiChat.js +37 -20
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +96 -54
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/logger.test.js +12 -11
- package/dist/src/core/logger.test.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.js +6 -2
- package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
- package/dist/src/core/prompts.test.js +4 -1
- package/dist/src/core/prompts.test.js.map +1 -1
- package/dist/src/core/turn.d.ts +2 -1
- package/dist/src/core/turn.js +2 -7
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/core/turn.test.js +18 -21
- package/dist/src/core/turn.test.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/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/oauth-provider.test.js +1 -0
- package/dist/src/mcp/oauth-provider.test.js.map +1 -1
- package/dist/src/routing/modelRouterService.js +1 -1
- package/dist/src/routing/modelRouterService.js.map +1 -1
- package/dist/src/routing/strategies/classifierStrategy.test.js +4 -3
- package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -1
- package/dist/src/services/chatCompressionService.d.ts +1 -1
- package/dist/src/services/chatCompressionService.js +1 -1
- package/dist/src/services/modelConfig.integration.test.js +34 -0
- package/dist/src/services/modelConfig.integration.test.js.map +1 -1
- package/dist/src/services/modelConfigService.d.ts +2 -0
- package/dist/src/services/modelConfigService.js +7 -2
- package/dist/src/services/modelConfigService.js.map +1 -1
- package/dist/src/services/modelConfigService.test.js +22 -0
- package/dist/src/services/modelConfigService.test.js.map +1 -1
- package/dist/src/services/test-data/resolved-aliases.golden.json +37 -4
- package/dist/src/telemetry/activity-monitor.test.js +4 -1
- package/dist/src/telemetry/activity-monitor.test.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +1 -0
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/modifiable-tool.d.ts +1 -1
- package/dist/src/tools/modifiable-tool.js +2 -2
- package/dist/src/tools/modifiable-tool.js.map +1 -1
- package/dist/src/tools/modifiable-tool.test.js +12 -11
- package/dist/src/tools/modifiable-tool.test.js.map +1 -1
- package/dist/src/utils/debugLogger.test.js +27 -25
- package/dist/src/utils/debugLogger.test.js.map +1 -1
- package/dist/src/utils/editor.d.ts +1 -1
- package/dist/src/utils/editor.js +3 -2
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/editor.test.js +7 -46
- package/dist/src/utils/editor.test.js.map +1 -1
- package/dist/src/utils/events.d.ts +33 -3
- package/dist/src/utils/events.js +35 -15
- package/dist/src/utils/events.js.map +1 -1
- package/dist/src/utils/events.test.js +86 -5
- package/dist/src/utils/events.test.js.map +1 -1
- package/dist/src/utils/installationManager.test.js +2 -1
- package/dist/src/utils/installationManager.test.js.map +1 -1
- package/dist/src/utils/llm-edit-fixer.test.js +2 -1
- package/dist/src/utils/llm-edit-fixer.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +3 -2
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.js +1 -1
- package/dist/src/utils/memoryImportProcessor.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.test.js +8 -14
- package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
- package/dist/src/utils/nextSpeakerChecker.test.js +3 -1
- package/dist/src/utils/nextSpeakerChecker.test.js.map +1 -1
- package/dist/src/utils/systemEncoding.test.js +2 -1
- package/dist/src/utils/systemEncoding.test.js.map +1 -1
- package/dist/src/utils/userAccountManager.test.js +7 -6
- package/dist/src/utils/userAccountManager.test.js.map +1 -1
- package/dist/src/utils/workspaceContext.test.js +5 -4
- package/dist/src/utils/workspaceContext.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/google-gemini-cli-core-0.18.0-nightly.20251118.86828bb56.tgz +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
|
-
import { ApiError } from '@google/genai';
|
|
7
|
+
import { ApiError, ThinkingLevel } from '@google/genai';
|
|
8
8
|
import { GeminiChat, InvalidStreamError, StreamEventType, SYNTHETIC_THOUGHT_SIGNATURE, } from './geminiChat.js';
|
|
9
9
|
import { setSimulate429 } from '../utils/testUtils.js';
|
|
10
10
|
import { DEFAULT_GEMINI_FLASH_MODEL, DEFAULT_GEMINI_MODEL, PREVIEW_GEMINI_MODEL, } from '../config/models.js';
|
|
@@ -65,7 +65,6 @@ describe('GeminiChat', () => {
|
|
|
65
65
|
let mockContentGenerator;
|
|
66
66
|
let chat;
|
|
67
67
|
let mockConfig;
|
|
68
|
-
const config = {};
|
|
69
68
|
beforeEach(() => {
|
|
70
69
|
vi.clearAllMocks();
|
|
71
70
|
vi.mocked(uiTelemetryService.setLastPromptTokenCount).mockClear();
|
|
@@ -104,6 +103,17 @@ describe('GeminiChat', () => {
|
|
|
104
103
|
}),
|
|
105
104
|
getContentGenerator: vi.fn().mockReturnValue(mockContentGenerator),
|
|
106
105
|
getRetryFetchErrors: vi.fn().mockReturnValue(false),
|
|
106
|
+
modelConfigService: {
|
|
107
|
+
getResolvedConfig: vi.fn().mockImplementation((modelConfigKey) => ({
|
|
108
|
+
model: modelConfigKey.model,
|
|
109
|
+
generateContentConfig: {
|
|
110
|
+
temperature: 0,
|
|
111
|
+
thinkingConfig: {
|
|
112
|
+
thinkingBudget: 1000,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
})),
|
|
116
|
+
},
|
|
107
117
|
isPreviewModelBypassMode: vi.fn().mockReturnValue(false),
|
|
108
118
|
setPreviewModelBypassMode: vi.fn(),
|
|
109
119
|
isPreviewModelFallbackMode: vi.fn().mockReturnValue(false),
|
|
@@ -113,7 +123,7 @@ describe('GeminiChat', () => {
|
|
|
113
123
|
// Disable 429 simulation for tests
|
|
114
124
|
setSimulate429(false);
|
|
115
125
|
// Reset history for each test by creating a new instance
|
|
116
|
-
chat = new GeminiChat(mockConfig
|
|
126
|
+
chat = new GeminiChat(mockConfig);
|
|
117
127
|
});
|
|
118
128
|
afterEach(() => {
|
|
119
129
|
vi.restoreAllMocks();
|
|
@@ -125,12 +135,12 @@ describe('GeminiChat', () => {
|
|
|
125
135
|
{ role: 'user', parts: [{ text: 'Hello' }] },
|
|
126
136
|
{ role: 'model', parts: [{ text: 'Hi there' }] },
|
|
127
137
|
];
|
|
128
|
-
const chatWithHistory = new GeminiChat(mockConfig,
|
|
138
|
+
const chatWithHistory = new GeminiChat(mockConfig, '', [], history);
|
|
129
139
|
const estimatedTokens = Math.ceil(JSON.stringify(history).length / 4);
|
|
130
140
|
expect(chatWithHistory.getLastPromptTokenCount()).toBe(estimatedTokens);
|
|
131
141
|
});
|
|
132
142
|
it('should initialize lastPromptTokenCount for empty history', () => {
|
|
133
|
-
const chatEmpty = new GeminiChat(mockConfig
|
|
143
|
+
const chatEmpty = new GeminiChat(mockConfig);
|
|
134
144
|
expect(chatEmpty.getLastPromptTokenCount()).toBe(Math.ceil(JSON.stringify([]).length / 4));
|
|
135
145
|
});
|
|
136
146
|
});
|
|
@@ -163,7 +173,7 @@ describe('GeminiChat', () => {
|
|
|
163
173
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithToolCall);
|
|
164
174
|
// 2. Action & Assert: The stream processing should complete without throwing an error
|
|
165
175
|
// because the presence of a tool call makes the empty final chunk acceptable.
|
|
166
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
176
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-tool-call-empty-end', new AbortController().signal);
|
|
167
177
|
await expect((async () => {
|
|
168
178
|
for await (const _ of stream) {
|
|
169
179
|
/* consume stream */
|
|
@@ -203,7 +213,7 @@ describe('GeminiChat', () => {
|
|
|
203
213
|
})();
|
|
204
214
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithNoFinish);
|
|
205
215
|
// 2. Action & Assert: The stream should fail because there's no finish reason.
|
|
206
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
216
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-no-finish-empty-end', new AbortController().signal);
|
|
207
217
|
await expect((async () => {
|
|
208
218
|
for await (const _ of stream) {
|
|
209
219
|
/* consume stream */
|
|
@@ -238,7 +248,7 @@ describe('GeminiChat', () => {
|
|
|
238
248
|
})();
|
|
239
249
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithInvalidEnd);
|
|
240
250
|
// 2. Action & Assert: The stream should complete without throwing an error.
|
|
241
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
251
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-valid-then-invalid-end', new AbortController().signal);
|
|
242
252
|
await expect((async () => {
|
|
243
253
|
for await (const _ of stream) {
|
|
244
254
|
/* consume stream */
|
|
@@ -273,7 +283,7 @@ describe('GeminiChat', () => {
|
|
|
273
283
|
})();
|
|
274
284
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(multiChunkStream);
|
|
275
285
|
// 2. Action: Send a message and consume the stream.
|
|
276
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
286
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-empty-chunk-consolidation', new AbortController().signal);
|
|
277
287
|
for await (const _ of stream) {
|
|
278
288
|
// Consume the stream
|
|
279
289
|
}
|
|
@@ -321,7 +331,7 @@ describe('GeminiChat', () => {
|
|
|
321
331
|
})();
|
|
322
332
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(multiChunkStream);
|
|
323
333
|
// 2. Action: Send a message and consume the stream.
|
|
324
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
334
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-multi-chunk', new AbortController().signal);
|
|
325
335
|
for await (const _ of stream) {
|
|
326
336
|
// Consume the stream to trigger history recording.
|
|
327
337
|
}
|
|
@@ -357,7 +367,7 @@ describe('GeminiChat', () => {
|
|
|
357
367
|
})();
|
|
358
368
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(mixedContentStream);
|
|
359
369
|
// 2. Action: Send a message and fully consume the stream to trigger history recording.
|
|
360
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
370
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-mixed-chunk', new AbortController().signal);
|
|
361
371
|
for await (const _ of stream) {
|
|
362
372
|
// This loop consumes the stream.
|
|
363
373
|
}
|
|
@@ -385,7 +395,7 @@ describe('GeminiChat', () => {
|
|
|
385
395
|
],
|
|
386
396
|
};
|
|
387
397
|
})());
|
|
388
|
-
const stream = await chat.sendMessageStream(
|
|
398
|
+
const stream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-fast-retry', new AbortController().signal);
|
|
389
399
|
for await (const _ of stream) {
|
|
390
400
|
// consume stream
|
|
391
401
|
}
|
|
@@ -405,7 +415,7 @@ describe('GeminiChat', () => {
|
|
|
405
415
|
],
|
|
406
416
|
};
|
|
407
417
|
})());
|
|
408
|
-
const stream = await chat.sendMessageStream(
|
|
418
|
+
const stream = await chat.sendMessageStream({ model: DEFAULT_GEMINI_FLASH_MODEL }, 'test', 'prompt-id-normal-retry', new AbortController().signal);
|
|
409
419
|
for await (const _ of stream) {
|
|
410
420
|
// consume stream
|
|
411
421
|
}
|
|
@@ -437,7 +447,7 @@ describe('GeminiChat', () => {
|
|
|
437
447
|
}));
|
|
438
448
|
// ACT
|
|
439
449
|
const consumeStream = async () => {
|
|
440
|
-
const stream = await chat.sendMessageStream(
|
|
450
|
+
const stream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-bypass', new AbortController().signal);
|
|
441
451
|
// Consume the stream to trigger execution
|
|
442
452
|
for await (const _ of stream) {
|
|
443
453
|
// do nothing
|
|
@@ -484,14 +494,12 @@ describe('GeminiChat', () => {
|
|
|
484
494
|
})();
|
|
485
495
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(emptyStreamResponse);
|
|
486
496
|
// 3. Action: Send the function response back to the model and consume the stream.
|
|
487
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash', {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
response: { name: 'Vesuvio' },
|
|
492
|
-
},
|
|
497
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, {
|
|
498
|
+
functionResponse: {
|
|
499
|
+
name: 'find_restaurant',
|
|
500
|
+
response: { name: 'Vesuvio' },
|
|
493
501
|
},
|
|
494
|
-
}, 'prompt-id-stream-1');
|
|
502
|
+
}, 'prompt-id-stream-1', new AbortController().signal);
|
|
495
503
|
// 4. Assert: The stream processing should throw an InvalidStreamError.
|
|
496
504
|
await expect((async () => {
|
|
497
505
|
for await (const _ of stream) {
|
|
@@ -522,7 +530,7 @@ describe('GeminiChat', () => {
|
|
|
522
530
|
};
|
|
523
531
|
})();
|
|
524
532
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithToolCall);
|
|
525
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
533
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-1', new AbortController().signal);
|
|
526
534
|
// Should not throw an error
|
|
527
535
|
await expect((async () => {
|
|
528
536
|
for await (const _ of stream) {
|
|
@@ -546,7 +554,7 @@ describe('GeminiChat', () => {
|
|
|
546
554
|
};
|
|
547
555
|
})();
|
|
548
556
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithoutFinishReason);
|
|
549
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
557
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-1', new AbortController().signal);
|
|
550
558
|
await expect((async () => {
|
|
551
559
|
for await (const _ of stream) {
|
|
552
560
|
// consume stream
|
|
@@ -569,7 +577,7 @@ describe('GeminiChat', () => {
|
|
|
569
577
|
};
|
|
570
578
|
})();
|
|
571
579
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithEmptyResponse);
|
|
572
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
580
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-1', new AbortController().signal);
|
|
573
581
|
await expect((async () => {
|
|
574
582
|
for await (const _ of stream) {
|
|
575
583
|
// consume stream
|
|
@@ -592,7 +600,7 @@ describe('GeminiChat', () => {
|
|
|
592
600
|
};
|
|
593
601
|
})();
|
|
594
602
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(validStream);
|
|
595
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
603
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-1', new AbortController().signal);
|
|
596
604
|
// Should not throw an error
|
|
597
605
|
await expect((async () => {
|
|
598
606
|
for await (const _ of stream) {
|
|
@@ -616,7 +624,7 @@ describe('GeminiChat', () => {
|
|
|
616
624
|
};
|
|
617
625
|
})();
|
|
618
626
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(streamWithMalformedFunctionCall);
|
|
619
|
-
const stream = await chat.sendMessageStream('gemini-2.5-pro',
|
|
627
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.5-pro' }, 'test', 'prompt-id-malformed', new AbortController().signal);
|
|
620
628
|
// Should throw an error
|
|
621
629
|
await expect((async () => {
|
|
622
630
|
for await (const _ of stream) {
|
|
@@ -650,7 +658,7 @@ describe('GeminiChat', () => {
|
|
|
650
658
|
};
|
|
651
659
|
})());
|
|
652
660
|
// 2. Send a message
|
|
653
|
-
const stream = await chat.sendMessageStream('gemini-2.5-pro',
|
|
661
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.5-pro' }, 'test retry', 'prompt-id-retry-malformed', new AbortController().signal);
|
|
654
662
|
const events = [];
|
|
655
663
|
for await (const event of stream) {
|
|
656
664
|
events.push(event);
|
|
@@ -688,7 +696,7 @@ describe('GeminiChat', () => {
|
|
|
688
696
|
};
|
|
689
697
|
})();
|
|
690
698
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(response);
|
|
691
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
699
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'hello', 'prompt-id-1', new AbortController().signal);
|
|
692
700
|
for await (const _ of stream) {
|
|
693
701
|
// consume stream
|
|
694
702
|
}
|
|
@@ -700,9 +708,43 @@ describe('GeminiChat', () => {
|
|
|
700
708
|
parts: [{ text: 'hello' }],
|
|
701
709
|
},
|
|
702
710
|
],
|
|
703
|
-
config: {
|
|
711
|
+
config: {
|
|
712
|
+
systemInstruction: '',
|
|
713
|
+
tools: [],
|
|
714
|
+
temperature: 0,
|
|
715
|
+
thinkingConfig: {
|
|
716
|
+
thinkingBudget: 1000,
|
|
717
|
+
},
|
|
718
|
+
abortSignal: expect.any(AbortSignal),
|
|
719
|
+
},
|
|
704
720
|
}, 'prompt-id-1');
|
|
705
721
|
});
|
|
722
|
+
it('should use thinkingLevel and remove thinkingBudget for gemini-3 models', async () => {
|
|
723
|
+
const response = (async function* () {
|
|
724
|
+
yield {
|
|
725
|
+
candidates: [
|
|
726
|
+
{
|
|
727
|
+
content: { parts: [{ text: 'response' }], role: 'model' },
|
|
728
|
+
finishReason: 'STOP',
|
|
729
|
+
},
|
|
730
|
+
],
|
|
731
|
+
};
|
|
732
|
+
})();
|
|
733
|
+
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(response);
|
|
734
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-3-test-only-model-string-for-testing' }, 'hello', 'prompt-id-thinking-level', new AbortController().signal);
|
|
735
|
+
for await (const _ of stream) {
|
|
736
|
+
// consume stream
|
|
737
|
+
}
|
|
738
|
+
expect(mockContentGenerator.generateContentStream).toHaveBeenCalledWith(expect.objectContaining({
|
|
739
|
+
model: 'gemini-3-test-only-model-string-for-testing',
|
|
740
|
+
config: expect.objectContaining({
|
|
741
|
+
thinkingConfig: {
|
|
742
|
+
thinkingBudget: undefined,
|
|
743
|
+
thinkingLevel: ThinkingLevel.HIGH,
|
|
744
|
+
},
|
|
745
|
+
}),
|
|
746
|
+
}), 'prompt-id-thinking-level');
|
|
747
|
+
});
|
|
706
748
|
});
|
|
707
749
|
describe('addHistory', () => {
|
|
708
750
|
it('should add a new content item to the history', () => {
|
|
@@ -740,7 +782,7 @@ describe('GeminiChat', () => {
|
|
|
740
782
|
candidates: [{ content: { parts: [{ text: '' }] } }],
|
|
741
783
|
};
|
|
742
784
|
})());
|
|
743
|
-
const stream = await chat.sendMessageStream('gemini-1.5-pro',
|
|
785
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-1.5-pro' }, 'test', 'prompt-id-no-retry', new AbortController().signal);
|
|
744
786
|
await expect((async () => {
|
|
745
787
|
for await (const _ of stream) {
|
|
746
788
|
// Must loop to trigger the internal logic that throws.
|
|
@@ -773,7 +815,7 @@ describe('GeminiChat', () => {
|
|
|
773
815
|
};
|
|
774
816
|
})());
|
|
775
817
|
// ACT: Send a message and collect all events from the stream.
|
|
776
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
818
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-yield-retry', new AbortController().signal);
|
|
777
819
|
const events = [];
|
|
778
820
|
for await (const event of stream) {
|
|
779
821
|
events.push(event);
|
|
@@ -805,7 +847,7 @@ describe('GeminiChat', () => {
|
|
|
805
847
|
],
|
|
806
848
|
};
|
|
807
849
|
})());
|
|
808
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
850
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test', 'prompt-id-retry-success', new AbortController().signal);
|
|
809
851
|
const chunks = [];
|
|
810
852
|
for await (const chunk of stream) {
|
|
811
853
|
chunks.push(chunk);
|
|
@@ -856,7 +898,7 @@ describe('GeminiChat', () => {
|
|
|
856
898
|
],
|
|
857
899
|
};
|
|
858
900
|
})());
|
|
859
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
901
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-retry-temperature', new AbortController().signal);
|
|
860
902
|
for await (const _ of stream) {
|
|
861
903
|
// consume stream
|
|
862
904
|
}
|
|
@@ -864,7 +906,7 @@ describe('GeminiChat', () => {
|
|
|
864
906
|
// First call should have original temperature
|
|
865
907
|
expect(mockContentGenerator.generateContentStream).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
866
908
|
config: expect.objectContaining({
|
|
867
|
-
temperature: 0
|
|
909
|
+
temperature: 0,
|
|
868
910
|
}),
|
|
869
911
|
}), 'prompt-id-retry-temperature');
|
|
870
912
|
// Second call (retry) should have temperature 1
|
|
@@ -887,7 +929,7 @@ describe('GeminiChat', () => {
|
|
|
887
929
|
],
|
|
888
930
|
};
|
|
889
931
|
})());
|
|
890
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
932
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test', 'prompt-id-retry-fail', new AbortController().signal);
|
|
891
933
|
await expect(async () => {
|
|
892
934
|
for await (const _ of stream) {
|
|
893
935
|
// Must loop to trigger the internal logic that throws.
|
|
@@ -936,7 +978,7 @@ describe('GeminiChat', () => {
|
|
|
936
978
|
it('should not retry on 400 Bad Request errors', async () => {
|
|
937
979
|
const error400 = new ApiError({ message: 'Bad Request', status: 400 });
|
|
938
980
|
vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(error400);
|
|
939
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
981
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-400', new AbortController().signal);
|
|
940
982
|
await expect((async () => {
|
|
941
983
|
for await (const _ of stream) {
|
|
942
984
|
/* consume stream */
|
|
@@ -959,7 +1001,7 @@ describe('GeminiChat', () => {
|
|
|
959
1001
|
],
|
|
960
1002
|
};
|
|
961
1003
|
})());
|
|
962
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
1004
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-429-retry', new AbortController().signal);
|
|
963
1005
|
const events = [];
|
|
964
1006
|
for await (const event of stream) {
|
|
965
1007
|
events.push(event);
|
|
@@ -988,7 +1030,7 @@ describe('GeminiChat', () => {
|
|
|
988
1030
|
],
|
|
989
1031
|
};
|
|
990
1032
|
})());
|
|
991
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
1033
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-500-retry', new AbortController().signal);
|
|
992
1034
|
const events = [];
|
|
993
1035
|
for await (const event of stream) {
|
|
994
1036
|
events.push(event);
|
|
@@ -1024,7 +1066,7 @@ describe('GeminiChat', () => {
|
|
|
1024
1066
|
throw error;
|
|
1025
1067
|
}
|
|
1026
1068
|
});
|
|
1027
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
1069
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-fetch-error-retry', new AbortController().signal);
|
|
1028
1070
|
const events = [];
|
|
1029
1071
|
for await (const event of stream) {
|
|
1030
1072
|
events.push(event);
|
|
@@ -1067,7 +1109,7 @@ describe('GeminiChat', () => {
|
|
|
1067
1109
|
};
|
|
1068
1110
|
})());
|
|
1069
1111
|
// 3. Send a new message
|
|
1070
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
1112
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'Second question', 'prompt-id-retry-existing', new AbortController().signal);
|
|
1071
1113
|
for await (const _ of stream) {
|
|
1072
1114
|
// consume stream
|
|
1073
1115
|
}
|
|
@@ -1119,7 +1161,7 @@ describe('GeminiChat', () => {
|
|
|
1119
1161
|
};
|
|
1120
1162
|
})());
|
|
1121
1163
|
// 2. Call the method and consume the stream.
|
|
1122
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
1164
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test empty stream', 'prompt-id-empty-stream', new AbortController().signal);
|
|
1123
1165
|
const chunks = [];
|
|
1124
1166
|
for await (const chunk of stream) {
|
|
1125
1167
|
chunks.push(chunk);
|
|
@@ -1180,11 +1222,11 @@ describe('GeminiChat', () => {
|
|
|
1180
1222
|
.mockResolvedValueOnce(firstStreamGenerator)
|
|
1181
1223
|
.mockResolvedValueOnce(secondStreamGenerator);
|
|
1182
1224
|
// 3. Start the first stream and consume only the first chunk to pause it
|
|
1183
|
-
const firstStream = await chat.sendMessageStream('test-model',
|
|
1225
|
+
const firstStream = await chat.sendMessageStream({ model: 'test-model' }, 'first', 'prompt-1', new AbortController().signal);
|
|
1184
1226
|
const firstStreamIterator = firstStream[Symbol.asyncIterator]();
|
|
1185
1227
|
await firstStreamIterator.next();
|
|
1186
1228
|
// 4. While the first stream is paused, start the second call. It will block.
|
|
1187
|
-
const secondStreamPromise = chat.sendMessageStream('test-model',
|
|
1229
|
+
const secondStreamPromise = chat.sendMessageStream({ model: 'test-model' }, 'second', 'prompt-2', new AbortController().signal);
|
|
1188
1230
|
// 5. Assert that only one API call has been made so far.
|
|
1189
1231
|
expect(mockContentGenerator.generateContentStream).toHaveBeenCalledTimes(1);
|
|
1190
1232
|
// 6. Unblock and fully consume the first stream to completion.
|
|
@@ -1224,7 +1266,7 @@ describe('GeminiChat', () => {
|
|
|
1224
1266
|
vi.mocked(mockContentGenerator.generateContentStream).mockImplementation(async () => (async function* () {
|
|
1225
1267
|
yield mockResponse;
|
|
1226
1268
|
})());
|
|
1227
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
1269
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'test message', 'prompt-id-res3', new AbortController().signal);
|
|
1228
1270
|
for await (const _ of stream) {
|
|
1229
1271
|
// consume stream
|
|
1230
1272
|
}
|
|
@@ -1287,7 +1329,7 @@ describe('GeminiChat', () => {
|
|
|
1287
1329
|
isInFallbackModeSpy.mockReturnValue(true);
|
|
1288
1330
|
return true; // Signal retry
|
|
1289
1331
|
});
|
|
1290
|
-
const stream = await chat.sendMessageStream('test-model',
|
|
1332
|
+
const stream = await chat.sendMessageStream({ model: 'test-model' }, 'trigger 429', 'prompt-id-fb1', new AbortController().signal);
|
|
1291
1333
|
// Consume stream to trigger logic
|
|
1292
1334
|
for await (const _ of stream) {
|
|
1293
1335
|
// no-op
|
|
@@ -1303,7 +1345,7 @@ describe('GeminiChat', () => {
|
|
|
1303
1345
|
vi.mocked(mockConfig.getModel).mockReturnValue('gemini-pro');
|
|
1304
1346
|
vi.mocked(mockContentGenerator.generateContentStream).mockRejectedValue(error429);
|
|
1305
1347
|
mockHandleFallback.mockResolvedValue(false);
|
|
1306
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
1348
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test stop', 'prompt-id-fb2', new AbortController().signal);
|
|
1307
1349
|
await expect((async () => {
|
|
1308
1350
|
for await (const _ of stream) {
|
|
1309
1351
|
/* consume stream */
|
|
@@ -1347,7 +1389,7 @@ describe('GeminiChat', () => {
|
|
|
1347
1389
|
};
|
|
1348
1390
|
})());
|
|
1349
1391
|
// Send a message and consume the stream
|
|
1350
|
-
const stream = await chat.sendMessageStream('gemini-2.0-flash',
|
|
1392
|
+
const stream = await chat.sendMessageStream({ model: 'gemini-2.0-flash' }, 'test message', 'prompt-id-discard-test', new AbortController().signal);
|
|
1351
1393
|
const events = [];
|
|
1352
1394
|
for await (const event of stream) {
|
|
1353
1395
|
events.push(event);
|
|
@@ -1411,7 +1453,7 @@ describe('GeminiChat', () => {
|
|
|
1411
1453
|
};
|
|
1412
1454
|
})();
|
|
1413
1455
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
|
|
1414
|
-
await chat.sendMessageStream('test-model',
|
|
1456
|
+
await chat.sendMessageStream({ model: 'test-model' }, 'test', 'prompt-id-preview-model-reset', new AbortController().signal);
|
|
1415
1457
|
expect(mockConfig.setPreviewModelBypassMode).toHaveBeenCalledWith(false);
|
|
1416
1458
|
});
|
|
1417
1459
|
it('should reset previewModelFallbackMode to false upon successful Preview Model usage', async () => {
|
|
@@ -1426,7 +1468,7 @@ describe('GeminiChat', () => {
|
|
|
1426
1468
|
};
|
|
1427
1469
|
})();
|
|
1428
1470
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
|
|
1429
|
-
const resultStream = await chat.sendMessageStream(
|
|
1471
|
+
const resultStream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-preview-model-healing', new AbortController().signal);
|
|
1430
1472
|
for await (const _ of resultStream) {
|
|
1431
1473
|
// consume stream
|
|
1432
1474
|
}
|
|
@@ -1446,7 +1488,7 @@ describe('GeminiChat', () => {
|
|
|
1446
1488
|
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(stream);
|
|
1447
1489
|
// Simulate bypass mode being active (downgrade happened)
|
|
1448
1490
|
vi.mocked(mockConfig.isPreviewModelBypassMode).mockReturnValue(true);
|
|
1449
|
-
const resultStream = await chat.sendMessageStream(
|
|
1491
|
+
const resultStream = await chat.sendMessageStream({ model: PREVIEW_GEMINI_MODEL }, 'test', 'prompt-id-bypass-no-healing', new AbortController().signal);
|
|
1450
1492
|
for await (const _ of resultStream) {
|
|
1451
1493
|
// consume stream
|
|
1452
1494
|
}
|
|
@@ -1455,7 +1497,7 @@ describe('GeminiChat', () => {
|
|
|
1455
1497
|
});
|
|
1456
1498
|
describe('ensureActiveLoopHasThoughtSignatures', () => {
|
|
1457
1499
|
it('should add thoughtSignature to the first functionCall in each model turn of the active loop', () => {
|
|
1458
|
-
const chat = new GeminiChat(mockConfig,
|
|
1500
|
+
const chat = new GeminiChat(mockConfig, '', [], []);
|
|
1459
1501
|
const history = [
|
|
1460
1502
|
{ role: 'user', parts: [{ text: 'Old message' }] },
|
|
1461
1503
|
{
|
|
@@ -1504,7 +1546,7 @@ describe('GeminiChat', () => {
|
|
|
1504
1546
|
expect(newContents[5]?.parts?.[1]).not.toHaveProperty('thoughtSignature');
|
|
1505
1547
|
});
|
|
1506
1548
|
it('should not modify contents if there is no user text message', () => {
|
|
1507
|
-
const chat = new GeminiChat(mockConfig,
|
|
1549
|
+
const chat = new GeminiChat(mockConfig, '', [], []);
|
|
1508
1550
|
const history = [
|
|
1509
1551
|
{
|
|
1510
1552
|
role: 'user',
|
|
@@ -1520,13 +1562,13 @@ describe('GeminiChat', () => {
|
|
|
1520
1562
|
expect(newContents[1]?.parts?.[0]).not.toHaveProperty('thoughtSignature');
|
|
1521
1563
|
});
|
|
1522
1564
|
it('should handle an empty history', () => {
|
|
1523
|
-
const chat = new GeminiChat(mockConfig,
|
|
1565
|
+
const chat = new GeminiChat(mockConfig, '', []);
|
|
1524
1566
|
const history = [];
|
|
1525
1567
|
const newContents = chat.ensureActiveLoopHasThoughtSignatures(history);
|
|
1526
1568
|
expect(newContents).toEqual([]);
|
|
1527
1569
|
});
|
|
1528
1570
|
it('should handle history with only a user message', () => {
|
|
1529
|
-
const chat = new GeminiChat(mockConfig,
|
|
1571
|
+
const chat = new GeminiChat(mockConfig, '', []);
|
|
1530
1572
|
const history = [{ role: 'user', parts: [{ text: 'Hello' }] }];
|
|
1531
1573
|
const newContents = chat.ensureActiveLoopHasThoughtSignatures(history);
|
|
1532
1574
|
expect(newContents).toEqual(history);
|