@azumag/opencode-rate-limit-fallback 1.50.0 → 1.58.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 +262 -0
- package/dist/src/errors/PatternRegistry.d.ts +58 -5
- package/dist/src/errors/PatternRegistry.js +182 -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
package/README.md
CHANGED
|
@@ -18,10 +18,12 @@ OpenCode plugin that automatically switches to fallback models when rate limited
|
|
|
18
18
|
- Configurable retry limits and timeouts
|
|
19
19
|
- Retry statistics tracking
|
|
20
20
|
- Toast notifications for user feedback
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
- Subagent session support with automatic fallback propagation to parent sessions
|
|
22
|
+
- Configurable maximum subagent nesting depth
|
|
23
|
+
- **Circuit breaker pattern** to prevent cascading failures from consistently failing models
|
|
24
|
+
- **Metrics collection** to track rate limits, fallbacks, and model performance
|
|
25
|
+
- **Configuration hot reload** - Reload configuration changes without restarting OpenCode
|
|
26
|
+
- **Dynamic fallback model prioritization** - Automatically reorders models based on success rate, response time, and usage frequency
|
|
25
27
|
|
|
26
28
|
## Installation
|
|
27
29
|
|
|
@@ -98,6 +100,12 @@ Create a configuration file at one of these locations:
|
|
|
98
100
|
"recoveryTimeoutMs": 60000,
|
|
99
101
|
"halfOpenMaxCalls": 1,
|
|
100
102
|
"successThreshold": 2
|
|
103
|
+
},
|
|
104
|
+
"configReload": {
|
|
105
|
+
"enabled": true,
|
|
106
|
+
"watchFile": true,
|
|
107
|
+
"debounceMs": 1000,
|
|
108
|
+
"notifyOnReload": true
|
|
101
109
|
}
|
|
102
110
|
}
|
|
103
111
|
```
|
|
@@ -111,9 +119,99 @@ Create a configuration file at one of these locations:
|
|
|
111
119
|
| `fallbackMode` | string | `"cycle"` | Behavior when all models are exhausted (see below) |
|
|
112
120
|
| `fallbackModels` | array | See below | List of fallback models in priority order |
|
|
113
121
|
| `maxSubagentDepth` | number | `10` | Maximum nesting depth for subagent hierarchies |
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
| `enableSubagentFallback` | boolean | `true` | Enable/disable fallback for subagent sessions |
|
|
123
|
+
| `retryPolicy` | object | See below | Retry policy configuration (see below) |
|
|
124
|
+
| `circuitBreaker` | object | See below | Circuit breaker configuration (see below) |
|
|
125
|
+
| `configReload` | object | See below | Configuration hot reload settings (see below) |
|
|
126
|
+
| `dynamicPrioritization` | object | See below | Dynamic prioritization settings (see below) |
|
|
127
|
+
|
|
128
|
+
### Dynamic Prioritization
|
|
129
|
+
|
|
130
|
+
The dynamic prioritization feature automatically reorders your fallback models based on their performance metrics, helping you use the most reliable and fastest models first.
|
|
131
|
+
|
|
132
|
+
| Option | Type | Default | Description |
|
|
133
|
+
|--------|------|---------|-------------|
|
|
134
|
+
| `enabled` | boolean | `false` | Enable/disable dynamic prioritization |
|
|
135
|
+
| `updateInterval` | number | `10` | Number of requests between score updates (performance optimization) |
|
|
136
|
+
| `successRateWeight` | number | `0.6` | Weight for success rate (0-1) |
|
|
137
|
+
| `responseTimeWeight` | number | `0.3` | Weight for response time (0-1) |
|
|
138
|
+
| `recentUsageWeight` | number | `0.1` | Weight for recent usage frequency (0-1) |
|
|
139
|
+
| `minSamples` | number | `3` | Minimum samples before using dynamic ordering |
|
|
140
|
+
| `maxHistorySize` | number | `100` | Maximum history size for usage tracking |
|
|
141
|
+
|
|
142
|
+
#### How It Works
|
|
143
|
+
|
|
144
|
+
Dynamic prioritization calculates a score for each model based on three factors:
|
|
145
|
+
|
|
146
|
+
1. **Success Rate** (default weight: 0.6)
|
|
147
|
+
- Based on health score from HealthTracker
|
|
148
|
+
- Higher success rate = higher score
|
|
149
|
+
|
|
150
|
+
2. **Response Time** (default weight: 0.3)
|
|
151
|
+
- Faster response times get higher scores
|
|
152
|
+
- Thresholds: <500ms (excellent), >5000ms (poor)
|
|
153
|
+
|
|
154
|
+
3. **Recent Usage** (default weight: 0.1)
|
|
155
|
+
- Recently used models get a small boost
|
|
156
|
+
- Decays over 24 hours
|
|
157
|
+
|
|
158
|
+
The final score is calculated as:
|
|
159
|
+
```
|
|
160
|
+
score = (healthScore / 100 * successRateWeight) +
|
|
161
|
+
(normalizedResponseTime * responseTimeWeight) +
|
|
162
|
+
(normalizedRecentUsage * recentUsageWeight)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
#### Learning Phase
|
|
166
|
+
|
|
167
|
+
- Uses static ordering until `minSamples` models have sufficient data
|
|
168
|
+
- Default: 3 models need at least 3 requests each
|
|
169
|
+
- Ensures reliable data before reordering
|
|
170
|
+
|
|
171
|
+
#### Configuration Examples
|
|
172
|
+
|
|
173
|
+
**Enable with defaults:**
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"dynamicPrioritization": {
|
|
177
|
+
"enabled": true
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Full configuration:**
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"dynamicPrioritization": {
|
|
186
|
+
"enabled": true,
|
|
187
|
+
"updateInterval": 10,
|
|
188
|
+
"successRateWeight": 0.6,
|
|
189
|
+
"responseTimeWeight": 0.3,
|
|
190
|
+
"recentUsageWeight": 0.1,
|
|
191
|
+
"minSamples": 3,
|
|
192
|
+
"maxHistorySize": 100
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Prioritize speed over reliability:**
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"dynamicPrioritization": {
|
|
201
|
+
"enabled": true,
|
|
202
|
+
"successRateWeight": 0.4,
|
|
203
|
+
"responseTimeWeight": 0.5,
|
|
204
|
+
"recentUsageWeight": 0.1
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
#### Important Notes
|
|
210
|
+
|
|
211
|
+
- **Disabled by default**: Set `enabled: true` to activate
|
|
212
|
+
- **Requires health tracking**: Uses HealthTracker data for success rates
|
|
213
|
+
- **Weights must sum to ~1.0**: Ensure optimal scoring behavior
|
|
214
|
+
- **Hot reload supported**: Can be enabled/disabled without restarting OpenCode
|
|
117
215
|
|
|
118
216
|
### Git Worktree Support
|
|
119
217
|
|
|
@@ -249,6 +347,75 @@ The circuit breaker maintains three states for each model:
|
|
|
249
347
|
| Production | 5 | 60000 | 1 |
|
|
250
348
|
| High Availability | 10 | 30000 | 2 |
|
|
251
349
|
|
|
350
|
+
### Configuration Hot Reload
|
|
351
|
+
|
|
352
|
+
The plugin supports automatic configuration reloading without requiring you to restart OpenCode. When you edit your configuration file, the plugin detects the changes and applies them seamlessly.
|
|
353
|
+
|
|
354
|
+
#### Configuration Options
|
|
355
|
+
|
|
356
|
+
| Option | Type | Default | Description |
|
|
357
|
+
|--------|------|---------|-------------|
|
|
358
|
+
| `configReload.enabled` | boolean | `false` | Enable/disable configuration hot reload |
|
|
359
|
+
| `configReload.watchFile` | boolean | `true` | Watch config file for changes |
|
|
360
|
+
| `configReload.debounceMs` | number | `1000` | Debounce delay (ms) to handle multiple file writes |
|
|
361
|
+
| `configReload.notifyOnReload` | boolean | `true` | Show toast notifications on reload |
|
|
362
|
+
|
|
363
|
+
#### How It Works
|
|
364
|
+
|
|
365
|
+
1. **File Watching**: When enabled, the plugin watches your configuration file for changes
|
|
366
|
+
2. **Debouncing**: Multiple file writes (e.g., from editors) are debounced to prevent unnecessary reloads
|
|
367
|
+
3. **Validation**: New configuration is validated before applying it
|
|
368
|
+
4. **Graceful Application**: If valid, the new configuration is applied without interrupting active sessions
|
|
369
|
+
5. **Toast Notifications**: You receive toast notifications for successful or failed reloads
|
|
370
|
+
|
|
371
|
+
#### Behavior
|
|
372
|
+
|
|
373
|
+
**What gets reloaded:**
|
|
374
|
+
- Fallback model list
|
|
375
|
+
- Cooldown periods
|
|
376
|
+
- Fallback mode
|
|
377
|
+
- Retry policies
|
|
378
|
+
- Circuit breaker settings
|
|
379
|
+
- Metrics configuration
|
|
380
|
+
- Log configuration
|
|
381
|
+
- Health tracking settings
|
|
382
|
+
|
|
383
|
+
**What doesn't change:**
|
|
384
|
+
- Active session states
|
|
385
|
+
- Rate-limited model tracking
|
|
386
|
+
- Health tracking data
|
|
387
|
+
- Metrics history
|
|
388
|
+
|
|
389
|
+
#### Configuration Examples
|
|
390
|
+
|
|
391
|
+
**Enable hot reload:**
|
|
392
|
+
```json
|
|
393
|
+
{
|
|
394
|
+
"configReload": {
|
|
395
|
+
"enabled": true
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**Full configuration:**
|
|
401
|
+
```json
|
|
402
|
+
{
|
|
403
|
+
"configReload": {
|
|
404
|
+
"enabled": true,
|
|
405
|
+
"watchFile": true,
|
|
406
|
+
"debounceMs": 1000,
|
|
407
|
+
"notifyOnReload": true
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### Important Notes
|
|
413
|
+
|
|
414
|
+
- **Disabled by default**: Set `configReload.enabled: true` to activate this feature
|
|
415
|
+
- **Valid configs only**: Invalid configurations are rejected, and old config is preserved
|
|
416
|
+
- **No restart needed**: You can experiment with different configurations without restarting OpenCode
|
|
417
|
+
- **Session preservation**: Active sessions continue working during reload
|
|
418
|
+
|
|
252
419
|
### ⚠️ Important: Configuration Required
|
|
253
420
|
|
|
254
421
|
**As of v1.43.0, this plugin requires explicit configuration.**
|
|
@@ -395,11 +562,12 @@ When OpenCode uses subagents (e.g., for complex tasks requiring specialized agen
|
|
|
395
562
|
## Metrics
|
|
396
563
|
|
|
397
564
|
The plugin includes a metrics collection feature that tracks:
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
565
|
+
- Rate limit events per provider/model
|
|
566
|
+
- Fallback statistics (total, successful, failed, average duration)
|
|
567
|
+
- **Retry statistics** (total attempts, successes, failures, average delay)
|
|
568
|
+
- Model performance (requests, successes, failures, response time)
|
|
569
|
+
- **Circuit breaker statistics** (state transitions, open/closed counts)
|
|
570
|
+
- **Dynamic prioritization statistics** (enabled status, reorder count, models with scores)
|
|
403
571
|
|
|
404
572
|
### Metrics Configuration
|
|
405
573
|
|
|
@@ -480,19 +648,25 @@ Model Performance:
|
|
|
480
648
|
Avg Response: 0.85s
|
|
481
649
|
Success Rate: 90.0%
|
|
482
650
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
651
|
+
Circuit Breaker:
|
|
652
|
+
----------------------------------------
|
|
653
|
+
anthropic/claude-3-5-sonnet-20250514:
|
|
654
|
+
State: OPEN
|
|
655
|
+
Failures: 5
|
|
656
|
+
Successes: 0
|
|
657
|
+
State Transitions: 2
|
|
658
|
+
google/gemini-2.5-pro:
|
|
659
|
+
State: CLOSED
|
|
660
|
+
Failures: 2
|
|
661
|
+
Successes: 8
|
|
662
|
+
State Transitions: 3
|
|
663
|
+
|
|
664
|
+
Dynamic Prioritization:
|
|
665
|
+
----------------------------------------
|
|
666
|
+
Enabled: Yes
|
|
667
|
+
Reorders: 5
|
|
668
|
+
Models with dynamic scores: 3
|
|
669
|
+
```
|
|
496
670
|
|
|
497
671
|
**JSON** (machine-readable):
|
|
498
672
|
```json
|
|
@@ -554,12 +728,17 @@ Model Performance:
|
|
|
554
728
|
"failures": 2,
|
|
555
729
|
"successes": 8,
|
|
556
730
|
"stateTransitions": 3
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
731
|
+
}
|
|
732
|
+
},
|
|
733
|
+
"dynamicPrioritization": {
|
|
734
|
+
"enabled": true,
|
|
735
|
+
"reorders": 5,
|
|
736
|
+
"modelsWithDynamicScores": 3
|
|
737
|
+
},
|
|
738
|
+
"startedAt": 1739148000000,
|
|
739
|
+
"generatedAt": 1739149800000
|
|
740
|
+
}
|
|
741
|
+
```
|
|
563
742
|
|
|
564
743
|
**CSV** (spreadsheet-friendly):
|
|
565
744
|
```
|
|
@@ -584,11 +763,15 @@ google/gemini-2.5-pro,7,5,71.4
|
|
|
584
763
|
model,requests,successes,failures,avg_response_time_ms,success_rate
|
|
585
764
|
google/gemini-2.5-pro,10,9,1,850,90.0
|
|
586
765
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
766
|
+
=== CIRCUIT_BREAKER ===
|
|
767
|
+
model,current_state,failures,successes,state_transitions
|
|
768
|
+
anthropic/claude-3-5-sonnet-20250514,OPEN,5,0,2
|
|
769
|
+
google/gemini-2.5-pro,CLOSED,2,8,3
|
|
770
|
+
|
|
771
|
+
=== DYNAMIC_PRIORITIZATION ===
|
|
772
|
+
enabled,reorders,models_with_dynamic_scores
|
|
773
|
+
Yes,5,3
|
|
774
|
+
```
|
|
592
775
|
|
|
593
776
|
## License
|
|
594
777
|
|
|
@@ -466,6 +466,100 @@ export class ConfigValidator {
|
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
468
|
}
|
|
469
|
+
// Validate dynamicPrioritization
|
|
470
|
+
if (config.dynamicPrioritization) {
|
|
471
|
+
if (typeof config.dynamicPrioritization !== 'object') {
|
|
472
|
+
errors.push({
|
|
473
|
+
path: 'dynamicPrioritization',
|
|
474
|
+
message: 'dynamicPrioritization must be an object',
|
|
475
|
+
severity: 'error',
|
|
476
|
+
value: config.dynamicPrioritization,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
if (config.dynamicPrioritization.enabled !== undefined && typeof config.dynamicPrioritization.enabled !== 'boolean') {
|
|
481
|
+
errors.push({
|
|
482
|
+
path: 'dynamicPrioritization.enabled',
|
|
483
|
+
message: 'enabled must be a boolean',
|
|
484
|
+
severity: 'error',
|
|
485
|
+
value: config.dynamicPrioritization.enabled,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
if (config.dynamicPrioritization.updateInterval !== undefined) {
|
|
489
|
+
if (typeof config.dynamicPrioritization.updateInterval !== 'number' || config.dynamicPrioritization.updateInterval < 1) {
|
|
490
|
+
errors.push({
|
|
491
|
+
path: 'dynamicPrioritization.updateInterval',
|
|
492
|
+
message: 'updateInterval must be a positive number',
|
|
493
|
+
severity: 'error',
|
|
494
|
+
value: config.dynamicPrioritization.updateInterval,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (config.dynamicPrioritization.successRateWeight !== undefined) {
|
|
499
|
+
if (typeof config.dynamicPrioritization.successRateWeight !== 'number' || config.dynamicPrioritization.successRateWeight < 0 || config.dynamicPrioritization.successRateWeight > 1) {
|
|
500
|
+
errors.push({
|
|
501
|
+
path: 'dynamicPrioritization.successRateWeight',
|
|
502
|
+
message: 'successRateWeight must be a number between 0 and 1',
|
|
503
|
+
severity: 'error',
|
|
504
|
+
value: config.dynamicPrioritization.successRateWeight,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
if (config.dynamicPrioritization.responseTimeWeight !== undefined) {
|
|
509
|
+
if (typeof config.dynamicPrioritization.responseTimeWeight !== 'number' || config.dynamicPrioritization.responseTimeWeight < 0 || config.dynamicPrioritization.responseTimeWeight > 1) {
|
|
510
|
+
errors.push({
|
|
511
|
+
path: 'dynamicPrioritization.responseTimeWeight',
|
|
512
|
+
message: 'responseTimeWeight must be a number between 0 and 1',
|
|
513
|
+
severity: 'error',
|
|
514
|
+
value: config.dynamicPrioritization.responseTimeWeight,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (config.dynamicPrioritization.recentUsageWeight !== undefined) {
|
|
519
|
+
if (typeof config.dynamicPrioritization.recentUsageWeight !== 'number' || config.dynamicPrioritization.recentUsageWeight < 0 || config.dynamicPrioritization.recentUsageWeight > 1) {
|
|
520
|
+
errors.push({
|
|
521
|
+
path: 'dynamicPrioritization.recentUsageWeight',
|
|
522
|
+
message: 'recentUsageWeight must be a number between 0 and 1',
|
|
523
|
+
severity: 'error',
|
|
524
|
+
value: config.dynamicPrioritization.recentUsageWeight,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// Validate that weights sum to approximately 1.0
|
|
529
|
+
const successRateWeight = config.dynamicPrioritization.successRateWeight ?? 0.6;
|
|
530
|
+
const responseTimeWeight = config.dynamicPrioritization.responseTimeWeight ?? 0.3;
|
|
531
|
+
const recentUsageWeight = config.dynamicPrioritization.recentUsageWeight ?? 0.1;
|
|
532
|
+
const totalWeight = successRateWeight + responseTimeWeight + recentUsageWeight;
|
|
533
|
+
if (Math.abs(totalWeight - 1.0) > 0.1) {
|
|
534
|
+
warnings.push({
|
|
535
|
+
path: 'dynamicPrioritization',
|
|
536
|
+
message: `Weights sum to ${totalWeight.toFixed(2)}, which is significantly different from 1.0. This may affect prioritization behavior.`,
|
|
537
|
+
severity: 'warning',
|
|
538
|
+
value: { successRateWeight, responseTimeWeight, recentUsageWeight, totalWeight },
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
if (config.dynamicPrioritization.minSamples !== undefined) {
|
|
542
|
+
if (typeof config.dynamicPrioritization.minSamples !== 'number' || config.dynamicPrioritization.minSamples < 1) {
|
|
543
|
+
errors.push({
|
|
544
|
+
path: 'dynamicPrioritization.minSamples',
|
|
545
|
+
message: 'minSamples must be a positive number',
|
|
546
|
+
severity: 'error',
|
|
547
|
+
value: config.dynamicPrioritization.minSamples,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (config.dynamicPrioritization.maxHistorySize !== undefined) {
|
|
552
|
+
if (typeof config.dynamicPrioritization.maxHistorySize !== 'number' || config.dynamicPrioritization.maxHistorySize < 1) {
|
|
553
|
+
errors.push({
|
|
554
|
+
path: 'dynamicPrioritization.maxHistorySize',
|
|
555
|
+
message: 'maxHistorySize must be a positive number',
|
|
556
|
+
severity: 'error',
|
|
557
|
+
value: config.dynamicPrioritization.maxHistorySize,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
469
563
|
// Log warnings if enabled
|
|
470
564
|
if (logWarnings && warnings.length > 0 && this.logger) {
|
|
471
565
|
for (const warning of warnings) {
|
|
@@ -78,3 +78,25 @@ export declare const DEFAULT_CONFIG_RELOAD_CONFIG: {
|
|
|
78
78
|
readonly debounceMs: 1000;
|
|
79
79
|
readonly notifyOnReload: true;
|
|
80
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Default dynamic prioritization configuration
|
|
83
|
+
*/
|
|
84
|
+
export declare const DEFAULT_DYNAMIC_PRIORITIZATION_CONFIG: {
|
|
85
|
+
readonly enabled: false;
|
|
86
|
+
readonly updateInterval: 10;
|
|
87
|
+
readonly successRateWeight: 0.6;
|
|
88
|
+
readonly responseTimeWeight: 0.3;
|
|
89
|
+
readonly recentUsageWeight: 0.1;
|
|
90
|
+
readonly minSamples: 3;
|
|
91
|
+
readonly maxHistorySize: 100;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Default error pattern learning configuration
|
|
95
|
+
*/
|
|
96
|
+
export declare const DEFAULT_ERROR_PATTERN_LEARNING_CONFIG: {
|
|
97
|
+
readonly enableLearning: false;
|
|
98
|
+
readonly autoApproveThreshold: 0.8;
|
|
99
|
+
readonly maxLearnedPatterns: 20;
|
|
100
|
+
readonly minErrorFrequency: 3;
|
|
101
|
+
readonly learningWindowMs: number;
|
|
102
|
+
};
|
|
@@ -101,3 +101,31 @@ export const DEFAULT_CONFIG_RELOAD_CONFIG = {
|
|
|
101
101
|
debounceMs: 1000,
|
|
102
102
|
notifyOnReload: true,
|
|
103
103
|
};
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// Dynamic Prioritization Defaults
|
|
106
|
+
// ============================================================================
|
|
107
|
+
/**
|
|
108
|
+
* Default dynamic prioritization configuration
|
|
109
|
+
*/
|
|
110
|
+
export const DEFAULT_DYNAMIC_PRIORITIZATION_CONFIG = {
|
|
111
|
+
enabled: false,
|
|
112
|
+
updateInterval: 10,
|
|
113
|
+
successRateWeight: 0.6,
|
|
114
|
+
responseTimeWeight: 0.3,
|
|
115
|
+
recentUsageWeight: 0.1,
|
|
116
|
+
minSamples: 3,
|
|
117
|
+
maxHistorySize: 100,
|
|
118
|
+
};
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// Error Pattern Learning Defaults
|
|
121
|
+
// ============================================================================
|
|
122
|
+
/**
|
|
123
|
+
* Default error pattern learning configuration
|
|
124
|
+
*/
|
|
125
|
+
export const DEFAULT_ERROR_PATTERN_LEARNING_CONFIG = {
|
|
126
|
+
enableLearning: false,
|
|
127
|
+
autoApproveThreshold: 0.8,
|
|
128
|
+
maxLearnedPatterns: 20,
|
|
129
|
+
minErrorFrequency: 3,
|
|
130
|
+
learningWindowMs: 24 * 60 * 60 * 1000, // 24 hours
|
|
131
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Prioritizer
|
|
3
|
+
* Dynamically prioritizes fallback models based on performance metrics
|
|
4
|
+
*/
|
|
5
|
+
import type { Logger } from '../../logger.js';
|
|
6
|
+
import type { FallbackModel, DynamicPrioritizationConfig } from '../types/index.js';
|
|
7
|
+
import type { HealthTracker } from '../health/HealthTracker.js';
|
|
8
|
+
import type { MetricsManager } from '../metrics/MetricsManager.js';
|
|
9
|
+
/**
|
|
10
|
+
* Dynamic Prioritizer class for calculating dynamic model scores
|
|
11
|
+
*/
|
|
12
|
+
export declare class DynamicPrioritizer {
|
|
13
|
+
private config;
|
|
14
|
+
private healthTracker;
|
|
15
|
+
private logger;
|
|
16
|
+
private metricsManager?;
|
|
17
|
+
private modelScores;
|
|
18
|
+
private modelUsageHistory;
|
|
19
|
+
private requestCount;
|
|
20
|
+
constructor(config: DynamicPrioritizationConfig, healthTracker: HealthTracker, logger: Logger, metricsManager?: MetricsManager);
|
|
21
|
+
/**
|
|
22
|
+
* Record usage of a model for tracking recent activity
|
|
23
|
+
*/
|
|
24
|
+
recordUsage(providerID: string, modelID: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Calculate dynamic score for a model
|
|
27
|
+
* Score is 0-1, higher is better
|
|
28
|
+
*/
|
|
29
|
+
calculateScore(providerID: string, modelID: string): number;
|
|
30
|
+
/**
|
|
31
|
+
* Get prioritized models based on dynamic scores
|
|
32
|
+
* Returns models sorted by score (highest first)
|
|
33
|
+
*/
|
|
34
|
+
getPrioritizedModels(candidates: FallbackModel[]): FallbackModel[];
|
|
35
|
+
/**
|
|
36
|
+
* Check if dynamic ordering should be used
|
|
37
|
+
* Returns true if dynamic prioritization is enabled and we have enough data for reliable ordering
|
|
38
|
+
*/
|
|
39
|
+
shouldUseDynamicOrdering(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Update configuration
|
|
42
|
+
*/
|
|
43
|
+
updateConfig(newConfig: DynamicPrioritizationConfig): void;
|
|
44
|
+
/**
|
|
45
|
+
* Get current scores for all tracked models
|
|
46
|
+
*/
|
|
47
|
+
getAllScores(): Map<string, number>;
|
|
48
|
+
/**
|
|
49
|
+
* Check if dynamic prioritization is enabled
|
|
50
|
+
*/
|
|
51
|
+
isEnabled(): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Get number of models with calculated scores
|
|
54
|
+
*/
|
|
55
|
+
getModelsWithDynamicScores(): number;
|
|
56
|
+
/**
|
|
57
|
+
* Update metrics with current dynamic prioritization state
|
|
58
|
+
*/
|
|
59
|
+
updateMetrics(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Reset all scores and usage history
|
|
62
|
+
*/
|
|
63
|
+
reset(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Normalize response time (inverse - faster is better)
|
|
66
|
+
* Returns 0-1, higher is better
|
|
67
|
+
*/
|
|
68
|
+
private normalizeResponseTime;
|
|
69
|
+
/**
|
|
70
|
+
* Calculate recent usage score
|
|
71
|
+
* Returns 0-1, higher for more recent usage
|
|
72
|
+
*/
|
|
73
|
+
private calculateRecentUsageScore;
|
|
74
|
+
}
|