@google/gemini-cli-core 0.11.0 → 0.11.1
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/google-gemini-cli-core-0.11.0.tgz +0 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.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/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/utils/errorParsing.d.ts +1 -1
- package/dist/src/utils/errorParsing.js +5 -33
- package/dist/src/utils/errorParsing.js.map +1 -1
- package/dist/src/utils/errorParsing.test.js +0 -88
- package/dist/src/utils/errorParsing.test.js.map +1 -1
- package/dist/src/utils/flashFallback.test.js +26 -45
- package/dist/src/utils/flashFallback.test.js.map +1 -1
- package/dist/src/utils/googleErrors.d.ts +104 -0
- package/dist/src/utils/googleErrors.js +152 -0
- package/dist/src/utils/googleErrors.js.map +1 -0
- package/dist/src/utils/googleErrors.test.d.ts +6 -0
- package/dist/src/utils/googleErrors.test.js +301 -0
- package/dist/src/utils/googleErrors.test.js.map +1 -0
- package/dist/src/utils/googleQuotaErrors.d.ts +35 -0
- package/dist/src/utils/googleQuotaErrors.js +131 -0
- package/dist/src/utils/googleQuotaErrors.js.map +1 -0
- package/dist/src/utils/googleQuotaErrors.test.d.ts +6 -0
- package/dist/src/utils/googleQuotaErrors.test.js +281 -0
- package/dist/src/utils/googleQuotaErrors.test.js.map +1 -0
- package/dist/src/utils/quotaErrorDetection.d.ts +0 -2
- package/dist/src/utils/quotaErrorDetection.js +0 -46
- package/dist/src/utils/quotaErrorDetection.js.map +1 -1
- package/dist/src/utils/retry.js +41 -145
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/retry.test.js +31 -110
- package/dist/src/utils/retry.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"googleQuotaErrors.js","sourceRoot":"","sources":["../../../src/utils/googleQuotaErrors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
7
|
+
import { classifyGoogleError, RetryableQuotaError, TerminalQuotaError, } from './googleQuotaErrors.js';
|
|
8
|
+
import * as errorParser from './googleErrors.js';
|
|
9
|
+
describe('classifyGoogleError', () => {
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
vi.restoreAllMocks();
|
|
12
|
+
});
|
|
13
|
+
it('should return original error if not a Google API error', () => {
|
|
14
|
+
const regularError = new Error('Something went wrong');
|
|
15
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(null);
|
|
16
|
+
const result = classifyGoogleError(regularError);
|
|
17
|
+
expect(result).toBe(regularError);
|
|
18
|
+
});
|
|
19
|
+
it('should return original error if code is not 429', () => {
|
|
20
|
+
const apiError = {
|
|
21
|
+
code: 500,
|
|
22
|
+
message: 'Server error',
|
|
23
|
+
details: [],
|
|
24
|
+
};
|
|
25
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
26
|
+
const originalError = new Error();
|
|
27
|
+
const result = classifyGoogleError(originalError);
|
|
28
|
+
expect(result).toBe(originalError);
|
|
29
|
+
expect(result).not.toBeInstanceOf(TerminalQuotaError);
|
|
30
|
+
expect(result).not.toBeInstanceOf(RetryableQuotaError);
|
|
31
|
+
});
|
|
32
|
+
it('should return TerminalQuotaError for daily quota violations in QuotaFailure', () => {
|
|
33
|
+
const apiError = {
|
|
34
|
+
code: 429,
|
|
35
|
+
message: 'Quota exceeded',
|
|
36
|
+
details: [
|
|
37
|
+
{
|
|
38
|
+
'@type': 'type.googleapis.com/google.rpc.QuotaFailure',
|
|
39
|
+
violations: [
|
|
40
|
+
{
|
|
41
|
+
subject: 'user',
|
|
42
|
+
description: 'daily limit',
|
|
43
|
+
quotaId: 'RequestsPerDay-limit',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
50
|
+
const result = classifyGoogleError(new Error());
|
|
51
|
+
expect(result).toBeInstanceOf(TerminalQuotaError);
|
|
52
|
+
expect(result.cause).toBe(apiError);
|
|
53
|
+
});
|
|
54
|
+
it('should return TerminalQuotaError for daily quota violations in ErrorInfo', () => {
|
|
55
|
+
const apiError = {
|
|
56
|
+
code: 429,
|
|
57
|
+
message: 'Quota exceeded',
|
|
58
|
+
details: [
|
|
59
|
+
{
|
|
60
|
+
'@type': 'type.googleapis.com/google.rpc.ErrorInfo',
|
|
61
|
+
reason: 'QUOTA_EXCEEDED',
|
|
62
|
+
domain: 'googleapis.com',
|
|
63
|
+
metadata: {
|
|
64
|
+
quota_limit: 'RequestsPerDay_PerProject_PerUser',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
70
|
+
const result = classifyGoogleError(new Error());
|
|
71
|
+
expect(result).toBeInstanceOf(TerminalQuotaError);
|
|
72
|
+
});
|
|
73
|
+
it('should return TerminalQuotaError for long retry delays', () => {
|
|
74
|
+
const apiError = {
|
|
75
|
+
code: 429,
|
|
76
|
+
message: 'Too many requests',
|
|
77
|
+
details: [
|
|
78
|
+
{
|
|
79
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
80
|
+
retryDelay: '301s', // > 5 minutes
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
85
|
+
const result = classifyGoogleError(new Error());
|
|
86
|
+
expect(result).toBeInstanceOf(TerminalQuotaError);
|
|
87
|
+
});
|
|
88
|
+
it('should return RetryableQuotaError for short retry delays', () => {
|
|
89
|
+
const apiError = {
|
|
90
|
+
code: 429,
|
|
91
|
+
message: 'Too many requests',
|
|
92
|
+
details: [
|
|
93
|
+
{
|
|
94
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
95
|
+
retryDelay: '45.123s',
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
100
|
+
const result = classifyGoogleError(new Error());
|
|
101
|
+
expect(result).toBeInstanceOf(RetryableQuotaError);
|
|
102
|
+
expect(result.retryDelayMs).toBe(45123);
|
|
103
|
+
});
|
|
104
|
+
it('should return RetryableQuotaError for per-minute quota violations in QuotaFailure', () => {
|
|
105
|
+
const apiError = {
|
|
106
|
+
code: 429,
|
|
107
|
+
message: 'Quota exceeded',
|
|
108
|
+
details: [
|
|
109
|
+
{
|
|
110
|
+
'@type': 'type.googleapis.com/google.rpc.QuotaFailure',
|
|
111
|
+
violations: [
|
|
112
|
+
{
|
|
113
|
+
subject: 'user',
|
|
114
|
+
description: 'per minute limit',
|
|
115
|
+
quotaId: 'RequestsPerMinute-limit',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
};
|
|
121
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
122
|
+
const result = classifyGoogleError(new Error());
|
|
123
|
+
expect(result).toBeInstanceOf(RetryableQuotaError);
|
|
124
|
+
expect(result.retryDelayMs).toBe(60000);
|
|
125
|
+
});
|
|
126
|
+
it('should return RetryableQuotaError for per-minute quota violations in ErrorInfo', () => {
|
|
127
|
+
const apiError = {
|
|
128
|
+
code: 429,
|
|
129
|
+
message: 'Quota exceeded',
|
|
130
|
+
details: [
|
|
131
|
+
{
|
|
132
|
+
'@type': 'type.googleapis.com/google.rpc.ErrorInfo',
|
|
133
|
+
reason: 'QUOTA_EXCEEDED',
|
|
134
|
+
domain: 'googleapis.com',
|
|
135
|
+
metadata: {
|
|
136
|
+
quota_limit: 'RequestsPerMinute_PerProject_PerUser',
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
142
|
+
const result = classifyGoogleError(new Error());
|
|
143
|
+
expect(result).toBeInstanceOf(RetryableQuotaError);
|
|
144
|
+
expect(result.retryDelayMs).toBe(60000);
|
|
145
|
+
});
|
|
146
|
+
it('should return RetryableQuotaError for another short retry delay', () => {
|
|
147
|
+
const apiError = {
|
|
148
|
+
code: 429,
|
|
149
|
+
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.\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests, limit: 2\nPlease retry in 56.185908122s.',
|
|
150
|
+
details: [
|
|
151
|
+
{
|
|
152
|
+
'@type': 'type.googleapis.com/google.rpc.QuotaFailure',
|
|
153
|
+
violations: [
|
|
154
|
+
{
|
|
155
|
+
quotaMetric: 'generativelanguage.googleapis.com/generate_content_free_tier_requests',
|
|
156
|
+
quotaId: 'GenerateRequestsPerMinutePerProjectPerModel-FreeTier',
|
|
157
|
+
quotaDimensions: {
|
|
158
|
+
location: 'global',
|
|
159
|
+
model: 'gemini-2.5-pro',
|
|
160
|
+
},
|
|
161
|
+
quotaValue: '2',
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
'@type': 'type.googleapis.com/google.rpc.Help',
|
|
167
|
+
links: [
|
|
168
|
+
{
|
|
169
|
+
description: 'Learn more about Gemini API quotas',
|
|
170
|
+
url: 'https://ai.google.dev/gemini-api/docs/rate-limits',
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
176
|
+
retryDelay: '56s',
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
};
|
|
180
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
181
|
+
const result = classifyGoogleError(new Error());
|
|
182
|
+
expect(result).toBeInstanceOf(RetryableQuotaError);
|
|
183
|
+
expect(result.retryDelayMs).toBe(56000);
|
|
184
|
+
});
|
|
185
|
+
it('should return RetryableQuotaError for Cloud Code RATE_LIMIT_EXCEEDED with retry delay', () => {
|
|
186
|
+
const apiError = {
|
|
187
|
+
code: 429,
|
|
188
|
+
message: 'You have exhausted your capacity on this model. Your quota will reset after 0s.',
|
|
189
|
+
details: [
|
|
190
|
+
{
|
|
191
|
+
'@type': 'type.googleapis.com/google.rpc.ErrorInfo',
|
|
192
|
+
reason: 'RATE_LIMIT_EXCEEDED',
|
|
193
|
+
domain: 'cloudcode-pa.googleapis.com',
|
|
194
|
+
metadata: {
|
|
195
|
+
uiMessage: 'true',
|
|
196
|
+
model: 'gemini-2.5-pro',
|
|
197
|
+
quotaResetDelay: '539.477544ms',
|
|
198
|
+
quotaResetTimeStamp: '2025-10-20T19:14:08Z',
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
203
|
+
retryDelay: '0.539477544s',
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
};
|
|
207
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
208
|
+
const result = classifyGoogleError(new Error());
|
|
209
|
+
expect(result).toBeInstanceOf(RetryableQuotaError);
|
|
210
|
+
expect(result.retryDelayMs).toBeCloseTo(539.477544);
|
|
211
|
+
});
|
|
212
|
+
it('should return TerminalQuotaError for Cloud Code QUOTA_EXHAUSTED', () => {
|
|
213
|
+
const apiError = {
|
|
214
|
+
code: 429,
|
|
215
|
+
message: 'You have exhausted your capacity on this model. Your quota will reset after 0s.',
|
|
216
|
+
details: [
|
|
217
|
+
{
|
|
218
|
+
'@type': 'type.googleapis.com/google.rpc.ErrorInfo',
|
|
219
|
+
reason: 'QUOTA_EXHAUSTED',
|
|
220
|
+
domain: 'cloudcode-pa.googleapis.com',
|
|
221
|
+
metadata: {
|
|
222
|
+
uiMessage: 'true',
|
|
223
|
+
model: 'gemini-2.5-pro',
|
|
224
|
+
quotaResetDelay: '539.477544ms',
|
|
225
|
+
quotaResetTimeStamp: '2025-10-20T19:14:08Z',
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
230
|
+
retryDelay: '0.539477544s',
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
};
|
|
234
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
235
|
+
const result = classifyGoogleError(new Error());
|
|
236
|
+
expect(result).toBeInstanceOf(TerminalQuotaError);
|
|
237
|
+
});
|
|
238
|
+
it('should prioritize daily limit over retry info', () => {
|
|
239
|
+
const apiError = {
|
|
240
|
+
code: 429,
|
|
241
|
+
message: 'Quota exceeded',
|
|
242
|
+
details: [
|
|
243
|
+
{
|
|
244
|
+
'@type': 'type.googleapis.com/google.rpc.QuotaFailure',
|
|
245
|
+
violations: [
|
|
246
|
+
{
|
|
247
|
+
subject: 'user',
|
|
248
|
+
description: 'daily limit',
|
|
249
|
+
quotaId: 'RequestsPerDay-limit',
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
255
|
+
retryDelay: '10s',
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
};
|
|
259
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
260
|
+
const result = classifyGoogleError(new Error());
|
|
261
|
+
expect(result).toBeInstanceOf(TerminalQuotaError);
|
|
262
|
+
});
|
|
263
|
+
it('should return original error for 429 without specific details', () => {
|
|
264
|
+
const apiError = {
|
|
265
|
+
code: 429,
|
|
266
|
+
message: 'Too many requests',
|
|
267
|
+
details: [
|
|
268
|
+
{
|
|
269
|
+
'@type': 'type.googleapis.com/google.rpc.DebugInfo',
|
|
270
|
+
detail: 'some debug info',
|
|
271
|
+
stackEntries: [],
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
};
|
|
275
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
276
|
+
const originalError = new Error();
|
|
277
|
+
const result = classifyGoogleError(originalError);
|
|
278
|
+
expect(result).toBe(originalError);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
//# sourceMappingURL=googleQuotaErrors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"googleQuotaErrors.test.js","sourceRoot":"","sources":["../../../src/utils/googleQuotaErrors.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,5 +14,3 @@ export interface ApiError {
|
|
|
14
14
|
}
|
|
15
15
|
export declare function isApiError(error: unknown): error is ApiError;
|
|
16
16
|
export declare function isStructuredError(error: unknown): error is StructuredError;
|
|
17
|
-
export declare function isProQuotaExceededError(error: unknown): boolean;
|
|
18
|
-
export declare function isGenericQuotaExceededError(error: unknown): boolean;
|
|
@@ -16,50 +16,4 @@ export function isStructuredError(error) {
|
|
|
16
16
|
'message' in error &&
|
|
17
17
|
typeof error.message === 'string');
|
|
18
18
|
}
|
|
19
|
-
export function isProQuotaExceededError(error) {
|
|
20
|
-
// Check for Pro quota exceeded errors by looking for the specific pattern
|
|
21
|
-
// This will match patterns like:
|
|
22
|
-
// - "Quota exceeded for quota metric 'Gemini 2.5 Pro Requests'"
|
|
23
|
-
// - "Quota exceeded for quota metric 'Gemini 2.5-preview Pro Requests'"
|
|
24
|
-
// We use string methods instead of regex to avoid ReDoS vulnerabilities
|
|
25
|
-
const checkMessage = (message) => message.includes("Quota exceeded for quota metric 'Gemini") &&
|
|
26
|
-
message.includes("Pro Requests'");
|
|
27
|
-
if (typeof error === 'string') {
|
|
28
|
-
return checkMessage(error);
|
|
29
|
-
}
|
|
30
|
-
if (isStructuredError(error)) {
|
|
31
|
-
return checkMessage(error.message);
|
|
32
|
-
}
|
|
33
|
-
if (isApiError(error)) {
|
|
34
|
-
return checkMessage(error.error.message);
|
|
35
|
-
}
|
|
36
|
-
// Check if it's a Gaxios error with response data
|
|
37
|
-
if (error && typeof error === 'object' && 'response' in error) {
|
|
38
|
-
const gaxiosError = error;
|
|
39
|
-
if (gaxiosError.response && gaxiosError.response.data) {
|
|
40
|
-
if (typeof gaxiosError.response.data === 'string') {
|
|
41
|
-
return checkMessage(gaxiosError.response.data);
|
|
42
|
-
}
|
|
43
|
-
if (typeof gaxiosError.response.data === 'object' &&
|
|
44
|
-
gaxiosError.response.data !== null &&
|
|
45
|
-
'error' in gaxiosError.response.data) {
|
|
46
|
-
const errorData = gaxiosError.response.data;
|
|
47
|
-
return checkMessage(errorData.error?.message || '');
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
export function isGenericQuotaExceededError(error) {
|
|
54
|
-
if (typeof error === 'string') {
|
|
55
|
-
return error.includes('Quota exceeded for quota metric');
|
|
56
|
-
}
|
|
57
|
-
if (isStructuredError(error)) {
|
|
58
|
-
return error.message.includes('Quota exceeded for quota metric');
|
|
59
|
-
}
|
|
60
|
-
if (isApiError(error)) {
|
|
61
|
-
return error.error.message.includes('Quota exceeded for quota metric');
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
19
|
//# sourceMappingURL=quotaErrorDetection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotaErrorDetection.js","sourceRoot":"","sources":["../../../src/utils/quotaErrorDetection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAO,IAAI,KAAK;QAChB,OAAQ,KAAkB,CAAC,KAAK,KAAK,QAAQ;QAC7C,SAAS,IAAK,KAAkB,CAAC,KAAK,CACvC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,SAAS,IAAI,KAAK;QAClB,OAAQ,KAAyB,CAAC,OAAO,KAAK,QAAQ,CACvD,CAAC;AACJ,CAAC
|
|
1
|
+
{"version":3,"file":"quotaErrorDetection.js","sourceRoot":"","sources":["../../../src/utils/quotaErrorDetection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAO,IAAI,KAAK;QAChB,OAAQ,KAAkB,CAAC,KAAK,KAAK,QAAQ;QAC7C,SAAS,IAAK,KAAkB,CAAC,KAAK,CACvC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,SAAS,IAAI,KAAK;QAClB,OAAQ,KAAyB,CAAC,OAAO,KAAK,QAAQ,CACvD,CAAC;AACJ,CAAC"}
|
package/dist/src/utils/retry.js
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { ApiError } from '@google/genai';
|
|
7
7
|
import { AuthType } from '../core/contentGenerator.js';
|
|
8
|
-
import {
|
|
8
|
+
import { classifyGoogleError, RetryableQuotaError, TerminalQuotaError, } from './googleQuotaErrors.js';
|
|
9
9
|
import { delay, createAbortError } from './delay.js';
|
|
10
10
|
import { debugLogger } from './debugLogger.js';
|
|
11
11
|
const FETCH_FAILED_MESSAGE = 'exception TypeError: fetch failed sending request';
|
|
12
12
|
const DEFAULT_RETRY_OPTIONS = {
|
|
13
|
-
maxAttempts:
|
|
13
|
+
maxAttempts: 3,
|
|
14
14
|
initialDelayMs: 5000,
|
|
15
15
|
maxDelayMs: 30000, // 30 seconds
|
|
16
16
|
shouldRetryOnError: defaultShouldRetry,
|
|
@@ -65,7 +65,6 @@ export async function retryWithBackoff(fn, options) {
|
|
|
65
65
|
};
|
|
66
66
|
let attempt = 0;
|
|
67
67
|
let currentDelay = initialDelayMs;
|
|
68
|
-
let consecutive429Count = 0;
|
|
69
68
|
while (attempt < maxAttempts) {
|
|
70
69
|
if (signal?.aborted) {
|
|
71
70
|
throw createAbortError();
|
|
@@ -87,115 +86,58 @@ export async function retryWithBackoff(fn, options) {
|
|
|
87
86
|
if (error instanceof Error && error.name === 'AbortError') {
|
|
88
87
|
throw error;
|
|
89
88
|
}
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
attempt = 0;
|
|
101
|
-
consecutive429Count = 0;
|
|
102
|
-
currentDelay = initialDelayMs;
|
|
103
|
-
// With the model updated, we continue to the next attempt
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
// Fallback handler returned null/false, meaning don't continue - stop retry process
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
catch (fallbackError) {
|
|
112
|
-
// If fallback fails, continue with original error
|
|
113
|
-
debugLogger.warn('Fallback to Flash model failed:', fallbackError);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
// Check for generic quota exceeded error (but not Pro, which was handled above) - immediate fallback for OAuth users
|
|
117
|
-
if (errorStatus === 429 &&
|
|
118
|
-
authType === AuthType.LOGIN_WITH_GOOGLE &&
|
|
119
|
-
!isProQuotaExceededError(error) &&
|
|
120
|
-
isGenericQuotaExceededError(error) &&
|
|
121
|
-
onPersistent429) {
|
|
122
|
-
try {
|
|
123
|
-
const fallbackModel = await onPersistent429(authType, error);
|
|
124
|
-
if (fallbackModel !== false && fallbackModel !== null) {
|
|
125
|
-
// Reset attempt counter and try with new model
|
|
126
|
-
attempt = 0;
|
|
127
|
-
consecutive429Count = 0;
|
|
128
|
-
currentDelay = initialDelayMs;
|
|
129
|
-
// With the model updated, we continue to the next attempt
|
|
130
|
-
continue;
|
|
89
|
+
const classifiedError = classifyGoogleError(error);
|
|
90
|
+
if (classifiedError instanceof TerminalQuotaError) {
|
|
91
|
+
if (onPersistent429 && authType === AuthType.LOGIN_WITH_GOOGLE) {
|
|
92
|
+
try {
|
|
93
|
+
const fallbackModel = await onPersistent429(authType, classifiedError);
|
|
94
|
+
if (fallbackModel) {
|
|
95
|
+
attempt = 0; // Reset attempts and retry with the new model.
|
|
96
|
+
currentDelay = initialDelayMs;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
131
99
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
throw error;
|
|
100
|
+
catch (fallbackError) {
|
|
101
|
+
debugLogger.warn('Fallback to Flash model failed:', fallbackError);
|
|
135
102
|
}
|
|
136
103
|
}
|
|
137
|
-
|
|
138
|
-
// If fallback fails, continue with original error
|
|
139
|
-
debugLogger.warn('Fallback to Flash model failed:', fallbackError);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
// Track consecutive 429 errors
|
|
143
|
-
if (errorStatus === 429) {
|
|
144
|
-
consecutive429Count++;
|
|
104
|
+
throw classifiedError; // Throw if no fallback or fallback failed.
|
|
145
105
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// With the model updated, we continue to the next attempt
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
// Fallback handler returned null/false, meaning don't continue - stop retry process
|
|
165
|
-
throw error;
|
|
106
|
+
if (classifiedError instanceof RetryableQuotaError) {
|
|
107
|
+
if (attempt >= maxAttempts) {
|
|
108
|
+
if (onPersistent429 && authType === AuthType.LOGIN_WITH_GOOGLE) {
|
|
109
|
+
try {
|
|
110
|
+
const fallbackModel = await onPersistent429(authType, classifiedError);
|
|
111
|
+
if (fallbackModel) {
|
|
112
|
+
attempt = 0; // Reset attempts and retry with the new model.
|
|
113
|
+
currentDelay = initialDelayMs;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (fallbackError) {
|
|
118
|
+
console.warn('Model fallback failed:', fallbackError);
|
|
119
|
+
}
|
|
166
120
|
}
|
|
121
|
+
throw classifiedError;
|
|
167
122
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
123
|
+
console.warn(`Attempt ${attempt} failed: ${classifiedError.message}. Retrying after ${classifiedError.retryDelayMs}ms...`);
|
|
124
|
+
await delay(classifiedError.retryDelayMs, signal);
|
|
125
|
+
continue;
|
|
172
126
|
}
|
|
173
|
-
//
|
|
127
|
+
// Generic retry logic for other errors
|
|
174
128
|
if (attempt >= maxAttempts ||
|
|
175
129
|
!shouldRetryOnError(error, retryFetchErrors)) {
|
|
176
130
|
throw error;
|
|
177
131
|
}
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
// Fall back to exponential backoff with jitter
|
|
188
|
-
logRetryAttempt(attempt, error, errorStatus);
|
|
189
|
-
// Add jitter: +/- 30% of currentDelay
|
|
190
|
-
const jitter = currentDelay * 0.3 * (Math.random() * 2 - 1);
|
|
191
|
-
const delayWithJitter = Math.max(0, currentDelay + jitter);
|
|
192
|
-
await delay(delayWithJitter, signal);
|
|
193
|
-
currentDelay = Math.min(maxDelayMs, currentDelay * 2);
|
|
194
|
-
}
|
|
132
|
+
const errorStatus = getErrorStatus(error);
|
|
133
|
+
logRetryAttempt(attempt, error, errorStatus);
|
|
134
|
+
// Exponential backoff with jitter for non-quota errors
|
|
135
|
+
const jitter = currentDelay * 0.3 * (Math.random() * 2 - 1);
|
|
136
|
+
const delayWithJitter = Math.max(0, currentDelay + jitter);
|
|
137
|
+
await delay(delayWithJitter, signal);
|
|
138
|
+
currentDelay = Math.min(maxDelayMs, currentDelay * 2);
|
|
195
139
|
}
|
|
196
140
|
}
|
|
197
|
-
// This line should theoretically be unreachable due to the throw in the catch block.
|
|
198
|
-
// Added for type safety and to satisfy the compiler that a promise is always returned.
|
|
199
141
|
throw new Error('Retry attempts exhausted');
|
|
200
142
|
}
|
|
201
143
|
/**
|
|
@@ -220,52 +162,6 @@ export function getErrorStatus(error) {
|
|
|
220
162
|
}
|
|
221
163
|
return undefined;
|
|
222
164
|
}
|
|
223
|
-
/**
|
|
224
|
-
* Extracts the Retry-After delay from an error object's headers.
|
|
225
|
-
* @param error The error object.
|
|
226
|
-
* @returns The delay in milliseconds, or 0 if not found or invalid.
|
|
227
|
-
*/
|
|
228
|
-
function getRetryAfterDelayMs(error) {
|
|
229
|
-
if (typeof error === 'object' && error !== null) {
|
|
230
|
-
// Check for error.response.headers (common in axios errors)
|
|
231
|
-
if ('response' in error &&
|
|
232
|
-
typeof error.response === 'object' &&
|
|
233
|
-
error.response !== null) {
|
|
234
|
-
const response = error.response;
|
|
235
|
-
if ('headers' in response &&
|
|
236
|
-
typeof response.headers === 'object' &&
|
|
237
|
-
response.headers !== null) {
|
|
238
|
-
const headers = response.headers;
|
|
239
|
-
const retryAfterHeader = headers['retry-after'];
|
|
240
|
-
if (typeof retryAfterHeader === 'string') {
|
|
241
|
-
const retryAfterSeconds = parseInt(retryAfterHeader, 10);
|
|
242
|
-
if (!isNaN(retryAfterSeconds)) {
|
|
243
|
-
return retryAfterSeconds * 1000;
|
|
244
|
-
}
|
|
245
|
-
// It might be an HTTP date
|
|
246
|
-
const retryAfterDate = new Date(retryAfterHeader);
|
|
247
|
-
if (!isNaN(retryAfterDate.getTime())) {
|
|
248
|
-
return Math.max(0, retryAfterDate.getTime() - Date.now());
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return 0;
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Determines the delay duration based on the error, prioritizing Retry-After header.
|
|
258
|
-
* @param error The error object.
|
|
259
|
-
* @returns An object containing the delay duration in milliseconds and the error status.
|
|
260
|
-
*/
|
|
261
|
-
function getDelayDurationAndStatus(error) {
|
|
262
|
-
const errorStatus = getErrorStatus(error);
|
|
263
|
-
let delayDurationMs = 0;
|
|
264
|
-
if (errorStatus === 429) {
|
|
265
|
-
delayDurationMs = getRetryAfterDelayMs(error);
|
|
266
|
-
}
|
|
267
|
-
return { delayDurationMs, errorStatus };
|
|
268
|
-
}
|
|
269
165
|
/**
|
|
270
166
|
* Logs a message for a retry attempt when using exponential backoff.
|
|
271
167
|
* @param attempt The current attempt number.
|