@azumag/opencode-rate-limit-fallback 1.50.0 → 1.57.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/README.md +219 -36
- package/dist/src/config/Validator.js +94 -0
- package/dist/src/config/defaults.d.ts +22 -0
- package/dist/src/config/defaults.js +28 -0
- package/dist/src/dynamic/DynamicPrioritizer.d.ts +74 -0
- package/dist/src/dynamic/DynamicPrioritizer.js +225 -0
- package/dist/src/errors/ConfidenceScorer.d.ts +45 -0
- package/dist/src/errors/ConfidenceScorer.js +120 -0
- package/dist/src/errors/PatternExtractor.d.ts +31 -0
- package/dist/src/errors/PatternExtractor.js +157 -0
- package/dist/src/errors/PatternLearner.d.ts +97 -0
- package/dist/src/errors/PatternLearner.js +257 -0
- package/dist/src/errors/PatternRegistry.d.ts +54 -5
- package/dist/src/errors/PatternRegistry.js +171 -8
- package/dist/src/errors/PatternStorage.d.ts +49 -0
- package/dist/src/errors/PatternStorage.js +234 -0
- package/dist/src/fallback/FallbackHandler.d.ts +3 -2
- package/dist/src/fallback/FallbackHandler.js +38 -5
- package/dist/src/fallback/ModelSelector.d.ts +7 -1
- package/dist/src/fallback/ModelSelector.js +14 -1
- package/dist/src/metrics/MetricsManager.d.ts +8 -0
- package/dist/src/metrics/MetricsManager.js +45 -0
- package/dist/src/types/index.d.ts +55 -0
- package/dist/src/utils/config.js +11 -1
- package/dist/src/utils/similarity.d.ts +10 -0
- package/dist/src/utils/similarity.js +24 -0
- package/package.json +1 -1
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Learning from Rate Limit Errors
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* PatternLearner - Orchestrates the learning process from errors
|
|
6
|
+
*/
|
|
7
|
+
export class PatternLearner {
|
|
8
|
+
extractor;
|
|
9
|
+
scorer;
|
|
10
|
+
storage;
|
|
11
|
+
config;
|
|
12
|
+
logger;
|
|
13
|
+
patternTracker = new Map();
|
|
14
|
+
learnedPatterns = new Map();
|
|
15
|
+
constructor(extractor, scorer, storage, config, logger) {
|
|
16
|
+
this.extractor = extractor;
|
|
17
|
+
this.scorer = scorer;
|
|
18
|
+
this.storage = storage;
|
|
19
|
+
this.config = config;
|
|
20
|
+
this.logger = logger;
|
|
21
|
+
// Note: Patterns will be loaded asynchronously via loadLearnedPatterns()
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Learn from a rate limit error
|
|
25
|
+
*/
|
|
26
|
+
learnFromError(error) {
|
|
27
|
+
if (!this.config.enabled) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
// Extract pattern candidates
|
|
32
|
+
const candidates = this.extractor.extractPatterns(error);
|
|
33
|
+
for (const candidate of candidates) {
|
|
34
|
+
this.trackPattern(candidate);
|
|
35
|
+
}
|
|
36
|
+
// Process and potentially save patterns
|
|
37
|
+
this.processPatterns();
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
this.logger.error('[PatternLearner] Failed to learn from error', {
|
|
41
|
+
error: error instanceof Error ? error.message : String(error),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Track a pattern candidate for learning
|
|
47
|
+
*/
|
|
48
|
+
trackPattern(candidate) {
|
|
49
|
+
// Generate a key for this pattern
|
|
50
|
+
const key = this.generatePatternKey(candidate);
|
|
51
|
+
const existing = this.patternTracker.get(key);
|
|
52
|
+
if (existing) {
|
|
53
|
+
// Update existing tracker
|
|
54
|
+
existing.lastSeen = Date.now();
|
|
55
|
+
existing.count++;
|
|
56
|
+
existing.samples.push(candidate.sourceError);
|
|
57
|
+
// Keep only last 10 samples
|
|
58
|
+
if (existing.samples.length > 10) {
|
|
59
|
+
existing.samples.shift();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// Create new tracker
|
|
64
|
+
this.patternTracker.set(key, {
|
|
65
|
+
pattern: candidate,
|
|
66
|
+
firstSeen: Date.now(),
|
|
67
|
+
lastSeen: Date.now(),
|
|
68
|
+
count: 1,
|
|
69
|
+
samples: [candidate.sourceError],
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Process tracked patterns and save those meeting criteria
|
|
75
|
+
*/
|
|
76
|
+
async processPatterns() {
|
|
77
|
+
for (const [key, tracker] of this.patternTracker.entries()) {
|
|
78
|
+
// Check if pattern meets frequency threshold
|
|
79
|
+
if (tracker.count < this.config.minErrorFrequency) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
// Check if pattern already exists in learned patterns
|
|
83
|
+
if (this.learnedPatterns.has(key)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
// Calculate confidence score
|
|
87
|
+
const confidence = this.scorer.calculateScore(tracker.pattern, tracker.count, tracker.firstSeen);
|
|
88
|
+
// Check if pattern meets auto-approve threshold
|
|
89
|
+
if (!this.scorer.shouldAutoApprove(confidence)) {
|
|
90
|
+
this.logger.debug(`[PatternLearner] Pattern confidence ${confidence.toFixed(2)} below threshold ${this.config.autoApproveThreshold}, not auto-approving`);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
// Create learned pattern
|
|
94
|
+
const learnedPattern = {
|
|
95
|
+
name: this.generatePatternName(tracker.pattern),
|
|
96
|
+
provider: tracker.pattern.provider,
|
|
97
|
+
patterns: tracker.pattern.patterns,
|
|
98
|
+
priority: this.calculatePriority(tracker.pattern, confidence),
|
|
99
|
+
confidence,
|
|
100
|
+
learnedAt: new Date(tracker.firstSeen).toISOString(),
|
|
101
|
+
sampleCount: tracker.count,
|
|
102
|
+
lastUsed: undefined,
|
|
103
|
+
};
|
|
104
|
+
// Save to storage
|
|
105
|
+
await this.storage.savePattern(learnedPattern);
|
|
106
|
+
// Add to learned patterns
|
|
107
|
+
this.learnedPatterns.set(key, learnedPattern);
|
|
108
|
+
// Remove from tracker
|
|
109
|
+
this.patternTracker.delete(key);
|
|
110
|
+
this.logger.info(`[PatternLearner] Learned new pattern: ${learnedPattern.name} (confidence: ${confidence.toFixed(2)})`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Merge similar patterns
|
|
115
|
+
*/
|
|
116
|
+
mergePatterns(patterns) {
|
|
117
|
+
if (patterns.length === 0) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
if (patterns.length === 1) {
|
|
121
|
+
return patterns[0];
|
|
122
|
+
}
|
|
123
|
+
// Merge all patterns into one
|
|
124
|
+
const mergedPattern = {
|
|
125
|
+
provider: patterns[0].provider,
|
|
126
|
+
patterns: patterns.flatMap(p => p.patterns),
|
|
127
|
+
sourceError: patterns.map(p => p.sourceError).join('; '),
|
|
128
|
+
extractedAt: Math.max(...patterns.map(p => p.extractedAt)),
|
|
129
|
+
};
|
|
130
|
+
// Deduplicate patterns
|
|
131
|
+
mergedPattern.patterns = [...new Set(mergedPattern.patterns)];
|
|
132
|
+
return mergedPattern;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Load learned patterns from storage
|
|
136
|
+
*/
|
|
137
|
+
async loadLearnedPatterns() {
|
|
138
|
+
try {
|
|
139
|
+
const patterns = await this.storage.loadPatterns();
|
|
140
|
+
for (const pattern of patterns) {
|
|
141
|
+
const key = this.generatePatternKeyFromLearned(pattern);
|
|
142
|
+
this.learnedPatterns.set(key, pattern);
|
|
143
|
+
}
|
|
144
|
+
this.logger.info(`[PatternLearner] Loaded ${patterns.length} learned patterns`);
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
this.logger.error('[PatternLearner] Failed to load learned patterns', {
|
|
148
|
+
error: error instanceof Error ? error.message : String(error),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get all learned patterns
|
|
154
|
+
*/
|
|
155
|
+
getLearnedPatterns() {
|
|
156
|
+
return Array.from(this.learnedPatterns.values());
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get learned patterns for a specific provider
|
|
160
|
+
*/
|
|
161
|
+
getLearnedPatternsForProvider(provider) {
|
|
162
|
+
return this.getLearnedPatterns().filter(p => !p.provider || p.provider === provider);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Add a learned pattern manually
|
|
166
|
+
*/
|
|
167
|
+
async addLearnedPattern(pattern) {
|
|
168
|
+
const key = this.generatePatternKeyFromLearned(pattern);
|
|
169
|
+
this.learnedPatterns.set(key, pattern);
|
|
170
|
+
await this.storage.savePattern(pattern);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Remove a learned pattern
|
|
174
|
+
*/
|
|
175
|
+
async removeLearnedPattern(name) {
|
|
176
|
+
const pattern = this.getLearnedPatternByName(name);
|
|
177
|
+
if (pattern) {
|
|
178
|
+
const key = this.generatePatternKeyFromLearned(pattern);
|
|
179
|
+
this.learnedPatterns.delete(key);
|
|
180
|
+
await this.storage.deletePattern(name);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Get a learned pattern by name
|
|
187
|
+
*/
|
|
188
|
+
getLearnedPatternByName(name) {
|
|
189
|
+
return this.getLearnedPatterns().find(p => p.name === name);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Generate a unique key for a pattern candidate
|
|
193
|
+
*/
|
|
194
|
+
generatePatternKey(pattern) {
|
|
195
|
+
const provider = pattern.provider || 'generic';
|
|
196
|
+
const patternStr = pattern.patterns.map(p => typeof p === 'string' ? p : p.source).join('|');
|
|
197
|
+
return `${provider}:${patternStr}`;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Generate a unique key for a learned pattern
|
|
201
|
+
*/
|
|
202
|
+
generatePatternKeyFromLearned(pattern) {
|
|
203
|
+
const provider = pattern.provider || 'generic';
|
|
204
|
+
const patternStr = pattern.patterns.map(p => typeof p === 'string' ? p : p.source).join('|');
|
|
205
|
+
return `${provider}:${patternStr}`;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Generate a name for a pattern
|
|
209
|
+
*/
|
|
210
|
+
generatePatternName(pattern) {
|
|
211
|
+
const provider = pattern.provider || 'generic';
|
|
212
|
+
const timestamp = Date.now();
|
|
213
|
+
return `learned-${provider}-${timestamp}`;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Calculate priority for a learned pattern
|
|
217
|
+
*/
|
|
218
|
+
calculatePriority(pattern, confidence) {
|
|
219
|
+
// Base priority on confidence
|
|
220
|
+
const basePriority = Math.floor(confidence * 50);
|
|
221
|
+
// Add bonus for provider-specific patterns
|
|
222
|
+
const providerBonus = pattern.provider ? 10 : 0;
|
|
223
|
+
// Add bonus for number of patterns
|
|
224
|
+
const patternCountBonus = Math.min(pattern.patterns.length * 2, 10);
|
|
225
|
+
return Math.max(1, Math.min(100, basePriority + providerBonus + patternCountBonus));
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Merge duplicate patterns in storage
|
|
229
|
+
*/
|
|
230
|
+
async mergeDuplicatePatterns() {
|
|
231
|
+
return this.storage.mergeDuplicatePatterns();
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Cleanup old patterns
|
|
235
|
+
*/
|
|
236
|
+
async cleanupOldPatterns() {
|
|
237
|
+
const maxPatterns = this.config.maxLearnedPatterns;
|
|
238
|
+
return this.storage.cleanupOldPatterns(maxPatterns);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Clear all tracked patterns
|
|
242
|
+
*/
|
|
243
|
+
clearTrackedPatterns() {
|
|
244
|
+
this.patternTracker.clear();
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get statistics about learning
|
|
248
|
+
*/
|
|
249
|
+
getStats() {
|
|
250
|
+
return {
|
|
251
|
+
trackedPatterns: this.patternTracker.size,
|
|
252
|
+
learnedPatterns: this.learnedPatterns.size,
|
|
253
|
+
pendingPatterns: Array.from(this.patternTracker.values())
|
|
254
|
+
.filter(t => t.count >= this.config.minErrorFrequency).length,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Error Pattern Registry for rate limit error detection
|
|
3
3
|
*/
|
|
4
|
-
import type { ErrorPattern } from '../types/index.js';
|
|
4
|
+
import type { ErrorPattern, LearningConfig, LearnedPattern } from '../types/index.js';
|
|
5
5
|
import { Logger } from '../../logger.js';
|
|
6
6
|
/**
|
|
7
7
|
* Error Pattern Registry class
|
|
@@ -9,8 +9,30 @@ import { Logger } from '../../logger.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export declare class ErrorPatternRegistry {
|
|
11
11
|
private patterns;
|
|
12
|
+
private learnedPatterns;
|
|
13
|
+
private patternLearner?;
|
|
12
14
|
private logger;
|
|
13
|
-
|
|
15
|
+
private configPath?;
|
|
16
|
+
constructor(logger?: Logger, config?: {
|
|
17
|
+
learningConfig?: LearningConfig;
|
|
18
|
+
configPath?: string;
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* Initialize pattern learning
|
|
22
|
+
*/
|
|
23
|
+
private initializeLearning;
|
|
24
|
+
/**
|
|
25
|
+
* Load learned patterns from storage (async)
|
|
26
|
+
*/
|
|
27
|
+
loadLearnedPatternsAsync(): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Load learned patterns from storage (synchronous - kept for backward compatibility)
|
|
30
|
+
*/
|
|
31
|
+
loadLearnedPatterns(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Reload learned patterns (for config hot reload)
|
|
34
|
+
*/
|
|
35
|
+
reloadLearnedPatterns(): Promise<void>;
|
|
14
36
|
/**
|
|
15
37
|
* Register default rate limit error patterns
|
|
16
38
|
*/
|
|
@@ -56,10 +78,37 @@ export declare class ErrorPatternRegistry {
|
|
|
56
78
|
*/
|
|
57
79
|
resetToDefaults(): void;
|
|
58
80
|
/**
|
|
59
|
-
* Learn a new pattern from an error
|
|
60
|
-
|
|
81
|
+
* Learn a new pattern from an error
|
|
82
|
+
*/
|
|
83
|
+
addLearnedPattern(error: unknown): void;
|
|
84
|
+
/**
|
|
85
|
+
* Get all learned patterns
|
|
86
|
+
*/
|
|
87
|
+
getLearnedPatterns(): LearnedPattern[];
|
|
88
|
+
/**
|
|
89
|
+
* Get a learned pattern by name
|
|
90
|
+
*/
|
|
91
|
+
getLearnedPatternByName(name: string): LearnedPattern | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* Remove a learned pattern by name
|
|
94
|
+
*/
|
|
95
|
+
removeLearnedPattern(name: string): Promise<boolean>;
|
|
96
|
+
/**
|
|
97
|
+
* Merge duplicate learned patterns
|
|
98
|
+
*/
|
|
99
|
+
mergeDuplicatePatterns(): Promise<number>;
|
|
100
|
+
/**
|
|
101
|
+
* Cleanup old learned patterns
|
|
102
|
+
*/
|
|
103
|
+
cleanupOldPatterns(): Promise<number>;
|
|
104
|
+
/**
|
|
105
|
+
* Get learning statistics
|
|
61
106
|
*/
|
|
62
|
-
|
|
107
|
+
getLearningStats(): {
|
|
108
|
+
trackedPatterns: number;
|
|
109
|
+
learnedPatterns: number;
|
|
110
|
+
pendingPatterns: number;
|
|
111
|
+
} | null;
|
|
63
112
|
/**
|
|
64
113
|
* Get statistics about registered patterns
|
|
65
114
|
*/
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Error Pattern Registry for rate limit error detection
|
|
3
3
|
*/
|
|
4
|
+
import { PatternExtractor } from './PatternExtractor.js';
|
|
5
|
+
import { ConfidenceScorer } from './ConfidenceScorer.js';
|
|
6
|
+
import { PatternStorage } from './PatternStorage.js';
|
|
7
|
+
import { PatternLearner } from './PatternLearner.js';
|
|
4
8
|
/**
|
|
5
9
|
* Error Pattern Registry class
|
|
6
10
|
* Manages and matches error patterns for rate limit detection
|
|
7
11
|
*/
|
|
8
12
|
export class ErrorPatternRegistry {
|
|
9
13
|
patterns = [];
|
|
14
|
+
learnedPatterns = new Map();
|
|
15
|
+
patternLearner;
|
|
10
16
|
logger;
|
|
11
|
-
|
|
17
|
+
configPath;
|
|
18
|
+
constructor(logger, config) {
|
|
12
19
|
// Initialize logger
|
|
13
20
|
this.logger = logger || {
|
|
14
21
|
debug: () => { },
|
|
@@ -16,7 +23,89 @@ export class ErrorPatternRegistry {
|
|
|
16
23
|
warn: () => { },
|
|
17
24
|
error: () => { },
|
|
18
25
|
};
|
|
26
|
+
this.configPath = config?.configPath;
|
|
19
27
|
this.registerDefaultPatterns();
|
|
28
|
+
// Initialize pattern learning if enabled
|
|
29
|
+
if (config?.learningConfig && config.learningConfig.enabled) {
|
|
30
|
+
this.initializeLearning(config.learningConfig);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Initialize pattern learning
|
|
35
|
+
*/
|
|
36
|
+
initializeLearning(learningConfig) {
|
|
37
|
+
if (!this.configPath) {
|
|
38
|
+
this.logger.warn('[ErrorPatternRegistry] Config path not provided, pattern learning disabled');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const extractor = new PatternExtractor();
|
|
43
|
+
const scorer = new ConfidenceScorer(learningConfig, this.patterns);
|
|
44
|
+
const storage = new PatternStorage(this.configPath, this.logger);
|
|
45
|
+
this.patternLearner = new PatternLearner(extractor, scorer, storage, learningConfig, this.logger);
|
|
46
|
+
// Note: Patterns will be loaded asynchronously via loadLearnedPatternsAsync()
|
|
47
|
+
this.logger.info('[ErrorPatternRegistry] Pattern learning enabled');
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
this.logger.error('[ErrorPatternRegistry] Failed to initialize pattern learning', {
|
|
51
|
+
error: error instanceof Error ? error.message : String(error),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Load learned patterns from storage (async)
|
|
57
|
+
*/
|
|
58
|
+
async loadLearnedPatternsAsync() {
|
|
59
|
+
if (!this.patternLearner) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
await this.patternLearner.loadLearnedPatterns();
|
|
64
|
+
const learnedPatterns = this.patternLearner.getLearnedPatterns();
|
|
65
|
+
for (const pattern of learnedPatterns) {
|
|
66
|
+
this.learnedPatterns.set(pattern.name, pattern);
|
|
67
|
+
this.register(pattern);
|
|
68
|
+
}
|
|
69
|
+
this.logger.info(`[ErrorPatternRegistry] Loaded ${learnedPatterns.length} learned patterns`);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
this.logger.error('[ErrorPatternRegistry] Failed to load learned patterns', {
|
|
73
|
+
error: error instanceof Error ? error.message : String(error),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Load learned patterns from storage (synchronous - kept for backward compatibility)
|
|
79
|
+
*/
|
|
80
|
+
loadLearnedPatterns() {
|
|
81
|
+
if (!this.patternLearner) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
// Load patterns without awaiting (sync fallback)
|
|
86
|
+
this.patternLearner.loadLearnedPatterns().catch((error) => {
|
|
87
|
+
this.logger.error('[ErrorPatternRegistry] Failed to load learned patterns asynchronously', {
|
|
88
|
+
error: error instanceof Error ? error.message : String(error),
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
const learnedPatterns = this.patternLearner.getLearnedPatterns();
|
|
92
|
+
for (const pattern of learnedPatterns) {
|
|
93
|
+
this.learnedPatterns.set(pattern.name, pattern);
|
|
94
|
+
this.register(pattern);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
this.logger.error('[ErrorPatternRegistry] Failed to load learned patterns', {
|
|
99
|
+
error: error instanceof Error ? error.message : String(error),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Reload learned patterns (for config hot reload)
|
|
105
|
+
*/
|
|
106
|
+
async reloadLearnedPatterns() {
|
|
107
|
+
this.learnedPatterns.clear();
|
|
108
|
+
await this.loadLearnedPatternsAsync();
|
|
20
109
|
}
|
|
21
110
|
/**
|
|
22
111
|
* Register default rate limit error patterns
|
|
@@ -108,7 +197,13 @@ export class ErrorPatternRegistry {
|
|
|
108
197
|
* Check if an error matches any registered rate limit pattern
|
|
109
198
|
*/
|
|
110
199
|
isRateLimitError(error) {
|
|
111
|
-
|
|
200
|
+
// Check if this is a rate limit error
|
|
201
|
+
const isRateLimit = this.getMatchedPattern(error) !== null;
|
|
202
|
+
// If enabled, learn from this error
|
|
203
|
+
if (isRateLimit && this.patternLearner) {
|
|
204
|
+
this.patternLearner.learnFromError(error);
|
|
205
|
+
}
|
|
206
|
+
return isRateLimit;
|
|
112
207
|
}
|
|
113
208
|
/**
|
|
114
209
|
* Get the matched pattern for an error, or null if no match
|
|
@@ -191,13 +286,81 @@ export class ErrorPatternRegistry {
|
|
|
191
286
|
this.registerDefaultPatterns();
|
|
192
287
|
}
|
|
193
288
|
/**
|
|
194
|
-
* Learn a new pattern from an error
|
|
195
|
-
|
|
289
|
+
* Learn a new pattern from an error
|
|
290
|
+
*/
|
|
291
|
+
addLearnedPattern(error) {
|
|
292
|
+
if (this.patternLearner) {
|
|
293
|
+
this.patternLearner.learnFromError(error);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
this.logger.warn('[ErrorPatternRegistry] Pattern learning is not enabled. Patterns must be manually registered via configuration.');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Get all learned patterns
|
|
301
|
+
*/
|
|
302
|
+
getLearnedPatterns() {
|
|
303
|
+
if (!this.patternLearner) {
|
|
304
|
+
return [];
|
|
305
|
+
}
|
|
306
|
+
return this.patternLearner.getLearnedPatterns();
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get a learned pattern by name
|
|
310
|
+
*/
|
|
311
|
+
getLearnedPatternByName(name) {
|
|
312
|
+
if (!this.patternLearner) {
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
return this.patternLearner.getLearnedPatternByName(name);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Remove a learned pattern by name
|
|
319
|
+
*/
|
|
320
|
+
async removeLearnedPattern(name) {
|
|
321
|
+
if (!this.patternLearner) {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
const removed = await this.patternLearner.removeLearnedPattern(name);
|
|
325
|
+
if (removed) {
|
|
326
|
+
this.removePattern(name);
|
|
327
|
+
}
|
|
328
|
+
return removed;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Merge duplicate learned patterns
|
|
332
|
+
*/
|
|
333
|
+
async mergeDuplicatePatterns() {
|
|
334
|
+
if (!this.patternLearner) {
|
|
335
|
+
return 0;
|
|
336
|
+
}
|
|
337
|
+
const mergedCount = await this.patternLearner.mergeDuplicatePatterns();
|
|
338
|
+
if (mergedCount > 0) {
|
|
339
|
+
this.reloadLearnedPatterns();
|
|
340
|
+
}
|
|
341
|
+
return mergedCount;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Cleanup old learned patterns
|
|
196
345
|
*/
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
346
|
+
async cleanupOldPatterns() {
|
|
347
|
+
if (!this.patternLearner) {
|
|
348
|
+
return 0;
|
|
349
|
+
}
|
|
350
|
+
const removedCount = await this.patternLearner.cleanupOldPatterns();
|
|
351
|
+
if (removedCount > 0) {
|
|
352
|
+
this.reloadLearnedPatterns();
|
|
353
|
+
}
|
|
354
|
+
return removedCount;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Get learning statistics
|
|
358
|
+
*/
|
|
359
|
+
getLearningStats() {
|
|
360
|
+
if (!this.patternLearner) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
return this.patternLearner.getStats();
|
|
201
364
|
}
|
|
202
365
|
/**
|
|
203
366
|
* Get statistics about registered patterns
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Storage for Learned Error Patterns
|
|
3
|
+
*/
|
|
4
|
+
import type { LearnedPattern } from '../types/index.js';
|
|
5
|
+
import type { Logger } from '../../logger.js';
|
|
6
|
+
/**
|
|
7
|
+
* PatternStorage - Manages persistence of learned patterns to config file
|
|
8
|
+
*/
|
|
9
|
+
export declare class PatternStorage {
|
|
10
|
+
private configPath;
|
|
11
|
+
private logger;
|
|
12
|
+
constructor(configPath: string, logger: Logger);
|
|
13
|
+
/**
|
|
14
|
+
* Save a learned pattern to config file
|
|
15
|
+
*/
|
|
16
|
+
savePattern(pattern: LearnedPattern): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Load all learned patterns from config file
|
|
19
|
+
*/
|
|
20
|
+
loadPatterns(): Promise<LearnedPattern[]>;
|
|
21
|
+
/**
|
|
22
|
+
* Delete a pattern by name from config file
|
|
23
|
+
*/
|
|
24
|
+
deletePattern(name: string): Promise<boolean>;
|
|
25
|
+
/**
|
|
26
|
+
* Merge duplicate patterns with high similarity
|
|
27
|
+
*/
|
|
28
|
+
mergeDuplicatePatterns(): Promise<number>;
|
|
29
|
+
/**
|
|
30
|
+
* Cleanup old patterns, keeping only the most confident ones
|
|
31
|
+
*/
|
|
32
|
+
cleanupOldPatterns(maxCount: number, patterns?: LearnedPattern[]): Promise<number>;
|
|
33
|
+
/**
|
|
34
|
+
* Load config file
|
|
35
|
+
*/
|
|
36
|
+
private loadConfig;
|
|
37
|
+
/**
|
|
38
|
+
* Save config file
|
|
39
|
+
*/
|
|
40
|
+
private saveConfig;
|
|
41
|
+
/**
|
|
42
|
+
* Validate pattern structure
|
|
43
|
+
*/
|
|
44
|
+
private isValidPattern;
|
|
45
|
+
/**
|
|
46
|
+
* Calculate similarity between two patterns
|
|
47
|
+
*/
|
|
48
|
+
private calculatePatternSimilarity;
|
|
49
|
+
}
|