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
@@ -10,8 +10,9 @@ import {
type FallbackModelHandler,
type FallbackIntent,
TerminalQuotaError,
UserTierId,
DEFAULT_GEMINI_FLASH_MODEL,
ModelNotFoundError,
type UserTierId,
PREVIEW_GEMINI_MODEL,
} from '@google/gemini-cli-core';
import { useCallback, useEffect, useRef, useState } from 'react';
import { type UseHistoryManagerReturn } from './useHistoryManager.js';
@@ -51,56 +52,29 @@ export function useQuotaAndFallback({
return null;
}
// Use actual user tier if available; otherwise, default to FREE tier behavior (safe default)
const isPaidTier =
userTier === UserTierId.LEGACY || userTier === UserTierId.STANDARD;
const isFallbackModel = failedModel === DEFAULT_GEMINI_FLASH_MODEL;
let message: string;
let isTerminalQuotaError = false;
let isModelNotFoundError = false;
if (error instanceof TerminalQuotaError) {
isTerminalQuotaError = true;
// Common part of the message for both tiers
const messageLines = [
`⚡ You have reached your daily ${failedModel} quota limit.`,
`⚡ You can choose to authenticate with a paid API key${
isFallbackModel ? '.' : ' or continue with the fallback model.'
}`,
`Usage limit reached for ${failedModel}.`,
error.retryDelayMs ? getResetTimeMessage(error.retryDelayMs) : null,
`/stats for usage details`,
`/auth to switch to API key.`,
].filter(Boolean);
message = messageLines.join('\n');
} else if (error instanceof ModelNotFoundError) {
isModelNotFoundError = true;
const messageLines = [
`It seems like you don't have access to Gemini 3.`,
`Learn more at https://goo.gle/enable-preview-features`,
`To disable Gemini 3, disable "Preview features" in /settings.`,
];
// Tier-specific part
if (isPaidTier) {
messageLines.push(
`⚡ Increase your limits by using a Gemini API Key. See: https://goo.gle/gemini-cli-docs-auth#gemini-api-key`,
`⚡ You can switch authentication methods by typing /auth`,
);
} else {
messageLines.push(
`⚡ Increase your limits by `,
`⚡ - signing up for a plan with higher limits at https://goo.gle/set-up-gemini-code-assist`,
`⚡ - or using a Gemini API Key. See: https://goo.gle/gemini-cli-docs-auth#gemini-api-key`,
`⚡ You can switch authentication methods by typing /auth`,
);
}
message = messageLines.join('\n');
} else {
// Capacity error
message = [
`🚦Pardon Our Congestion! It looks like ${failedModel} is very popular at the moment.`,
`Please retry again later.`,
].join('\n');
}
// Add message to UI history
historyManager.addItem(
{
type: MessageType.INFO,
text: message,
},
Date.now(),
);
if (isFallbackModel) {
return 'stop';
message = `${failedModel} is currently experiencing high demand. We apologize and appreciate your patience.`;
}
setModelSwitchedFromQuotaError(true);
@@ -117,6 +91,9 @@ export function useQuotaAndFallback({
failedModel,
fallbackModel,
resolve,
message,
isTerminalQuotaError,
isModelNotFoundError,
});
},
);
@@ -136,14 +113,25 @@ export function useQuotaAndFallback({
setProQuotaRequest(null);
isDialogPending.current = false; // Reset the flag here
if (choice === 'retry') {
historyManager.addItem(
{
type: MessageType.INFO,
text: 'Switched to fallback model. Tip: Press Ctrl+P (or Up Arrow) to recall your previous prompt and submit it again if you wish.',
},
Date.now(),
);
if (choice === 'retry_always') {
// If we were recovering from a Preview Model failure, show a specific message.
if (proQuotaRequest.failedModel === PREVIEW_GEMINI_MODEL) {
historyManager.addItem(
{
type: MessageType.INFO,
text: `Switched to fallback model ${proQuotaRequest.fallbackModel}. ${!proQuotaRequest.isModelNotFoundError ? `We will periodically check if ${PREVIEW_GEMINI_MODEL} is available again.` : ''}`,
},
Date.now(),
);
} else {
historyManager.addItem(
{
type: MessageType.INFO,
text: 'Switched to fallback model.',
},
Date.now(),
);
}
}
},
[proQuotaRequest, historyManager],
@@ -154,3 +142,15 @@ export function useQuotaAndFallback({
handleProQuotaChoice,
};
}
function getResetTimeMessage(delayMs: number): string {
const resetDate = new Date(Date.now() + delayMs);
const timeFormatter = new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'short',
});
return `Access resets at ${timeFormatter.format(resetDate)}.`;
}