@google/gemini-cli-core 0.10.0-nightly.20250926.1487841d → 0.10.0-nightly.20251009.cd354aeb
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/README.md +108 -71
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/src/agents/codebase-investigator.d.ts +46 -0
- package/dist/src/agents/codebase-investigator.js +136 -0
- package/dist/src/agents/codebase-investigator.js.map +1 -0
- package/dist/src/agents/executor.d.ts +92 -0
- package/dist/src/agents/executor.js +580 -0
- package/dist/src/agents/executor.js.map +1 -0
- package/dist/src/agents/executor.test.d.ts +6 -0
- package/dist/src/agents/executor.test.js +595 -0
- package/dist/src/agents/executor.test.js.map +1 -0
- package/dist/src/agents/invocation.d.ts +46 -0
- package/dist/src/agents/invocation.js +102 -0
- package/dist/src/agents/invocation.js.map +1 -0
- package/dist/src/agents/invocation.test.d.ts +6 -0
- package/dist/src/agents/invocation.test.js +215 -0
- package/dist/src/agents/invocation.test.js.map +1 -0
- package/dist/src/agents/registry.d.ts +36 -0
- package/dist/src/agents/registry.js +60 -0
- package/dist/src/agents/registry.js.map +1 -0
- package/dist/src/agents/registry.test.d.ts +6 -0
- package/dist/src/agents/registry.test.js +146 -0
- package/dist/src/agents/registry.test.js.map +1 -0
- package/dist/src/agents/schema-utils.d.ts +39 -0
- package/dist/src/agents/schema-utils.js +57 -0
- package/dist/src/agents/schema-utils.js.map +1 -0
- package/dist/src/agents/schema-utils.test.d.ts +6 -0
- package/dist/src/agents/schema-utils.test.js +144 -0
- package/dist/src/agents/schema-utils.test.js.map +1 -0
- package/dist/src/agents/subagent-tool-wrapper.d.ts +38 -0
- package/dist/src/agents/subagent-tool-wrapper.js +48 -0
- package/dist/src/agents/subagent-tool-wrapper.js.map +1 -0
- package/dist/src/agents/subagent-tool-wrapper.test.d.ts +6 -0
- package/dist/src/agents/subagent-tool-wrapper.test.js +112 -0
- package/dist/src/agents/subagent-tool-wrapper.test.js.map +1 -0
- package/dist/src/agents/types.d.ts +145 -0
- package/dist/src/agents/types.js +18 -0
- package/dist/src/agents/types.js.map +1 -0
- package/dist/src/agents/utils.d.ts +15 -0
- package/dist/src/agents/utils.js +29 -0
- package/dist/src/agents/utils.js.map +1 -0
- package/dist/src/agents/utils.test.d.ts +6 -0
- package/dist/src/agents/utils.test.js +87 -0
- package/dist/src/agents/utils.test.js.map +1 -0
- package/dist/src/code_assist/setup.js +4 -2
- package/dist/src/code_assist/setup.js.map +1 -1
- package/dist/src/config/config.d.ts +28 -5
- package/dist/src/config/config.js +61 -7
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +63 -0
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/core/baseLlmClient.js +19 -21
- package/dist/src/core/baseLlmClient.js.map +1 -1
- package/dist/src/core/baseLlmClient.test.js +57 -17
- package/dist/src/core/baseLlmClient.test.js.map +1 -1
- package/dist/src/core/client.d.ts +2 -0
- package/dist/src/core/client.js +52 -43
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +300 -95
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.js +3 -1
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/coreToolScheduler.test.js +33 -23
- package/dist/src/core/coreToolScheduler.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +6 -0
- package/dist/src/core/geminiChat.js +21 -18
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +20 -19
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.test.js +11 -11
- package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
- package/dist/src/core/prompts.d.ts +2 -1
- package/dist/src/core/prompts.js +88 -15
- package/dist/src/core/prompts.js.map +1 -1
- package/dist/src/core/prompts.test.js +73 -24
- package/dist/src/core/prompts.test.js.map +1 -1
- package/dist/src/core/turn.d.ts +10 -2
- package/dist/src/core/turn.js +6 -1
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/core/turn.test.js +1 -1
- package/dist/src/core/turn.test.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/ide/detect-ide.d.ts +1 -0
- package/dist/src/ide/detect-ide.js +4 -1
- package/dist/src/ide/detect-ide.js.map +1 -1
- package/dist/src/ide/detect-ide.test.js +11 -0
- package/dist/src/ide/detect-ide.test.js.map +1 -1
- package/dist/src/ide/ide-client.js +3 -3
- package/dist/src/ide/ide-client.test.js +4 -4
- package/dist/src/ide/ide-installer.js +1 -1
- package/dist/src/ide/ide-installer.js.map +1 -1
- package/dist/src/ide/ide-installer.test.js +13 -1
- package/dist/src/ide/ide-installer.test.js.map +1 -1
- 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/mcp/sa-impersonation-provider.d.ts +33 -0
- package/dist/src/mcp/sa-impersonation-provider.js +130 -0
- package/dist/src/mcp/sa-impersonation-provider.js.map +1 -0
- package/dist/src/mcp/sa-impersonation-provider.test.d.ts +6 -0
- package/dist/src/mcp/sa-impersonation-provider.test.js +117 -0
- package/dist/src/mcp/sa-impersonation-provider.test.js.map +1 -0
- package/dist/src/services/chatRecordingService.d.ts +2 -1
- package/dist/src/services/chatRecordingService.js +2 -1
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/fileSystemService.d.ts +9 -0
- package/dist/src/services/fileSystemService.js +11 -0
- package/dist/src/services/fileSystemService.js.map +1 -1
- package/dist/src/services/shellExecutionService.d.ts +2 -0
- package/dist/src/services/shellExecutionService.js +48 -7
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +13 -4
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +12 -2
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +79 -9
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +99 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +11 -3
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +27 -4
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/constants.d.ts +6 -24
- package/dist/src/telemetry/constants.js +7 -25
- package/dist/src/telemetry/constants.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +5 -3
- package/dist/src/telemetry/index.js +10 -3
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +6 -1
- package/dist/src/telemetry/loggers.js +132 -12
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.circular.js +3 -3
- package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +160 -13
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/memory-monitor.d.ts +149 -0
- package/dist/src/telemetry/memory-monitor.js +335 -0
- package/dist/src/telemetry/memory-monitor.js.map +1 -0
- package/dist/src/telemetry/memory-monitor.test.d.ts +6 -0
- package/dist/src/telemetry/memory-monitor.test.js +472 -0
- package/dist/src/telemetry/memory-monitor.test.js.map +1 -0
- package/dist/src/telemetry/metrics.d.ts +392 -20
- package/dist/src/telemetry/metrics.js +463 -235
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/metrics.test.js +391 -55
- package/dist/src/telemetry/metrics.test.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +37 -1
- package/dist/src/telemetry/types.js +60 -0
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +2 -2
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
- package/dist/src/test-utils/mock-tool.d.ts +28 -3
- package/dist/src/test-utils/mock-tool.js +71 -1
- package/dist/src/test-utils/mock-tool.js.map +1 -1
- package/dist/src/tools/glob.js +2 -1
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +3 -2
- package/dist/src/tools/mcp-client.js +44 -30
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +168 -5
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/shell.js +2 -2
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/shell.test.js +1 -1
- package/dist/src/tools/shell.test.js.map +1 -1
- package/dist/src/tools/smart-edit.d.ts +20 -1
- package/dist/src/tools/smart-edit.js +149 -9
- package/dist/src/tools/smart-edit.js.map +1 -1
- package/dist/src/tools/smart-edit.test.js +150 -29
- package/dist/src/tools/smart-edit.test.js.map +1 -1
- package/dist/src/tools/tool-names.d.ts +8 -0
- package/dist/src/tools/tool-names.js +17 -0
- package/dist/src/tools/tool-names.js.map +1 -0
- package/dist/src/tools/tool-registry.test.js +10 -10
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/web-fetch.js +3 -0
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/web-fetch.test.js +44 -0
- package/dist/src/tools/web-fetch.test.js.map +1 -1
- package/dist/src/tools/write-file.js +2 -1
- package/dist/src/tools/write-file.js.map +1 -1
- package/dist/src/tools/write-todos.d.ts +1 -1
- package/dist/src/tools/write-todos.js +4 -3
- package/dist/src/tools/write-todos.js.map +1 -1
- package/dist/src/utils/editCorrector.js +2 -2
- package/dist/src/utils/editCorrector.js.map +1 -1
- package/dist/src/utils/editor.js +1 -0
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/editor.test.js +1 -0
- package/dist/src/utils/editor.test.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/formatters.d.ts +1 -0
- package/dist/src/utils/formatters.js +2 -1
- package/dist/src/utils/formatters.js.map +1 -1
- package/dist/src/utils/formatters.test.d.ts +6 -0
- package/dist/src/utils/formatters.test.js +26 -0
- package/dist/src/utils/formatters.test.js.map +1 -0
- package/dist/src/utils/googleErrors.d.ts +104 -0
- package/dist/src/utils/googleErrors.js +108 -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 +212 -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 +108 -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 +189 -0
- package/dist/src/utils/googleQuotaErrors.test.js.map +1 -0
- package/dist/src/utils/llm-edit-fixer.js +13 -4
- package/dist/src/utils/llm-edit-fixer.js.map +1 -1
- package/dist/src/utils/llm-edit-fixer.test.js +81 -0
- package/dist/src/utils/llm-edit-fixer.test.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.d.ts +1 -0
- package/dist/src/utils/memoryDiscovery.js +2 -1
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/memoryDiscovery.test.js +99 -21
- package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.js +13 -20
- package/dist/src/utils/memoryImportProcessor.js.map +1 -1
- package/dist/src/utils/memoryImportProcessor.test.js +14 -0
- package/dist/src/utils/memoryImportProcessor.test.js.map +1 -1
- 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.d.ts +3 -1
- package/dist/src/utils/retry.js +53 -161
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/retry.test.js +87 -146
- package/dist/src/utils/retry.test.js.map +1 -1
- package/dist/src/utils/terminalSerializer.d.ts +1 -4
- package/dist/src/utils/terminalSerializer.js +3 -3
- package/dist/src/utils/terminalSerializer.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/src/test-utils/tools.d.ts +0 -45
- package/dist/src/test-utils/tools.js +0 -105
- package/dist/src/test-utils/tools.js.map +0 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect } from 'vitest';
|
|
7
|
+
import { parseGoogleApiError } from './googleErrors.js';
|
|
8
|
+
describe('parseGoogleApiError', () => {
|
|
9
|
+
it('should return null for non-gaxios errors', () => {
|
|
10
|
+
expect(parseGoogleApiError(new Error('vanilla error'))).toBeNull();
|
|
11
|
+
expect(parseGoogleApiError(null)).toBeNull();
|
|
12
|
+
expect(parseGoogleApiError({})).toBeNull();
|
|
13
|
+
});
|
|
14
|
+
it('should parse a standard gaxios error', () => {
|
|
15
|
+
const mockError = {
|
|
16
|
+
response: {
|
|
17
|
+
status: 429,
|
|
18
|
+
data: {
|
|
19
|
+
error: {
|
|
20
|
+
code: 429,
|
|
21
|
+
message: 'Quota exceeded',
|
|
22
|
+
details: [
|
|
23
|
+
{
|
|
24
|
+
'@type': 'type.googleapis.com/google.rpc.QuotaFailure',
|
|
25
|
+
violations: [{ subject: 'user', description: 'daily limit' }],
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const parsed = parseGoogleApiError(mockError);
|
|
33
|
+
expect(parsed).not.toBeNull();
|
|
34
|
+
expect(parsed?.code).toBe(429);
|
|
35
|
+
expect(parsed?.message).toBe('Quota exceeded');
|
|
36
|
+
expect(parsed?.details).toHaveLength(1);
|
|
37
|
+
const detail = parsed?.details[0];
|
|
38
|
+
expect(detail['@type']).toBe('type.googleapis.com/google.rpc.QuotaFailure');
|
|
39
|
+
expect(detail.violations[0].description).toBe('daily limit');
|
|
40
|
+
});
|
|
41
|
+
it('should parse an error with details stringified in the message', () => {
|
|
42
|
+
const innerError = {
|
|
43
|
+
error: {
|
|
44
|
+
code: 429,
|
|
45
|
+
message: 'Inner quota message',
|
|
46
|
+
details: [
|
|
47
|
+
{
|
|
48
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
49
|
+
retryDelay: '10s',
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const mockError = {
|
|
55
|
+
response: {
|
|
56
|
+
status: 429,
|
|
57
|
+
data: {
|
|
58
|
+
error: {
|
|
59
|
+
code: 429,
|
|
60
|
+
message: JSON.stringify(innerError),
|
|
61
|
+
details: [], // Top-level details are empty
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
const parsed = parseGoogleApiError(mockError);
|
|
67
|
+
expect(parsed).not.toBeNull();
|
|
68
|
+
expect(parsed?.code).toBe(429);
|
|
69
|
+
expect(parsed?.message).toBe('Inner quota message');
|
|
70
|
+
expect(parsed?.details).toHaveLength(1);
|
|
71
|
+
expect(parsed?.details[0]['@type']).toBe('type.googleapis.com/google.rpc.RetryInfo');
|
|
72
|
+
});
|
|
73
|
+
it('should return null if details are not in the expected format', () => {
|
|
74
|
+
const mockError = {
|
|
75
|
+
response: {
|
|
76
|
+
status: 400,
|
|
77
|
+
data: {
|
|
78
|
+
error: {
|
|
79
|
+
code: 400,
|
|
80
|
+
message: 'Bad Request',
|
|
81
|
+
details: 'just a string', // Invalid details format
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
expect(parseGoogleApiError(mockError)).toBeNull();
|
|
87
|
+
});
|
|
88
|
+
it('should return null if there are no valid details', () => {
|
|
89
|
+
const mockError = {
|
|
90
|
+
response: {
|
|
91
|
+
status: 400,
|
|
92
|
+
data: {
|
|
93
|
+
error: {
|
|
94
|
+
code: 400,
|
|
95
|
+
message: 'Bad Request',
|
|
96
|
+
details: [
|
|
97
|
+
{
|
|
98
|
+
// missing '@type'
|
|
99
|
+
reason: 'some reason',
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
expect(parseGoogleApiError(mockError)).toBeNull();
|
|
107
|
+
});
|
|
108
|
+
it('should parse a doubly nested error in the message', () => {
|
|
109
|
+
const innerError = {
|
|
110
|
+
error: {
|
|
111
|
+
code: 429,
|
|
112
|
+
message: 'Innermost quota message',
|
|
113
|
+
details: [
|
|
114
|
+
{
|
|
115
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
116
|
+
retryDelay: '20s',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
const middleError = {
|
|
122
|
+
error: {
|
|
123
|
+
code: 429,
|
|
124
|
+
message: JSON.stringify(innerError),
|
|
125
|
+
details: [],
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
const mockError = {
|
|
129
|
+
response: {
|
|
130
|
+
status: 429,
|
|
131
|
+
data: {
|
|
132
|
+
error: {
|
|
133
|
+
code: 429,
|
|
134
|
+
message: JSON.stringify(middleError),
|
|
135
|
+
details: [],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
const parsed = parseGoogleApiError(mockError);
|
|
141
|
+
expect(parsed).not.toBeNull();
|
|
142
|
+
expect(parsed?.code).toBe(429);
|
|
143
|
+
expect(parsed?.message).toBe('Innermost quota message');
|
|
144
|
+
expect(parsed?.details).toHaveLength(1);
|
|
145
|
+
expect(parsed?.details[0]['@type']).toBe('type.googleapis.com/google.rpc.RetryInfo');
|
|
146
|
+
});
|
|
147
|
+
it('should parse an error that is not in a response object', () => {
|
|
148
|
+
const innerError = {
|
|
149
|
+
error: {
|
|
150
|
+
code: 429,
|
|
151
|
+
message: 'Innermost quota message',
|
|
152
|
+
details: [
|
|
153
|
+
{
|
|
154
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
155
|
+
retryDelay: '20s',
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
const mockError = {
|
|
161
|
+
error: {
|
|
162
|
+
code: 429,
|
|
163
|
+
message: JSON.stringify(innerError),
|
|
164
|
+
details: [],
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
const parsed = parseGoogleApiError(mockError);
|
|
168
|
+
expect(parsed).not.toBeNull();
|
|
169
|
+
expect(parsed?.code).toBe(429);
|
|
170
|
+
expect(parsed?.message).toBe('Innermost quota message');
|
|
171
|
+
expect(parsed?.details).toHaveLength(1);
|
|
172
|
+
expect(parsed?.details[0]['@type']).toBe('type.googleapis.com/google.rpc.RetryInfo');
|
|
173
|
+
});
|
|
174
|
+
it('should parse an error that is a JSON string', () => {
|
|
175
|
+
const innerError = {
|
|
176
|
+
error: {
|
|
177
|
+
code: 429,
|
|
178
|
+
message: 'Innermost quota message',
|
|
179
|
+
details: [
|
|
180
|
+
{
|
|
181
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
182
|
+
retryDelay: '20s',
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
const mockError = {
|
|
188
|
+
error: {
|
|
189
|
+
code: 429,
|
|
190
|
+
message: JSON.stringify(innerError),
|
|
191
|
+
details: [],
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
const parsed = parseGoogleApiError(JSON.stringify(mockError));
|
|
195
|
+
expect(parsed).not.toBeNull();
|
|
196
|
+
expect(parsed?.code).toBe(429);
|
|
197
|
+
expect(parsed?.message).toBe('Innermost quota message');
|
|
198
|
+
expect(parsed?.details).toHaveLength(1);
|
|
199
|
+
expect(parsed?.details[0]['@type']).toBe('type.googleapis.com/google.rpc.RetryInfo');
|
|
200
|
+
});
|
|
201
|
+
it('should parse the user-provided nested error string', () => {
|
|
202
|
+
const userErrorString = '{"error":{"message":"{\\n \\"error\\": {\\n \\"code\\": 429,\\n \\"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_paid_tier_input_token_count, limit: 10000\\\\nPlease retry in 40.025771073s.\\",\\n \\"status\\": \\"RESOURCE_EXHAUSTED\\",\\n \\"details\\": [\\n {\\n \\"@type\\": \\"type.googleapis.com/google.rpc.DebugInfo\\",\\n \\"detail\\": \\"[ORIGINAL ERROR] generic::resource_exhausted: 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_paid_tier_input_token_count, limit: 10000\\\\nPlease retry in 40.025771073s. [google.rpc.error_details_ext] { 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_paid_tier_input_token_count, limit: 10000\\\\\\\\nPlease retry in 40.025771073s.\\\\\\" }\\"\\n },\\n {\\n \\"@type\\": \\"type.googleapis.com/google.rpc.QuotaFailure\\",\\n \\"violations\\": [\\n {\\n \\"quotaMetric\\": \\"generativelanguage.googleapis.com/generate_content_paid_tier_input_token_count\\",\\n \\"quotaId\\": \\"GenerateContentPaidTierInputTokensPerModelPerMinute\\",\\n \\"quotaDimensions\\": {\\n \\"location\\": \\"global\\",\\n \\"model\\": \\"gemini-2.5-pro\\"\\n },\\n \\"quotaValue\\": \\"10000\\"\\n }\\n ]\\n },\\n {\\n \\"@type\\": \\"type.googleapis.com/google.rpc.Help\\",\\n \\"links\\": [\\n {\\n \\"description\\": \\"Learn more about Gemini API quotas\\",\\n \\"url\\": \\"https://ai.google.dev/gemini-api/docs/rate-limits\\"\\n }\\n ]\\n },\\n {\\n \\"@type\\": \\"type.googleapis.com/google.rpc.RetryInfo\\",\\n \\"retryDelay\\": \\"40s\\"\\n }\\n ]\\n }\\n}\\n","code":429,"status":"Too Many Requests"}}';
|
|
203
|
+
const parsed = parseGoogleApiError(userErrorString);
|
|
204
|
+
expect(parsed).not.toBeNull();
|
|
205
|
+
expect(parsed?.code).toBe(429);
|
|
206
|
+
expect(parsed?.message).toContain('You exceeded your current quota');
|
|
207
|
+
expect(parsed?.details).toHaveLength(4);
|
|
208
|
+
expect(parsed?.details.some((d) => d['@type'] === 'type.googleapis.com/google.rpc.QuotaFailure')).toBe(true);
|
|
209
|
+
expect(parsed?.details.some((d) => d['@type'] === 'type.googleapis.com/google.rpc.RetryInfo')).toBe(true);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
//# sourceMappingURL=googleErrors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"googleErrors.test.js","sourceRoot":"","sources":["../../../src/utils/googleErrors.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { GoogleApiError } from './googleErrors.js';
|
|
7
|
+
/**
|
|
8
|
+
* A non-retryable error indicating a hard quota limit has been reached (e.g., daily limit).
|
|
9
|
+
*/
|
|
10
|
+
export declare class TerminalQuotaError extends Error {
|
|
11
|
+
readonly cause: GoogleApiError;
|
|
12
|
+
constructor(message: string, cause: GoogleApiError);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A retryable error indicating a temporary quota issue (e.g., per-minute limit).
|
|
16
|
+
*/
|
|
17
|
+
export declare class RetryableQuotaError extends Error {
|
|
18
|
+
readonly cause: GoogleApiError;
|
|
19
|
+
retryDelayMs: number;
|
|
20
|
+
constructor(message: string, cause: GoogleApiError, retryDelaySeconds: number);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Analyzes a caught error and classifies it as a specific quota-related error if applicable.
|
|
24
|
+
*
|
|
25
|
+
* It decides whether an error is a `TerminalQuotaError` or a `RetryableQuotaError` based on
|
|
26
|
+
* the following logic:
|
|
27
|
+
* - If the error indicates a daily limit, it's a `TerminalQuotaError`.
|
|
28
|
+
* - If the error suggests a retry delay of more than 5 minutes, it's a `TerminalQuotaError`.
|
|
29
|
+
* - If the error suggests a retry delay of 5 minutes or less, it's a `RetryableQuotaError`.
|
|
30
|
+
* - If the error indicates a per-minute limit, it's a `RetryableQuotaError`.
|
|
31
|
+
*
|
|
32
|
+
* @param error The error to classify.
|
|
33
|
+
* @returns A `TerminalQuotaError`, `RetryableQuotaError`, or the original `unknown` error.
|
|
34
|
+
*/
|
|
35
|
+
export declare function classifyGoogleError(error: unknown): unknown;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { parseGoogleApiError } from './googleErrors.js';
|
|
7
|
+
const FIVE_MINUTES_IN_SECONDS = 5 * 60;
|
|
8
|
+
/**
|
|
9
|
+
* A non-retryable error indicating a hard quota limit has been reached (e.g., daily limit).
|
|
10
|
+
*/
|
|
11
|
+
export class TerminalQuotaError extends Error {
|
|
12
|
+
cause;
|
|
13
|
+
constructor(message, cause) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.cause = cause;
|
|
16
|
+
this.name = 'TerminalQuotaError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A retryable error indicating a temporary quota issue (e.g., per-minute limit).
|
|
21
|
+
*/
|
|
22
|
+
export class RetryableQuotaError extends Error {
|
|
23
|
+
cause;
|
|
24
|
+
retryDelayMs;
|
|
25
|
+
constructor(message, cause, retryDelaySeconds) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.cause = cause;
|
|
28
|
+
this.name = 'RetryableQuotaError';
|
|
29
|
+
this.retryDelayMs = retryDelaySeconds * 1000;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parses a duration string (e.g., "34.074824224s", "60s") and returns the time in seconds.
|
|
34
|
+
* @param duration The duration string to parse.
|
|
35
|
+
* @returns The duration in seconds, or null if parsing fails.
|
|
36
|
+
*/
|
|
37
|
+
function parseDurationInSeconds(duration) {
|
|
38
|
+
if (!duration.endsWith('s')) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const seconds = parseFloat(duration.slice(0, -1));
|
|
42
|
+
return isNaN(seconds) ? null : seconds;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Analyzes a caught error and classifies it as a specific quota-related error if applicable.
|
|
46
|
+
*
|
|
47
|
+
* It decides whether an error is a `TerminalQuotaError` or a `RetryableQuotaError` based on
|
|
48
|
+
* the following logic:
|
|
49
|
+
* - If the error indicates a daily limit, it's a `TerminalQuotaError`.
|
|
50
|
+
* - If the error suggests a retry delay of more than 5 minutes, it's a `TerminalQuotaError`.
|
|
51
|
+
* - If the error suggests a retry delay of 5 minutes or less, it's a `RetryableQuotaError`.
|
|
52
|
+
* - If the error indicates a per-minute limit, it's a `RetryableQuotaError`.
|
|
53
|
+
*
|
|
54
|
+
* @param error The error to classify.
|
|
55
|
+
* @returns A `TerminalQuotaError`, `RetryableQuotaError`, or the original `unknown` error.
|
|
56
|
+
*/
|
|
57
|
+
export function classifyGoogleError(error) {
|
|
58
|
+
const googleApiError = parseGoogleApiError(error);
|
|
59
|
+
if (!googleApiError || googleApiError.code !== 429) {
|
|
60
|
+
return error; // Not a 429 error we can handle.
|
|
61
|
+
}
|
|
62
|
+
const quotaFailure = googleApiError.details.find((d) => d['@type'] === 'type.googleapis.com/google.rpc.QuotaFailure');
|
|
63
|
+
const errorInfo = googleApiError.details.find((d) => d['@type'] === 'type.googleapis.com/google.rpc.ErrorInfo');
|
|
64
|
+
const retryInfo = googleApiError.details.find((d) => d['@type'] === 'type.googleapis.com/google.rpc.RetryInfo');
|
|
65
|
+
// 1. Check for long-term limits in QuotaFailure or ErrorInfo
|
|
66
|
+
if (quotaFailure) {
|
|
67
|
+
for (const violation of quotaFailure.violations) {
|
|
68
|
+
const quotaId = violation.quotaId ?? '';
|
|
69
|
+
if (quotaId.includes('PerDay') || quotaId.includes('Daily')) {
|
|
70
|
+
return new TerminalQuotaError(`Reached a daily quota limit: ${violation.description}`, googleApiError);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (errorInfo) {
|
|
75
|
+
const quotaLimit = errorInfo.metadata?.['quota_limit'] ?? '';
|
|
76
|
+
if (quotaLimit.includes('PerDay') || quotaLimit.includes('Daily')) {
|
|
77
|
+
return new TerminalQuotaError(`Reached a daily quota limit: ${errorInfo.reason}`, googleApiError);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// 2. Check for long delays in RetryInfo
|
|
81
|
+
if (retryInfo?.retryDelay) {
|
|
82
|
+
const delaySeconds = parseDurationInSeconds(retryInfo.retryDelay);
|
|
83
|
+
if (delaySeconds !== null) {
|
|
84
|
+
if (delaySeconds > FIVE_MINUTES_IN_SECONDS) {
|
|
85
|
+
return new TerminalQuotaError(`Quota limit requires a long delay of ${retryInfo.retryDelay}.`, googleApiError);
|
|
86
|
+
}
|
|
87
|
+
// This is a retryable error with a specific delay.
|
|
88
|
+
return new RetryableQuotaError(`Quota limit hit. Retrying after ${retryInfo.retryDelay}.`, googleApiError, delaySeconds);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// 3. Check for short-term limits in QuotaFailure or ErrorInfo
|
|
92
|
+
if (quotaFailure) {
|
|
93
|
+
for (const violation of quotaFailure.violations) {
|
|
94
|
+
const quotaId = violation.quotaId ?? '';
|
|
95
|
+
if (quotaId.includes('PerMinute')) {
|
|
96
|
+
return new RetryableQuotaError(`Quota limit hit: ${violation.description}. Retrying after 60s.`, googleApiError, 60);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (errorInfo) {
|
|
101
|
+
const quotaLimit = errorInfo.metadata?.['quota_limit'] ?? '';
|
|
102
|
+
if (quotaLimit.includes('PerMinute')) {
|
|
103
|
+
return new RetryableQuotaError(`Quota limit hit: ${errorInfo.reason}. Retrying after 60s.`, googleApiError, 60);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return error; // Fallback to original error if no specific classification fits.
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=googleQuotaErrors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"googleQuotaErrors.js","sourceRoot":"","sources":["../../../src/utils/googleQuotaErrors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,189 @@
|
|
|
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 prioritize daily limit over retry info', () => {
|
|
147
|
+
const apiError = {
|
|
148
|
+
code: 429,
|
|
149
|
+
message: 'Quota exceeded',
|
|
150
|
+
details: [
|
|
151
|
+
{
|
|
152
|
+
'@type': 'type.googleapis.com/google.rpc.QuotaFailure',
|
|
153
|
+
violations: [
|
|
154
|
+
{
|
|
155
|
+
subject: 'user',
|
|
156
|
+
description: 'daily limit',
|
|
157
|
+
quotaId: 'RequestsPerDay-limit',
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
'@type': 'type.googleapis.com/google.rpc.RetryInfo',
|
|
163
|
+
retryDelay: '10s',
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
168
|
+
const result = classifyGoogleError(new Error());
|
|
169
|
+
expect(result).toBeInstanceOf(TerminalQuotaError);
|
|
170
|
+
});
|
|
171
|
+
it('should return original error for 429 without specific details', () => {
|
|
172
|
+
const apiError = {
|
|
173
|
+
code: 429,
|
|
174
|
+
message: 'Too many requests',
|
|
175
|
+
details: [
|
|
176
|
+
{
|
|
177
|
+
'@type': 'type.googleapis.com/google.rpc.DebugInfo',
|
|
178
|
+
detail: 'some debug info',
|
|
179
|
+
stackEntries: [],
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
};
|
|
183
|
+
vi.spyOn(errorParser, 'parseGoogleApiError').mockReturnValue(apiError);
|
|
184
|
+
const originalError = new Error();
|
|
185
|
+
const result = classifyGoogleError(originalError);
|
|
186
|
+
expect(result).toBe(originalError);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
//# sourceMappingURL=googleQuotaErrors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"googleQuotaErrors.test.js","sourceRoot":"","sources":["../../../src/utils/googleQuotaErrors.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,WAAW,MAAM,mBAAmB,CAAC;AAGjD,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,eAAe,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,YAAY,GAAG,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACvD,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QACnE,MAAM,MAAM,GAAG,mBAAmB,CAAC,YAAY,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,QAAQ,GAAmB;YAC/B,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,cAAc;YACvB,OAAO,EAAE,EAAE;SACZ,CAAC;QACF,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,aAAa,GAAG,IAAI,KAAK,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,mBAAmB,CAAC,aAAa,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,GAAG,EAAE;QACrF,MAAM,QAAQ,GAAmB;YAC/B,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,gBAAgB;YACzB,OAAO,EAAE;gBACP;oBACE,OAAO,EAAE,6CAA6C;oBACtD,UAAU,EAAE;wBACV;4BACE,OAAO,EAAE,MAAM;4BACf,WAAW,EAAE,aAAa;4BAC1B,OAAO,EAAE,sBAAsB;yBAChC;qBACF;iBACF;aACF;SACF,CAAC;QACF,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;QAClD,MAAM,CAAE,MAA6B,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,QAAQ,GAAmB;YAC/B,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,gBAAgB;YACzB,OAAO,EAAE;gBACP;oBACE,OAAO,EAAE,0CAA0C;oBACnD,MAAM,EAAE,gBAAgB;oBACxB,MAAM,EAAE,gBAAgB;oBACxB,QAAQ,EAAE;wBACR,WAAW,EAAE,mCAAmC;qBACjD;iBACF;aACF;SACF,CAAC;QACF,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,QAAQ,GAAmB;YAC/B,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,mBAAmB;YAC5B,OAAO,EAAE;gBACP;oBACE,OAAO,EAAE,0CAA0C;oBACnD,UAAU,EAAE,MAAM,EAAE,cAAc;iBACnC;aACF;SACF,CAAC;QACF,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,QAAQ,GAAmB;YAC/B,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,mBAAmB;YAC5B,OAAO,EAAE;gBACP;oBACE,OAAO,EAAE,0CAA0C;oBACnD,UAAU,EAAE,SAAS;iBACtB;aACF;SACF,CAAC;QACF,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,CAAE,MAA8B,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mFAAmF,EAAE,GAAG,EAAE;QAC3F,MAAM,QAAQ,GAAmB;YAC/B,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,gBAAgB;YACzB,OAAO,EAAE;gBACP;oBACE,OAAO,EAAE,6CAA6C;oBACtD,UAAU,EAAE;wBACV;4BACE,OAAO,EAAE,MAAM;4BACf,WAAW,EAAE,kBAAkB;4BAC/B,OAAO,EAAE,yBAAyB;yBACnC;qBACF;iBACF;aACF;SACF,CAAC;QACF,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,CAAE,MAA8B,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,QAAQ,GAAmB;YAC/B,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,gBAAgB;YACzB,OAAO,EAAE;gBACP;oBACE,OAAO,EAAE,0CAA0C;oBACnD,MAAM,EAAE,gBAAgB;oBACxB,MAAM,EAAE,gBAAgB;oBACxB,QAAQ,EAAE;wBACR,WAAW,EAAE,sCAAsC;qBACpD;iBACF;aACF;SACF,CAAC;QACF,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,CAAE,MAA8B,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,QAAQ,GAAmB;YAC/B,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,gBAAgB;YACzB,OAAO,EAAE;gBACP;oBACE,OAAO,EAAE,6CAA6C;oBACtD,UAAU,EAAE;wBACV;4BACE,OAAO,EAAE,MAAM;4BACf,WAAW,EAAE,aAAa;4BAC1B,OAAO,EAAE,sBAAsB;yBAChC;qBACF;iBACF;gBACD;oBACE,OAAO,EAAE,0CAA0C;oBACnD,UAAU,EAAE,KAAK;iBAClB;aACF;SACF,CAAC;QACF,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,QAAQ,GAAmB;YAC/B,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,mBAAmB;YAC5B,OAAO,EAAE;gBACP;oBACE,OAAO,EAAE,0CAA0C;oBACnD,MAAM,EAAE,iBAAiB;oBACzB,YAAY,EAAE,EAAE;iBACjB;aACF;SACF,CAAC;QACF,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACvE,MAAM,aAAa,GAAG,IAAI,KAAK,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,mBAAmB,CAAC,aAAa,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
6
7
|
import { Type } from '@google/genai';
|
|
7
8
|
import {} from '../core/baseLlmClient.js';
|
|
8
9
|
import { LruCache } from './LruCache.js';
|
|
@@ -22,13 +23,13 @@ You will be given:
|
|
|
22
23
|
1. The high-level instruction for the original edit.
|
|
23
24
|
2. The exact \`search\` and \`replace\` strings that failed.
|
|
24
25
|
3. The error message that was produced.
|
|
25
|
-
4. The full content of the source file.
|
|
26
|
+
4. The full content of the latest version of the source file.
|
|
26
27
|
|
|
27
28
|
# Rules for Correction
|
|
28
29
|
1. **Minimal Correction:** Your new \`search\` string must be a close variation of the original. Focus on fixing issues like whitespace, indentation, line endings, or small contextual differences.
|
|
29
30
|
2. **Explain the Fix:** Your \`explanation\` MUST state exactly why the original \`search\` failed and how your new \`search\` string resolves that specific failure. (e.g., "The original search failed due to incorrect indentation; the new search corrects the indentation to match the source file.").
|
|
30
|
-
3. **Preserve the \`replace\` String:** Do NOT modify the \`replace\` string unless the instruction explicitly requires it and it was the source of the error. Your primary focus is fixing the \`search\` string.
|
|
31
|
-
4. **No Changes Case:** CRUCIAL: if the change is already present in the file, set \`noChangesRequired\` to True and explain why in the \`explanation\`. It is crucial that you only do this if the changes outline in \`replace\` are
|
|
31
|
+
3. **Preserve the \`replace\` String:** Do NOT modify the \`replace\` string unless the instruction explicitly requires it and it was the source of the error. Do not escape any characters in \`replace\`. Your primary focus is fixing the \`search\` string.
|
|
32
|
+
4. **No Changes Case:** CRUCIAL: if the change is already present in the file, set \`noChangesRequired\` to True and explain why in the \`explanation\`. It is crucial that you only do this if the changes outline in \`replace\` are already in the file and suits the instruction.
|
|
32
33
|
5. **Exactness:** The final \`search\` field must be the EXACT literal text from the file. Do not escape characters.
|
|
33
34
|
`;
|
|
34
35
|
const EDIT_USER_PROMPT = `
|
|
@@ -88,7 +89,15 @@ export async function FixLLMEditWithInstruction(instruction, old_string, new_str
|
|
|
88
89
|
promptId = `llm-fixer-fallback-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
89
90
|
console.warn(`Could not find promptId in context. This is unexpected. Using a fallback ID: ${promptId}`);
|
|
90
91
|
}
|
|
91
|
-
const cacheKey =
|
|
92
|
+
const cacheKey = createHash('sha256')
|
|
93
|
+
.update(JSON.stringify([
|
|
94
|
+
current_content,
|
|
95
|
+
old_string,
|
|
96
|
+
new_string,
|
|
97
|
+
instruction,
|
|
98
|
+
error,
|
|
99
|
+
]))
|
|
100
|
+
.digest('hex');
|
|
92
101
|
const cachedResult = editCorrectionWithInstructionCache.get(cacheKey);
|
|
93
102
|
if (cachedResult) {
|
|
94
103
|
return cachedResult;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-edit-fixer.js","sourceRoot":"","sources":["../../../src/utils/llm-edit-fixer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAgB,IAAI,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAsB,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,cAAc,GAAG,EAAE,CAAC;AAE1B,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;CAqBvB,CAAC;AAEF,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BxB,CAAC;AASF,MAAM,uBAAuB,GAAG;IAC9B,IAAI,EAAE,IAAI,CAAC,MAAM;IACjB,UAAU,EAAE;QACV,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE;QAClC,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE;QAC7B,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE;QAC9B,iBAAiB,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE;KAC1C;IACD,QAAQ,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,aAAa,CAAC;CAC/C,CAAC;AAEF,MAAM,kCAAkC,GAAG,IAAI,QAAQ,CAGrD,cAAc,CAAC,CAAC;AAElB;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,WAAmB,EACnB,UAAkB,EAClB,UAAkB,EAClB,KAAa,EACb,eAAuB,EACvB,aAA4B,EAC5B,WAAwB;IAExB,IAAI,QAAQ,GAAG,eAAe,CAAC,QAAQ,EAAE,CAAC;IAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,sBAAsB,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACrF,OAAO,CAAC,IAAI,CACV,gFAAgF,QAAQ,EAAE,CAC3F,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,
|
|
1
|
+
{"version":3,"file":"llm-edit-fixer.js","sourceRoot":"","sources":["../../../src/utils/llm-edit-fixer.ts"],"names":[],"mappings":"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"}
|