@azumag/opencode-rate-limit-fallback 1.59.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.
@@ -1,9 +1,6 @@
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
4
  import { PatternLearner } from './PatternLearner.js';
8
5
  /**
9
6
  * Error Pattern Registry class
@@ -11,101 +8,21 @@ import { PatternLearner } from './PatternLearner.js';
11
8
  */
12
9
  export class ErrorPatternRegistry {
13
10
  patterns = [];
14
- learnedPatterns = new Map();
15
- patternLearner;
16
- logger;
17
- configPath;
18
- constructor(logger, config) {
11
+ learnedPatterns = [];
12
+ patternLearner = null;
13
+ learningConfig = null;
14
+ // Logger is available for future use
15
+ // @ts-ignore - Unused but kept for potential future use
16
+ _logger;
17
+ constructor(logger) {
19
18
  // Initialize logger
20
- this.logger = logger || {
19
+ this._logger = logger || {
21
20
  debug: () => { },
22
21
  info: () => { },
23
22
  warn: () => { },
24
23
  error: () => { },
25
24
  };
26
- this.configPath = config?.configPath;
27
25
  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();
109
26
  }
110
27
  /**
111
28
  * Register default rate limit error patterns
@@ -193,20 +110,66 @@ export class ErrorPatternRegistry {
193
110
  this.register(pattern);
194
111
  }
195
112
  }
113
+ /**
114
+ * Initialize pattern learning
115
+ */
116
+ initializePatternLearning(config, configFilePath) {
117
+ this.learningConfig = config;
118
+ this.patternLearner = new PatternLearner(config, this._logger);
119
+ this.patternLearner.setConfigFilePath(configFilePath);
120
+ }
121
+ /**
122
+ * Check if pattern learning is enabled
123
+ */
124
+ isLearningEnabled() {
125
+ return this.learningConfig?.enabled === true && this.patternLearner !== null;
126
+ }
127
+ /**
128
+ * Get the pattern learner instance
129
+ */
130
+ getPatternLearner() {
131
+ return this.patternLearner;
132
+ }
133
+ /**
134
+ * Add a learned pattern
135
+ */
136
+ addLearnedPattern(pattern) {
137
+ // Check for duplicates by name
138
+ const existingIndex = this.learnedPatterns.findIndex(p => p.name === pattern.name);
139
+ if (existingIndex >= 0) {
140
+ this.learnedPatterns[existingIndex] = pattern;
141
+ }
142
+ else {
143
+ this.learnedPatterns.push(pattern);
144
+ }
145
+ }
146
+ /**
147
+ * Get all learned patterns
148
+ */
149
+ getLearnedPatterns() {
150
+ return [...this.learnedPatterns];
151
+ }
152
+ /**
153
+ * Clear all learned patterns
154
+ */
155
+ clearLearnedPatterns() {
156
+ this.learnedPatterns = [];
157
+ }
158
+ /**
159
+ * Update learned patterns
160
+ */
161
+ updateLearnedPatterns(patterns) {
162
+ this.learnedPatterns = [...patterns];
163
+ }
196
164
  /**
197
165
  * Check if an error matches any registered rate limit pattern
198
166
  */
199
167
  isRateLimitError(error) {
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;
168
+ return this.getMatchedPattern(error) !== null;
207
169
  }
208
170
  /**
209
171
  * Get the matched pattern for an error, or null if no match
172
+ * Checks default patterns first, then learned patterns
210
173
  */
211
174
  getMatchedPattern(error) {
212
175
  if (!error || typeof error !== 'object') {
@@ -220,7 +183,7 @@ export class ErrorPatternRegistry {
220
183
  const statusCode = err.data?.statusCode?.toString() || '';
221
184
  // Combine all text sources for matching
222
185
  const allText = [responseBody, message, name, statusCode].join(' ').toLowerCase();
223
- // Check each pattern
186
+ // Check each pattern in default patterns first
224
187
  for (const pattern of this.patterns) {
225
188
  for (const patternStr of pattern.patterns) {
226
189
  let match = false;
@@ -241,13 +204,34 @@ export class ErrorPatternRegistry {
241
204
  }
242
205
  }
243
206
  }
207
+ // Check learned patterns
208
+ for (const pattern of this.learnedPatterns) {
209
+ for (const patternStr of pattern.patterns) {
210
+ let match = false;
211
+ if (typeof patternStr === 'string') {
212
+ // String matching (case-insensitive)
213
+ if (allText.includes(patternStr.toLowerCase())) {
214
+ match = true;
215
+ }
216
+ }
217
+ else if (patternStr instanceof RegExp) {
218
+ // RegExp matching
219
+ if (patternStr.test(allText)) {
220
+ match = true;
221
+ }
222
+ }
223
+ if (match) {
224
+ return pattern;
225
+ }
226
+ }
227
+ }
244
228
  return null;
245
229
  }
246
230
  /**
247
- * Get all registered patterns
231
+ * Get all registered patterns (including learned patterns)
248
232
  */
249
233
  getAllPatterns() {
250
- return [...this.patterns];
234
+ return [...this.patterns, ...this.learnedPatterns];
251
235
  }
252
236
  /**
253
237
  * Get patterns for a specific provider
@@ -285,101 +269,13 @@ export class ErrorPatternRegistry {
285
269
  this.clearAllPatterns();
286
270
  this.registerDefaultPatterns();
287
271
  }
288
- /**
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
- * Learn a new pattern from an error (async version)
301
- */
302
- async addLearnedPatternAsync(error) {
303
- if (this.patternLearner) {
304
- this.patternLearner.learnFromError(error);
305
- }
306
- else {
307
- this.logger.warn('[ErrorPatternRegistry] Pattern learning is not enabled. Patterns must be manually registered via configuration.');
308
- }
309
- }
310
- /**
311
- * Get all learned patterns
312
- */
313
- getLearnedPatterns() {
314
- if (!this.patternLearner) {
315
- return [];
316
- }
317
- return this.patternLearner.getLearnedPatterns();
318
- }
319
- /**
320
- * Get a learned pattern by name
321
- */
322
- getLearnedPatternByName(name) {
323
- if (!this.patternLearner) {
324
- return undefined;
325
- }
326
- return this.patternLearner.getLearnedPatternByName(name);
327
- }
328
- /**
329
- * Remove a learned pattern by name
330
- */
331
- async removeLearnedPattern(name) {
332
- if (!this.patternLearner) {
333
- return false;
334
- }
335
- const removed = await this.patternLearner.removeLearnedPattern(name);
336
- if (removed) {
337
- this.removePattern(name);
338
- }
339
- return removed;
340
- }
341
- /**
342
- * Merge duplicate learned patterns
343
- */
344
- async mergeDuplicatePatterns() {
345
- if (!this.patternLearner) {
346
- return 0;
347
- }
348
- const mergedCount = await this.patternLearner.mergeDuplicatePatterns();
349
- if (mergedCount > 0) {
350
- this.reloadLearnedPatterns();
351
- }
352
- return mergedCount;
353
- }
354
- /**
355
- * Cleanup old learned patterns
356
- */
357
- async cleanupOldPatterns() {
358
- if (!this.patternLearner) {
359
- return 0;
360
- }
361
- const removedCount = await this.patternLearner.cleanupOldPatterns();
362
- if (removedCount > 0) {
363
- this.reloadLearnedPatterns();
364
- }
365
- return removedCount;
366
- }
367
- /**
368
- * Get learning statistics
369
- */
370
- getLearningStats() {
371
- if (!this.patternLearner) {
372
- return null;
373
- }
374
- return this.patternLearner.getStats();
375
- }
376
272
  /**
377
273
  * Get statistics about registered patterns
378
274
  */
379
275
  getStats() {
380
276
  const byProvider = {};
381
277
  const byPriority = {};
382
- for (const pattern of this.patterns) {
278
+ for (const pattern of [...this.patterns, ...this.learnedPatterns]) {
383
279
  // Count by provider
384
280
  const provider = pattern.provider || 'generic';
385
281
  byProvider[provider] = (byProvider[provider] || 0) + 1;
@@ -388,7 +284,9 @@ export class ErrorPatternRegistry {
388
284
  byPriority[priorityRange] = (byPriority[priorityRange] || 0) + 1;
389
285
  }
390
286
  return {
391
- total: this.patterns.length,
287
+ total: this.patterns.length + this.learnedPatterns.length,
288
+ default: this.patterns.length,
289
+ learned: this.learnedPatterns.length,
392
290
  byProvider,
393
291
  byPriority,
394
292
  };
@@ -1,49 +1,48 @@
1
1
  /**
2
- * Pattern Storage for Learned Error Patterns
2
+ * Pattern Storage for persisting learned patterns
3
3
  */
4
- import type { LearnedPattern } from '../types/index.js';
5
- import type { Logger } from '../../logger.js';
4
+ import type { LearnedPattern, PatternLearningConfig, ErrorPattern } from '../types/index.js';
6
5
  /**
7
- * PatternStorage - Manages persistence of learned patterns to config file
6
+ * Pattern Storage class
7
+ * Manages persistence of learned patterns
8
8
  */
9
9
  export declare class PatternStorage {
10
- private configPath;
11
- private logger;
12
- constructor(configPath: string, logger: Logger);
10
+ private config;
11
+ private configFilePath;
13
12
  /**
14
- * Save a learned pattern to config file
13
+ * Constructor
15
14
  */
16
- savePattern(pattern: LearnedPattern): Promise<void>;
15
+ constructor(config: PatternLearningConfig);
17
16
  /**
18
- * Load all learned patterns from config file
17
+ * Update configuration
19
18
  */
20
- loadPatterns(): Promise<LearnedPattern[]>;
19
+ updateConfig(config: PatternLearningConfig): void;
21
20
  /**
22
- * Delete a pattern by name from config file
21
+ * Set the config file path
23
22
  */
24
- deletePattern(name: string): Promise<boolean>;
23
+ setConfigFilePath(path: string): void;
25
24
  /**
26
- * Merge duplicate patterns with high similarity
25
+ * Merge similar patterns (Jaccard similarity > 0.8)
27
26
  */
28
- mergeDuplicatePatterns(): Promise<number>;
27
+ mergeSimilarPatterns(patterns: LearnedPattern[]): LearnedPattern[];
29
28
  /**
30
- * Cleanup old patterns, keeping only the most confident ones
29
+ * Clean up old patterns when exceeding limit
31
30
  */
32
- cleanupOldPatterns(maxCount: number, patterns?: LearnedPattern[]): Promise<number>;
31
+ cleanupPatterns(patterns: LearnedPattern[]): LearnedPattern[];
33
32
  /**
34
- * Load config file
33
+ * Save learned patterns to config file
35
34
  */
36
- private loadConfig;
35
+ saveLearnedPatterns(patterns: LearnedPattern[]): Promise<void>;
37
36
  /**
38
- * Save config file
37
+ * Validate and load learned patterns from config
39
38
  */
40
- private saveConfig;
39
+ loadLearnedPatterns(): Promise<LearnedPattern[]>;
41
40
  /**
42
- * Validate pattern structure
41
+ * Validate a learned pattern object
43
42
  */
44
- private isValidPattern;
43
+ private isValidLearnedPattern;
45
44
  /**
46
- * Calculate similarity between two patterns
45
+ * Create a learned pattern from an error pattern
47
46
  */
48
- private calculatePatternSimilarity;
47
+ createLearnedPattern(basePattern: ErrorPattern, confidence: number, sampleCount: number): LearnedPattern;
49
48
  }