mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-10 22:21:22 -07:00
fix(patch): cherry-pick f51d745 to release/v0.13.0-preview.0-pr-12586 to patch version v0.13.0-preview.0 and create version 0.13.0-preview.1 (#12595)
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
This commit is contained in:
@@ -25,6 +25,44 @@ describe('classifyGoogleError', () => {
|
||||
expect(result).toBe(regularError);
|
||||
});
|
||||
|
||||
it('should return RetryableQuotaError when message contains "Please retry in Xs"', () => {
|
||||
const complexError = {
|
||||
error: {
|
||||
message:
|
||||
'{"error": {"code": 429, "status": 429, "message": "You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/usage?tab=rate-limit. \n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests, limit: 2\nPlease retry in 44.097740004s.", "details": [{"detail": "??? to (unknown) : APP_ERROR(8) You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/usage?tab=rate-limit. \n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests, limit: 2\nPlease retry in 44.097740004s."}]}}',
|
||||
code: 429,
|
||||
status: 'Too Many Requests',
|
||||
},
|
||||
};
|
||||
const rawError = new Error(JSON.stringify(complexError));
|
||||
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(null);
|
||||
|
||||
const result = classifyGoogleError(rawError);
|
||||
|
||||
expect(result).toBeInstanceOf(RetryableQuotaError);
|
||||
expect((result as RetryableQuotaError).retryDelayMs).toBe(44097.740004);
|
||||
expect((result as RetryableQuotaError).message).toBe(rawError.message);
|
||||
});
|
||||
|
||||
it('should return RetryableQuotaError when error is a string and message contains "Please retry in Xms"', () => {
|
||||
const complexErrorString = JSON.stringify({
|
||||
error: {
|
||||
message:
|
||||
'{"error": {"code": 429, "status": 429, "message": "You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/usage?tab=rate-limit. \n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests, limit: 2\nPlease retry in 900.2ms.", "details": [{"detail": "??? to (unknown) : APP_ERROR(8) You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/usage?tab=rate-limit. \n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests, limit: 2\nPlease retry in 900.2ms."}]}}',
|
||||
code: 429,
|
||||
status: 'Too Many Requests',
|
||||
},
|
||||
});
|
||||
const rawError = new Error(complexErrorString);
|
||||
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(null);
|
||||
|
||||
const result = classifyGoogleError(rawError);
|
||||
|
||||
expect(result).toBeInstanceOf(RetryableQuotaError);
|
||||
expect((result as RetryableQuotaError).retryDelayMs).toBeCloseTo(900.2);
|
||||
expect((result as RetryableQuotaError).message).toBe(rawError.message);
|
||||
});
|
||||
|
||||
it('should return original error if code is not 429', () => {
|
||||
const apiError: GoogleApiError = {
|
||||
code: 500,
|
||||
|
||||
@@ -43,16 +43,20 @@ export class RetryableQuotaError extends Error {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a duration string (e.g., "34.074824224s", "60s") and returns the time in seconds.
|
||||
* Parses a duration string (e.g., "34.074824224s", "60s", "900ms") and returns the time in seconds.
|
||||
* @param duration The duration string to parse.
|
||||
* @returns The duration in seconds, or null if parsing fails.
|
||||
*/
|
||||
function parseDurationInSeconds(duration: string): number | null {
|
||||
if (!duration.endsWith('s')) {
|
||||
return null;
|
||||
if (duration.endsWith('ms')) {
|
||||
const milliseconds = parseFloat(duration.slice(0, -2));
|
||||
return isNaN(milliseconds) ? null : milliseconds / 1000;
|
||||
}
|
||||
const seconds = parseFloat(duration.slice(0, -1));
|
||||
return isNaN(seconds) ? null : seconds;
|
||||
if (duration.endsWith('s')) {
|
||||
const seconds = parseFloat(duration.slice(0, -1));
|
||||
return isNaN(seconds) ? null : seconds;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,6 +68,7 @@ function parseDurationInSeconds(duration: string): number | null {
|
||||
* - If the error suggests a retry delay of more than 2 minutes, it's a `TerminalQuotaError`.
|
||||
* - If the error suggests a retry delay of 2 minutes or less, it's a `RetryableQuotaError`.
|
||||
* - If the error indicates a per-minute limit, it's a `RetryableQuotaError`.
|
||||
* - If the error message contains the phrase "Please retry in X[s|ms]", it's a `RetryableQuotaError`.
|
||||
*
|
||||
* @param error The error to classify.
|
||||
* @returns A `TerminalQuotaError`, `RetryableQuotaError`, or the original `unknown` error.
|
||||
@@ -72,6 +77,24 @@ export function classifyGoogleError(error: unknown): unknown {
|
||||
const googleApiError = parseGoogleApiError(error);
|
||||
|
||||
if (!googleApiError || googleApiError.code !== 429) {
|
||||
// Fallback: try to parse the error message for a retry delay
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
const match = errorMessage.match(/Please retry in ([0-9.]+(?:ms|s))/);
|
||||
if (match?.[1]) {
|
||||
const retryDelaySeconds = parseDurationInSeconds(match[1]);
|
||||
if (retryDelaySeconds !== null) {
|
||||
return new RetryableQuotaError(
|
||||
errorMessage,
|
||||
googleApiError ?? {
|
||||
code: 429,
|
||||
message: errorMessage,
|
||||
details: [],
|
||||
},
|
||||
retryDelaySeconds,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return error; // Not a 429 error we can handle.
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user