mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 21:03:05 -07:00
@@ -37,6 +37,7 @@ describe('upgradeCommand', () => {
|
|||||||
getContentGeneratorConfig: vi.fn().mockReturnValue({
|
getContentGeneratorConfig: vi.fn().mockReturnValue({
|
||||||
authType: AuthType.LOGIN_WITH_GOOGLE,
|
authType: AuthType.LOGIN_WITH_GOOGLE,
|
||||||
}),
|
}),
|
||||||
|
getUserTierName: vi.fn().mockReturnValue(undefined),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as unknown as CommandContext);
|
} as unknown as CommandContext);
|
||||||
@@ -115,4 +116,23 @@ describe('upgradeCommand', () => {
|
|||||||
});
|
});
|
||||||
expect(openBrowserSecurely).not.toHaveBeenCalled();
|
expect(openBrowserSecurely).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return info message for ultra tiers', async () => {
|
||||||
|
vi.mocked(mockContext.services.config!.getUserTierName).mockReturnValue(
|
||||||
|
'Advanced Ultra',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!upgradeCommand.action) {
|
||||||
|
throw new Error('The upgrade command must have an action.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await upgradeCommand.action(mockContext, '');
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
type: 'message',
|
||||||
|
messageType: 'info',
|
||||||
|
content: 'You are already on the highest tier: Advanced Ultra.',
|
||||||
|
});
|
||||||
|
expect(openBrowserSecurely).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
shouldLaunchBrowser,
|
shouldLaunchBrowser,
|
||||||
UPGRADE_URL_PAGE,
|
UPGRADE_URL_PAGE,
|
||||||
} from '@google/gemini-cli-core';
|
} from '@google/gemini-cli-core';
|
||||||
|
import { isUltraTier } from '../../utils/tierUtils.js';
|
||||||
import { CommandKind, type SlashCommand } from './types.js';
|
import { CommandKind, type SlashCommand } from './types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,6 +36,15 @@ export const upgradeCommand: SlashCommand = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tierName = context.services.config?.getUserTierName();
|
||||||
|
if (isUltraTier(tierName)) {
|
||||||
|
return {
|
||||||
|
type: 'message',
|
||||||
|
messageType: 'info',
|
||||||
|
content: `You are already on the highest tier: ${tierName}.`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (!shouldLaunchBrowser()) {
|
if (!shouldLaunchBrowser()) {
|
||||||
return {
|
return {
|
||||||
type: 'message',
|
type: 'message',
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export const DialogManager = ({
|
|||||||
!!uiState.quota.proQuotaRequest.isModelNotFoundError
|
!!uiState.quota.proQuotaRequest.isModelNotFoundError
|
||||||
}
|
}
|
||||||
authType={uiState.quota.proQuotaRequest.authType}
|
authType={uiState.quota.proQuotaRequest.authType}
|
||||||
|
tierName={config?.getUserTierName()}
|
||||||
onChoice={uiActions.handleProQuotaChoice}
|
onChoice={uiActions.handleProQuotaChoice}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -202,6 +202,40 @@ describe('ProQuotaDialog', () => {
|
|||||||
);
|
);
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should NOT render upgrade option for LOGIN_WITH_GOOGLE if tier is Ultra', () => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<ProQuotaDialog
|
||||||
|
failedModel="gemini-2.5-pro"
|
||||||
|
fallbackModel="gemini-2.5-flash"
|
||||||
|
message="free tier quota error"
|
||||||
|
isTerminalQuotaError={true}
|
||||||
|
isModelNotFoundError={false}
|
||||||
|
authType={AuthType.LOGIN_WITH_GOOGLE}
|
||||||
|
tierName="Gemini Advanced Ultra"
|
||||||
|
onChoice={mockOnChoice}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(RadioButtonSelect).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'Switch to gemini-2.5-flash',
|
||||||
|
value: 'retry_always',
|
||||||
|
key: 'retry_always',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Stop',
|
||||||
|
value: 'retry_later',
|
||||||
|
key: 'retry_later',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when it is a capacity error', () => {
|
describe('when it is a capacity error', () => {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Box, Text } from 'ink';
|
|||||||
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { theme } from '../semantic-colors.js';
|
||||||
import { AuthType } from '@google/gemini-cli-core';
|
import { AuthType } from '@google/gemini-cli-core';
|
||||||
|
import { isUltraTier } from '../../utils/tierUtils.js';
|
||||||
|
|
||||||
interface ProQuotaDialogProps {
|
interface ProQuotaDialogProps {
|
||||||
failedModel: string;
|
failedModel: string;
|
||||||
@@ -17,6 +18,7 @@ interface ProQuotaDialogProps {
|
|||||||
isTerminalQuotaError: boolean;
|
isTerminalQuotaError: boolean;
|
||||||
isModelNotFoundError?: boolean;
|
isModelNotFoundError?: boolean;
|
||||||
authType?: AuthType;
|
authType?: AuthType;
|
||||||
|
tierName?: string;
|
||||||
onChoice: (
|
onChoice: (
|
||||||
choice: 'retry_later' | 'retry_once' | 'retry_always' | 'upgrade',
|
choice: 'retry_later' | 'retry_once' | 'retry_always' | 'upgrade',
|
||||||
) => void;
|
) => void;
|
||||||
@@ -29,6 +31,7 @@ export function ProQuotaDialog({
|
|||||||
isTerminalQuotaError,
|
isTerminalQuotaError,
|
||||||
isModelNotFoundError,
|
isModelNotFoundError,
|
||||||
authType,
|
authType,
|
||||||
|
tierName,
|
||||||
onChoice,
|
onChoice,
|
||||||
}: ProQuotaDialogProps): React.JSX.Element {
|
}: ProQuotaDialogProps): React.JSX.Element {
|
||||||
let items;
|
let items;
|
||||||
@@ -47,6 +50,8 @@ export function ProQuotaDialog({
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
} else if (isModelNotFoundError || isTerminalQuotaError) {
|
} else if (isModelNotFoundError || isTerminalQuotaError) {
|
||||||
|
const isUltra = isUltraTier(tierName);
|
||||||
|
|
||||||
// free users and out of quota users on G1 pro and Cloud Console gets an option to upgrade
|
// free users and out of quota users on G1 pro and Cloud Console gets an option to upgrade
|
||||||
items = [
|
items = [
|
||||||
{
|
{
|
||||||
@@ -54,7 +59,7 @@ export function ProQuotaDialog({
|
|||||||
value: 'retry_always' as const,
|
value: 'retry_always' as const,
|
||||||
key: 'retry_always',
|
key: 'retry_always',
|
||||||
},
|
},
|
||||||
...(authType === AuthType.LOGIN_WITH_GOOGLE
|
...(authType === AuthType.LOGIN_WITH_GOOGLE && !isUltra
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
label: 'Upgrade for higher limits',
|
label: 'Upgrade for higher limits',
|
||||||
|
|||||||
@@ -182,4 +182,23 @@ describe('<UserIdentity />', () => {
|
|||||||
expect(output).toContain('/upgrade');
|
expect(output).toContain('/upgrade');
|
||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not render /upgrade indicator for ultra tiers', async () => {
|
||||||
|
const mockConfig = makeFakeConfig();
|
||||||
|
vi.spyOn(mockConfig, 'getContentGeneratorConfig').mockReturnValue({
|
||||||
|
authType: AuthType.LOGIN_WITH_GOOGLE,
|
||||||
|
model: 'gemini-pro',
|
||||||
|
} as unknown as ContentGeneratorConfig);
|
||||||
|
vi.spyOn(mockConfig, 'getUserTierName').mockReturnValue('Advanced Ultra');
|
||||||
|
|
||||||
|
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
|
||||||
|
<UserIdentity config={mockConfig} />,
|
||||||
|
);
|
||||||
|
await waitUntilReady();
|
||||||
|
|
||||||
|
const output = lastFrame();
|
||||||
|
expect(output).toContain('Plan: Advanced Ultra');
|
||||||
|
expect(output).not.toContain('/upgrade');
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
UserAccountManager,
|
UserAccountManager,
|
||||||
AuthType,
|
AuthType,
|
||||||
} from '@google/gemini-cli-core';
|
} from '@google/gemini-cli-core';
|
||||||
|
import { isUltraTier } from '../../utils/tierUtils.js';
|
||||||
|
|
||||||
interface UserIdentityProps {
|
interface UserIdentityProps {
|
||||||
config: Config;
|
config: Config;
|
||||||
@@ -33,6 +34,8 @@ export const UserIdentity: React.FC<UserIdentityProps> = ({ config }) => {
|
|||||||
[config, authType],
|
[config, authType],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isUltra = useMemo(() => isUltraTier(tierName), [tierName]);
|
||||||
|
|
||||||
if (!authType) {
|
if (!authType) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -60,7 +63,7 @@ export const UserIdentity: React.FC<UserIdentityProps> = ({ config }) => {
|
|||||||
<Text color={theme.text.primary} wrap="truncate-end">
|
<Text color={theme.text.primary} wrap="truncate-end">
|
||||||
<Text bold>Plan:</Text> {tierName}
|
<Text bold>Plan:</Text> {tierName}
|
||||||
</Text>
|
</Text>
|
||||||
<Text color={theme.text.secondary}> /upgrade</Text>
|
{!isUltra && <Text color={theme.text.secondary}> /upgrade</Text>}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2026 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { isUltraTier } from './tierUtils.js';
|
||||||
|
|
||||||
|
describe('tierUtils', () => {
|
||||||
|
describe('isUltraTier', () => {
|
||||||
|
it('should return true if tier name contains "ultra" (case-insensitive)', () => {
|
||||||
|
expect(isUltraTier('Advanced Ultra')).toBe(true);
|
||||||
|
expect(isUltraTier('gemini ultra')).toBe(true);
|
||||||
|
expect(isUltraTier('ULTRA')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if tier name does not contain "ultra"', () => {
|
||||||
|
expect(isUltraTier('Free')).toBe(false);
|
||||||
|
expect(isUltraTier('Pro')).toBe(false);
|
||||||
|
expect(isUltraTier('Standard')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if tier name is undefined', () => {
|
||||||
|
expect(isUltraTier(undefined)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2026 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given tier name corresponds to an "Ultra" tier.
|
||||||
|
*
|
||||||
|
* @param tierName The name of the user's tier.
|
||||||
|
* @returns True if the tier is an "Ultra" tier, false otherwise.
|
||||||
|
*/
|
||||||
|
export function isUltraTier(tierName?: string): boolean {
|
||||||
|
return !!tierName?.toLowerCase().includes('ultra');
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user