@azumag/opencode-rate-limit-fallback 1.58.0 → 1.63.0
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/index.js +27 -0
- package/dist/src/config/Validator.js +0 -8
- package/dist/src/config/defaults.d.ts +14 -3
- package/dist/src/config/defaults.js +15 -4
- package/dist/src/diagnostics/Reporter.d.ts +4 -1
- package/dist/src/diagnostics/Reporter.js +16 -0
- package/dist/src/errors/ConfidenceScorer.d.ts +37 -27
- package/dist/src/errors/ConfidenceScorer.js +80 -90
- package/dist/src/errors/PatternExtractor.d.ts +21 -14
- package/dist/src/errors/PatternExtractor.js +176 -114
- package/dist/src/errors/PatternLearner.d.ts +29 -60
- package/dist/src/errors/PatternLearner.js +139 -210
- package/dist/src/errors/PatternRegistry.d.ts +35 -58
- package/dist/src/errors/PatternRegistry.js +90 -192
- package/dist/src/errors/PatternStorage.d.ts +24 -25
- package/dist/src/errors/PatternStorage.js +133 -188
- package/dist/src/main/ConfigReloader.d.ts +6 -0
- package/dist/src/main/ConfigReloader.js +17 -0
- package/dist/src/types/index.d.ts +26 -26
- package/dist/src/utils/config.js +9 -4
- package/package.json +1 -1
|
@@ -1,157 +1,219 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pattern
|
|
2
|
+
* Pattern Extractor for extracting patterns from error messages
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* Pre-defined provider IDs for matching
|
|
6
6
|
*/
|
|
7
|
-
const
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'resource_exhausted',
|
|
17
|
-
'daily limit exceeded',
|
|
18
|
-
'monthly limit exceeded',
|
|
19
|
-
'maximum requests',
|
|
20
|
-
'requests per minute',
|
|
21
|
-
'requests per second',
|
|
22
|
-
'request limit',
|
|
23
|
-
'request_limit',
|
|
24
|
-
'limit exceeded',
|
|
25
|
-
'limit_exceeded',
|
|
7
|
+
const KNOWN_PROVIDERS = [
|
|
8
|
+
'anthropic',
|
|
9
|
+
'google',
|
|
10
|
+
'openai',
|
|
11
|
+
'cohere',
|
|
12
|
+
'mistral',
|
|
13
|
+
'together',
|
|
14
|
+
'deepseek',
|
|
15
|
+
'gemini',
|
|
26
16
|
];
|
|
27
17
|
/**
|
|
28
|
-
*
|
|
18
|
+
* Pre-defined HTTP status code regex patterns
|
|
29
19
|
*/
|
|
30
|
-
const
|
|
20
|
+
const STATUS_CODE_PATTERNS = [
|
|
21
|
+
/\b(429|503|502|500)\b/g, // Common rate limit and server error codes
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Pre-defined rate limit phrase patterns
|
|
25
|
+
*/
|
|
26
|
+
const RATE_LIMIT_PHRASE_PATTERNS = [
|
|
27
|
+
/(?:rate.?limit|quota|exceed|too.?many.?requests|throttl)/gi,
|
|
28
|
+
];
|
|
31
29
|
/**
|
|
32
|
-
*
|
|
30
|
+
* Pre-defined API error code patterns
|
|
33
31
|
*/
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
32
|
+
const ERROR_CODE_PATTERNS = [
|
|
33
|
+
/\b(?:insufficient_quota|resource_exhausted|rate_limit_error|quota_exceeded|over_quota)\b/gi,
|
|
34
|
+
];
|
|
38
35
|
/**
|
|
39
|
-
*
|
|
36
|
+
* Minimum length for pattern strings
|
|
37
|
+
*/
|
|
38
|
+
const MIN_PATTERN_LENGTH = 3;
|
|
39
|
+
/**
|
|
40
|
+
* Pattern Extractor class
|
|
41
|
+
* Extracts pattern candidates from error messages
|
|
40
42
|
*/
|
|
41
43
|
export class PatternExtractor {
|
|
42
44
|
/**
|
|
43
|
-
*
|
|
45
|
+
* Check if an object is a valid error object
|
|
46
|
+
*/
|
|
47
|
+
isValidErrorObject(error) {
|
|
48
|
+
if (!error || typeof error !== 'object') {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Extract error text from various error fields
|
|
44
55
|
*/
|
|
45
|
-
|
|
56
|
+
extractErrorText(error) {
|
|
46
57
|
if (!this.isValidErrorObject(error)) {
|
|
47
58
|
return [];
|
|
48
59
|
}
|
|
49
60
|
const err = error;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (extractedStatusCode) {
|
|
63
|
-
// Use pre-defined regex pattern instead of constructing one
|
|
64
|
-
if (extractedStatusCode in RATE_LIMIT_STATUS_REGEX) {
|
|
65
|
-
patterns.push(RATE_LIMIT_STATUS_REGEX[extractedStatusCode]);
|
|
61
|
+
const textSources = [];
|
|
62
|
+
// Extract from response body
|
|
63
|
+
if (err.data && typeof err.data === 'object') {
|
|
64
|
+
const data = err.data;
|
|
65
|
+
if (typeof data.responseBody === 'string') {
|
|
66
|
+
textSources.push(data.responseBody);
|
|
67
|
+
}
|
|
68
|
+
if (typeof data.message === 'string') {
|
|
69
|
+
textSources.push(data.message);
|
|
70
|
+
}
|
|
71
|
+
if (typeof data.statusCode === 'number') {
|
|
72
|
+
textSources.push(String(data.statusCode));
|
|
66
73
|
}
|
|
67
|
-
patterns.push(String(extractedStatusCode));
|
|
68
74
|
}
|
|
69
|
-
// Extract
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const extractedPhrases = this.extractPhrases(error);
|
|
73
|
-
patterns.push(...extractedPhrases);
|
|
74
|
-
// Extract error codes
|
|
75
|
-
if (errorCode) {
|
|
76
|
-
patterns.push(errorCode.toLowerCase());
|
|
75
|
+
// Extract from error properties
|
|
76
|
+
if (typeof err.message === 'string') {
|
|
77
|
+
textSources.push(err.message);
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (validPatterns.length === 0) {
|
|
81
|
-
return [];
|
|
79
|
+
if (typeof err.name === 'string') {
|
|
80
|
+
textSources.push(err.name);
|
|
82
81
|
}
|
|
83
|
-
return
|
|
84
|
-
provider: provider || undefined,
|
|
85
|
-
patterns: validPatterns,
|
|
86
|
-
sourceError: originalText,
|
|
87
|
-
extractedAt: Date.now(),
|
|
88
|
-
}];
|
|
82
|
+
return textSources;
|
|
89
83
|
}
|
|
90
84
|
/**
|
|
91
|
-
* Extract provider ID from error
|
|
85
|
+
* Extract provider ID from error text
|
|
92
86
|
*/
|
|
93
|
-
extractProvider(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
String(err.message || ''),
|
|
101
|
-
String(err.data?.message || ''),
|
|
102
|
-
String(err.data?.responseBody || ''),
|
|
103
|
-
].join(' ').toLowerCase();
|
|
104
|
-
// Check for known provider names
|
|
105
|
-
for (const provider of KNOWN_PROVIDERS) {
|
|
106
|
-
if (allText.includes(provider)) {
|
|
107
|
-
return provider;
|
|
87
|
+
extractProvider(textSources) {
|
|
88
|
+
for (const text of textSources) {
|
|
89
|
+
const lowerText = text.toLowerCase();
|
|
90
|
+
for (const provider of KNOWN_PROVIDERS) {
|
|
91
|
+
if (lowerText.includes(provider)) {
|
|
92
|
+
return provider;
|
|
93
|
+
}
|
|
108
94
|
}
|
|
109
95
|
}
|
|
110
96
|
return null;
|
|
111
97
|
}
|
|
112
98
|
/**
|
|
113
|
-
* Extract HTTP status
|
|
99
|
+
* Extract HTTP status codes from error text
|
|
114
100
|
*/
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
101
|
+
extractStatusCodes(textSources) {
|
|
102
|
+
const statusCodes = new Set();
|
|
103
|
+
for (const text of textSources) {
|
|
104
|
+
for (const pattern of STATUS_CODE_PATTERNS) {
|
|
105
|
+
pattern.lastIndex = 0; // Reset regex state
|
|
106
|
+
const matches = text.matchAll(pattern);
|
|
107
|
+
for (const match of matches) {
|
|
108
|
+
if (match[1]) {
|
|
109
|
+
statusCodes.add(match[1]);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
123
113
|
}
|
|
124
|
-
return
|
|
114
|
+
return Array.from(statusCodes);
|
|
125
115
|
}
|
|
126
116
|
/**
|
|
127
|
-
* Extract
|
|
117
|
+
* Extract rate limit phrases from error text
|
|
128
118
|
*/
|
|
129
|
-
extractPhrases(
|
|
130
|
-
|
|
131
|
-
|
|
119
|
+
extractPhrases(textSources) {
|
|
120
|
+
const phrases = new Set();
|
|
121
|
+
const lowerTextSources = textSources.map(t => t.toLowerCase());
|
|
122
|
+
// Common rate limit phrases to look for
|
|
123
|
+
const commonPhrases = [
|
|
124
|
+
'rate limit',
|
|
125
|
+
'rate_limit',
|
|
126
|
+
'ratelimit',
|
|
127
|
+
'too many requests',
|
|
128
|
+
'quota exceeded',
|
|
129
|
+
'rate limit exceeded',
|
|
130
|
+
'quota limit',
|
|
131
|
+
'insufficient quota',
|
|
132
|
+
'rate limited',
|
|
133
|
+
'rate-limited',
|
|
134
|
+
'throttled',
|
|
135
|
+
'resource exhausted',
|
|
136
|
+
'daily limit',
|
|
137
|
+
'request limit',
|
|
138
|
+
];
|
|
139
|
+
for (const text of lowerTextSources) {
|
|
140
|
+
// Extract common phrases
|
|
141
|
+
for (const phrase of commonPhrases) {
|
|
142
|
+
if (text.includes(phrase)) {
|
|
143
|
+
phrases.add(phrase);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Extract phrases using pre-defined patterns (for variations)
|
|
147
|
+
for (const pattern of RATE_LIMIT_PHRASE_PATTERNS) {
|
|
148
|
+
pattern.lastIndex = 0;
|
|
149
|
+
const matches = text.matchAll(pattern);
|
|
150
|
+
for (const match of matches) {
|
|
151
|
+
const phrase = match[0].toLowerCase().replace(/\s+/g, ' ').trim();
|
|
152
|
+
if (phrase.length >= MIN_PATTERN_LENGTH) {
|
|
153
|
+
phrases.add(phrase);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
132
157
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
158
|
+
// Extract error codes (these go in errorCodes, not phrases)
|
|
159
|
+
// But we also add them to phrases for now
|
|
160
|
+
for (const text of lowerTextSources) {
|
|
161
|
+
for (const pattern of ERROR_CODE_PATTERNS) {
|
|
162
|
+
pattern.lastIndex = 0;
|
|
163
|
+
const matches = text.matchAll(pattern);
|
|
164
|
+
for (const match of matches) {
|
|
165
|
+
const errorCode = match[0].toLowerCase();
|
|
166
|
+
if (errorCode.length >= MIN_PATTERN_LENGTH) {
|
|
167
|
+
phrases.add(errorCode);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
145
170
|
}
|
|
146
171
|
}
|
|
147
|
-
return
|
|
172
|
+
return Array.from(phrases);
|
|
148
173
|
}
|
|
149
174
|
/**
|
|
150
|
-
*
|
|
151
|
-
* @param error - The error to validate
|
|
152
|
-
* @returns True if error is a valid object
|
|
175
|
+
* Extract API error codes from error text
|
|
153
176
|
*/
|
|
154
|
-
|
|
155
|
-
|
|
177
|
+
extractErrorCodes(textSources) {
|
|
178
|
+
const errorCodes = new Set();
|
|
179
|
+
for (const text of textSources) {
|
|
180
|
+
for (const pattern of ERROR_CODE_PATTERNS) {
|
|
181
|
+
pattern.lastIndex = 0;
|
|
182
|
+
const matches = text.matchAll(pattern);
|
|
183
|
+
for (const match of matches) {
|
|
184
|
+
const code = match[0].toLowerCase();
|
|
185
|
+
errorCodes.add(code);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return Array.from(errorCodes);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Extract pattern candidates from an error
|
|
193
|
+
*/
|
|
194
|
+
extractPattern(error) {
|
|
195
|
+
if (!this.isValidErrorObject(error)) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
const textSources = this.extractErrorText(error);
|
|
199
|
+
if (textSources.length === 0) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const provider = this.extractProvider(textSources);
|
|
203
|
+
const statusCodes = this.extractStatusCodes(textSources);
|
|
204
|
+
const phrases = this.extractPhrases(textSources);
|
|
205
|
+
const errorCodes = this.extractErrorCodes(textSources);
|
|
206
|
+
const rawText = textSources.join(' ').toLowerCase();
|
|
207
|
+
// If no patterns were extracted, return null
|
|
208
|
+
if (phrases.length === 0 && errorCodes.length === 0 && statusCodes.length === 0) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
provider,
|
|
213
|
+
statusCode: statusCodes[0] || null,
|
|
214
|
+
phrases,
|
|
215
|
+
errorCodes,
|
|
216
|
+
rawText,
|
|
217
|
+
};
|
|
156
218
|
}
|
|
157
219
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pattern
|
|
2
|
+
* Pattern Learner for orchestrating error pattern learning
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
4
|
+
import type { LearnedPattern, PatternLearningConfig } from '../types/index.js';
|
|
5
5
|
import type { Logger } from '../../logger.js';
|
|
6
|
-
import { PatternExtractor } from './PatternExtractor.js';
|
|
7
|
-
import { ConfidenceScorer } from './ConfidenceScorer.js';
|
|
8
|
-
import { PatternStorage } from './PatternStorage.js';
|
|
9
6
|
/**
|
|
10
|
-
*
|
|
7
|
+
* Pattern Learner class
|
|
8
|
+
* Orchestrates the learning process
|
|
11
9
|
*/
|
|
12
10
|
export declare class PatternLearner {
|
|
13
11
|
private extractor;
|
|
@@ -15,83 +13,54 @@ export declare class PatternLearner {
|
|
|
15
13
|
private storage;
|
|
16
14
|
private config;
|
|
17
15
|
private logger;
|
|
18
|
-
private
|
|
19
|
-
private
|
|
20
|
-
constructor(extractor: PatternExtractor, scorer: ConfidenceScorer, storage: PatternStorage, config: LearningConfig, logger: Logger);
|
|
16
|
+
private patternTracking;
|
|
17
|
+
private stats;
|
|
21
18
|
/**
|
|
22
|
-
*
|
|
19
|
+
* Constructor
|
|
23
20
|
*/
|
|
24
|
-
|
|
21
|
+
constructor(config: PatternLearningConfig, logger?: Logger);
|
|
25
22
|
/**
|
|
26
|
-
*
|
|
23
|
+
* Update configuration
|
|
27
24
|
*/
|
|
28
|
-
|
|
25
|
+
updateConfig(config: PatternLearningConfig): void;
|
|
29
26
|
/**
|
|
30
|
-
*
|
|
27
|
+
* Set the config file path for storage
|
|
31
28
|
*/
|
|
32
|
-
|
|
29
|
+
setConfigFilePath(path: string): void;
|
|
33
30
|
/**
|
|
34
|
-
*
|
|
31
|
+
* Process an error and learn from it
|
|
35
32
|
*/
|
|
36
|
-
|
|
33
|
+
processError(error: unknown): Promise<LearnedPattern | null>;
|
|
37
34
|
/**
|
|
38
35
|
* Load learned patterns from storage
|
|
39
36
|
*/
|
|
40
|
-
loadLearnedPatterns(): Promise<
|
|
37
|
+
loadLearnedPatterns(): Promise<LearnedPattern[]>;
|
|
41
38
|
/**
|
|
42
|
-
*
|
|
39
|
+
* Save learned patterns
|
|
43
40
|
*/
|
|
44
|
-
|
|
41
|
+
saveLearnedPatterns(patterns: LearnedPattern[]): Promise<void>;
|
|
45
42
|
/**
|
|
46
|
-
* Get
|
|
43
|
+
* Get statistics
|
|
47
44
|
*/
|
|
48
|
-
|
|
45
|
+
getStats(): typeof PatternLearner.prototype.stats;
|
|
49
46
|
/**
|
|
50
|
-
*
|
|
47
|
+
* Reset statistics
|
|
51
48
|
*/
|
|
52
|
-
|
|
49
|
+
resetStats(): void;
|
|
53
50
|
/**
|
|
54
|
-
*
|
|
51
|
+
* Clear all pattern tracking
|
|
55
52
|
*/
|
|
56
|
-
|
|
53
|
+
clearTracking(): void;
|
|
57
54
|
/**
|
|
58
|
-
*
|
|
55
|
+
* Create a pattern key for tracking
|
|
59
56
|
*/
|
|
60
|
-
|
|
57
|
+
private createPatternKey;
|
|
61
58
|
/**
|
|
62
|
-
*
|
|
59
|
+
* Get or create pattern tracking
|
|
63
60
|
*/
|
|
64
|
-
private
|
|
61
|
+
private getOrCreateTracking;
|
|
65
62
|
/**
|
|
66
|
-
*
|
|
63
|
+
* Save a single learned pattern
|
|
67
64
|
*/
|
|
68
|
-
private
|
|
69
|
-
/**
|
|
70
|
-
* Generate a name for a pattern
|
|
71
|
-
*/
|
|
72
|
-
private generatePatternName;
|
|
73
|
-
/**
|
|
74
|
-
* Calculate priority for a learned pattern
|
|
75
|
-
*/
|
|
76
|
-
private calculatePriority;
|
|
77
|
-
/**
|
|
78
|
-
* Merge duplicate patterns in storage
|
|
79
|
-
*/
|
|
80
|
-
mergeDuplicatePatterns(): Promise<number>;
|
|
81
|
-
/**
|
|
82
|
-
* Cleanup old patterns
|
|
83
|
-
*/
|
|
84
|
-
cleanupOldPatterns(): Promise<number>;
|
|
85
|
-
/**
|
|
86
|
-
* Clear all tracked patterns
|
|
87
|
-
*/
|
|
88
|
-
clearTrackedPatterns(): void;
|
|
89
|
-
/**
|
|
90
|
-
* Get statistics about learning
|
|
91
|
-
*/
|
|
92
|
-
getStats(): {
|
|
93
|
-
trackedPatterns: number;
|
|
94
|
-
learnedPatterns: number;
|
|
95
|
-
pendingPatterns: number;
|
|
96
|
-
};
|
|
65
|
+
private saveLearnedPattern;
|
|
97
66
|
}
|