feat: launch Gemini 3 in Gemini CLI 🚀🚀🚀 (in main) (#13287)

Co-authored-by: Adam Weidman <65992621+adamfweidman@users.noreply.github.com>
Co-authored-by: Sehoon Shon <sshon@google.com>
Co-authored-by: Adib234 <30782825+Adib234@users.noreply.github.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
Co-authored-by: Aishanee Shah <aishaneeshah@gmail.com>
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: joshualitt <joshualitt@google.com>
Co-authored-by: Jenna Inouye <jinouye@google.com>
This commit is contained in:
Shreya Keshive
2025-11-18 12:01:16 -05:00
committed by GitHub
parent 78075c8a37
commit 86828bb561
79 changed files with 3148 additions and 605 deletions
+66 -1
View File
@@ -8,7 +8,7 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { ApiError } from '@google/genai';
import { AuthType } from '../core/contentGenerator.js';
import type { HttpError } from './retry.js';
import { type HttpError, ModelNotFoundError } from './httpErrors.js';
import { retryWithBackoff } from './retry.js';
import { setSimulate429 } from './testUtils.js';
import { debugLogger } from './debugLogger.js';
@@ -16,6 +16,7 @@ import {
TerminalQuotaError,
RetryableQuotaError,
} from './googleQuotaErrors.js';
import { PREVIEW_GEMINI_MODEL } from '../config/models.js';
// Helper to create a mock function that fails a certain number of times
const createFailingFunction = (
@@ -433,4 +434,68 @@ describe('retryWithBackoff', () => {
);
expect(mockFn).toHaveBeenCalledTimes(1);
});
it('should trigger fallback for OAuth personal users on persistent 500 errors', async () => {
const fallbackCallback = vi.fn().mockResolvedValue('gemini-2.5-flash');
let fallbackOccurred = false;
const mockFn = vi.fn().mockImplementation(async () => {
if (!fallbackOccurred) {
const error: HttpError = new Error('Internal Server Error');
error.status = 500;
throw error;
}
return 'success';
});
const promise = retryWithBackoff(mockFn, {
maxAttempts: 3,
initialDelayMs: 100,
onPersistent429: async (authType?: string, error?: unknown) => {
fallbackOccurred = true;
return await fallbackCallback(authType, error);
},
authType: AuthType.LOGIN_WITH_GOOGLE,
});
await vi.runAllTimersAsync();
await expect(promise).resolves.toBe('success');
expect(fallbackCallback).toHaveBeenCalledWith(
AuthType.LOGIN_WITH_GOOGLE,
expect.objectContaining({ status: 500 }),
);
// 3 attempts (initial + 2 retries) fail with 500, then fallback triggers, then 1 success
expect(mockFn).toHaveBeenCalledTimes(4);
});
it('should trigger fallback for OAuth personal users on ModelNotFoundError', async () => {
const fallbackCallback = vi.fn().mockResolvedValue(PREVIEW_GEMINI_MODEL);
let fallbackOccurred = false;
const mockFn = vi.fn().mockImplementation(async () => {
if (!fallbackOccurred) {
throw new ModelNotFoundError('Requested entity was not found.', 404);
}
return 'success';
});
const promise = retryWithBackoff(mockFn, {
maxAttempts: 3,
initialDelayMs: 100,
onPersistent429: async (authType?: string, error?: unknown) => {
fallbackOccurred = true;
return await fallbackCallback(authType, error);
},
authType: AuthType.LOGIN_WITH_GOOGLE,
});
await vi.runAllTimersAsync();
await expect(promise).resolves.toBe('success');
expect(fallbackCallback).toHaveBeenCalledWith(
AuthType.LOGIN_WITH_GOOGLE,
expect.any(ModelNotFoundError),
);
expect(mockFn).toHaveBeenCalledTimes(2);
});
});