@azumag/opencode-rate-limit-fallback 1.0.22 → 1.1.4
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 +27 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +310 -309
- package/dist/index.js.map +1 -0
- package/package.json +20 -19
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ OpenCode plugin that automatically switches to fallback models when rate limited
|
|
|
13
13
|
- Session model tracking for sequential fallback across multiple rate limits
|
|
14
14
|
- Cooldown period to prevent immediate retry on rate-limited models
|
|
15
15
|
- Toast notifications for user feedback
|
|
16
|
+
- Subagent session support with automatic fallback propagation to parent sessions
|
|
17
|
+
- Configurable maximum subagent nesting depth
|
|
16
18
|
|
|
17
19
|
## Installation
|
|
18
20
|
|
|
@@ -54,6 +56,8 @@ Create a configuration file at one of these locations:
|
|
|
54
56
|
"enabled": true,
|
|
55
57
|
"cooldownMs": 60000,
|
|
56
58
|
"fallbackMode": "cycle",
|
|
59
|
+
"maxSubagentDepth": 10,
|
|
60
|
+
"enableSubagentFallback": true,
|
|
57
61
|
"fallbackModels": [
|
|
58
62
|
{ "providerID": "anthropic", "modelID": "claude-sonnet-4-20250514" },
|
|
59
63
|
{ "providerID": "google", "modelID": "gemini-2.5-pro" },
|
|
@@ -70,6 +74,8 @@ Create a configuration file at one of these locations:
|
|
|
70
74
|
| `cooldownMs` | number | `60000` | Cooldown period (ms) before retrying a rate-limited model |
|
|
71
75
|
| `fallbackMode` | string | `"cycle"` | Behavior when all models are exhausted (see below) |
|
|
72
76
|
| `fallbackModels` | array | See below | List of fallback models in priority order |
|
|
77
|
+
| `maxSubagentDepth` | number | `10` | Maximum nesting depth for subagent hierarchies |
|
|
78
|
+
| `enableSubagentFallback` | boolean | `true` | Enable/disable fallback for subagent sessions |
|
|
73
79
|
|
|
74
80
|
### Fallback Modes
|
|
75
81
|
|
|
@@ -89,14 +95,32 @@ If no configuration is provided, the following models are used:
|
|
|
89
95
|
|
|
90
96
|
## How It Works
|
|
91
97
|
|
|
92
|
-
|
|
98
|
+
1. **Detection**: The plugin listens for rate limit errors via:
|
|
93
99
|
- `session.error` events
|
|
94
100
|
- `message.updated` events with errors
|
|
95
101
|
- `session.status` events with `type: "retry"`
|
|
96
102
|
|
|
97
|
-
2. **
|
|
103
|
+
2. **Abort**: When a rate limit is detected, the current session is aborted to stop OpenCode's internal retry mechanism.
|
|
98
104
|
|
|
99
|
-
3. **
|
|
105
|
+
3. **Fallback**: The plugin selects the next available model from the fallback list and resends the last user message.
|
|
106
|
+
|
|
107
|
+
4. **Cooldown**: Rate-limited models are tracked and skipped for the configured cooldown period.
|
|
108
|
+
|
|
109
|
+
## Subagent Support
|
|
110
|
+
|
|
111
|
+
When OpenCode uses subagents (e.g., for complex tasks requiring specialized agents):
|
|
112
|
+
|
|
113
|
+
- **Automatic Detection**: The plugin detects `subagent.session.created` events
|
|
114
|
+
- **Hierarchy Tracking**: Maintains parent-child relationships between sessions
|
|
115
|
+
- **Fallback Propagation**: When a subagent hits a rate limit, the fallback is triggered at the root session level
|
|
116
|
+
- **Model Sharing**: All subagents in a hierarchy share the same fallback model
|
|
117
|
+
|
|
118
|
+
### Subagent Configuration
|
|
119
|
+
|
|
120
|
+
| Option | Type | Default | Description |
|
|
121
|
+
|--------|------|---------|-------------|
|
|
122
|
+
| `maxSubagentDepth` | number | `10` | Maximum nesting depth for subagent hierarchies |
|
|
123
|
+
| `enableSubagentFallback` | boolean | `true` | Enable/disable fallback for subagent sessions |
|
|
100
124
|
|
|
101
125
|
## License
|
|
102
126
|
|
package/dist/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA2NlD,eAAO,MAAM,iBAAiB,EAAE,MAic/B,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,51 @@
|
|
|
1
|
-
import { existsSync, readFileSync
|
|
2
|
-
import { join
|
|
3
|
-
//
|
|
4
|
-
|
|
1
|
+
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
// Event type guards
|
|
4
|
+
function isSessionErrorEvent(event) {
|
|
5
|
+
return event.type === "session.error" &&
|
|
6
|
+
typeof event.properties === "object" &&
|
|
7
|
+
event.properties !== null &&
|
|
8
|
+
"sessionID" in event.properties &&
|
|
9
|
+
"error" in event.properties;
|
|
10
|
+
}
|
|
11
|
+
function isMessageUpdatedEvent(event) {
|
|
12
|
+
return event.type === "message.updated" &&
|
|
13
|
+
typeof event.properties === "object" &&
|
|
14
|
+
event.properties !== null &&
|
|
15
|
+
"info" in event.properties;
|
|
16
|
+
}
|
|
17
|
+
function isSessionStatusEvent(event) {
|
|
18
|
+
return event.type === "session.status" &&
|
|
19
|
+
typeof event.properties === "object" &&
|
|
20
|
+
event.properties !== null;
|
|
21
|
+
}
|
|
22
|
+
// Subagent event type guards
|
|
23
|
+
function isSubagentSessionCreatedEvent(event) {
|
|
24
|
+
return event.type === "subagent.session.created" &&
|
|
25
|
+
typeof event.properties === "object" &&
|
|
26
|
+
event.properties !== null &&
|
|
27
|
+
"sessionID" in event.properties &&
|
|
28
|
+
"parentSessionID" in event.properties;
|
|
29
|
+
}
|
|
5
30
|
const DEFAULT_FALLBACK_MODELS = [
|
|
6
31
|
{ providerID: "anthropic", modelID: "claude-sonnet-4-20250514" },
|
|
7
32
|
{ providerID: "google", modelID: "gemini-2.5-pro" },
|
|
8
33
|
{ providerID: "google", modelID: "gemini-2.5-flash" },
|
|
9
34
|
];
|
|
35
|
+
const VALID_FALLBACK_MODES = ["cycle", "stop", "retry-last"];
|
|
36
|
+
const RATE_LIMIT_INDICATORS = [
|
|
37
|
+
"rate limit",
|
|
38
|
+
"rate_limit",
|
|
39
|
+
"ratelimit",
|
|
40
|
+
"too many requests",
|
|
41
|
+
"quota exceeded",
|
|
42
|
+
"resource exhausted",
|
|
43
|
+
"usage limit",
|
|
44
|
+
"high concurrency usage of this api",
|
|
45
|
+
"high concurrency",
|
|
46
|
+
"reduce concurrency",
|
|
47
|
+
"429",
|
|
48
|
+
];
|
|
10
49
|
const DEFAULT_CONFIG = {
|
|
11
50
|
fallbackModels: DEFAULT_FALLBACK_MODELS,
|
|
12
51
|
cooldownMs: 60 * 1000,
|
|
@@ -27,16 +66,15 @@ function loadConfig(directory) {
|
|
|
27
66
|
const content = readFileSync(configPath, "utf-8");
|
|
28
67
|
const userConfig = JSON.parse(content);
|
|
29
68
|
const mode = userConfig.fallbackMode;
|
|
30
|
-
const validModes = ["cycle", "stop", "retry-last"];
|
|
31
69
|
return {
|
|
32
70
|
...DEFAULT_CONFIG,
|
|
33
71
|
...userConfig,
|
|
34
72
|
fallbackModels: userConfig.fallbackModels || DEFAULT_CONFIG.fallbackModels,
|
|
35
|
-
fallbackMode:
|
|
73
|
+
fallbackMode: VALID_FALLBACK_MODES.includes(mode) ? mode : DEFAULT_CONFIG.fallbackMode,
|
|
36
74
|
};
|
|
37
75
|
}
|
|
38
76
|
catch (error) {
|
|
39
|
-
//
|
|
77
|
+
// Silently ignore config load errors
|
|
40
78
|
}
|
|
41
79
|
}
|
|
42
80
|
}
|
|
@@ -46,126 +84,116 @@ function getModelKey(providerID, modelID) {
|
|
|
46
84
|
return `${providerID}/${modelID}`;
|
|
47
85
|
}
|
|
48
86
|
function isRateLimitError(error) {
|
|
49
|
-
if (!error)
|
|
87
|
+
if (!error || typeof error !== "object")
|
|
50
88
|
return false;
|
|
51
|
-
|
|
89
|
+
// More type-safe error object structure
|
|
90
|
+
const err = error;
|
|
91
|
+
// Check for 429 status code in APIError
|
|
92
|
+
if (err.name === "APIError" && err.data?.statusCode === 429) {
|
|
52
93
|
return true;
|
|
53
94
|
}
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
"rate_limit",
|
|
60
|
-
"ratelimit",
|
|
61
|
-
"too many requests",
|
|
62
|
-
"quota exceeded",
|
|
63
|
-
"resource exhausted",
|
|
64
|
-
"usage limit",
|
|
65
|
-
"high concurrency usage of this api",
|
|
66
|
-
"high concurrency",
|
|
67
|
-
"reduce concurrency",
|
|
68
|
-
"429",
|
|
69
|
-
];
|
|
70
|
-
return rateLimitIndicators.some((indicator) => responseBody.includes(indicator) ||
|
|
95
|
+
// Type-safe access to error fields
|
|
96
|
+
const responseBody = String(err.data?.responseBody || "").toLowerCase();
|
|
97
|
+
const message = String(err.data?.message || err.message || "").toLowerCase();
|
|
98
|
+
const errorName = String(err.name || "").toLowerCase();
|
|
99
|
+
return RATE_LIMIT_INDICATORS.some((indicator) => responseBody.includes(indicator) ||
|
|
71
100
|
message.includes(indicator) ||
|
|
72
101
|
errorName.includes(indicator));
|
|
73
102
|
}
|
|
103
|
+
// Constants for deduplication and state management
|
|
104
|
+
const DEDUP_WINDOW_MS = 5000;
|
|
105
|
+
const STATE_TIMEOUT_MS = 30000;
|
|
106
|
+
const CLEANUP_INTERVAL_MS = 300000; // 5 minutes
|
|
107
|
+
const SESSION_ENTRY_TTL_MS = 3600000; // 1 hour
|
|
108
|
+
// Track cleanup intervals globally to prevent TypeScript warnings
|
|
109
|
+
const activeCleanupIntervals = [];
|
|
74
110
|
export const RateLimitFallback = async ({ client, directory }) => {
|
|
75
111
|
const config = loadConfig(directory);
|
|
76
|
-
const logFilePath = join(process.env.HOME || "", ".opencode", "rate-limit-fallback-debug.log");
|
|
77
|
-
// Ensure log directory exists
|
|
78
|
-
try {
|
|
79
|
-
mkdirSync(dirname(logFilePath), { recursive: true });
|
|
80
|
-
}
|
|
81
|
-
catch { }
|
|
82
|
-
// Write to file for debugging
|
|
83
|
-
const logToFile = (message) => {
|
|
84
|
-
try {
|
|
85
|
-
appendFileSync(logFilePath, `[${new Date().toISOString()}] ${message}\n`);
|
|
86
|
-
}
|
|
87
|
-
catch { }
|
|
88
|
-
};
|
|
89
|
-
logToFile(`Plugin loaded, config: ${JSON.stringify(config)}`);
|
|
90
|
-
console.log("[rate-limit-fallback] Plugin loaded, config:", config);
|
|
91
|
-
// Use client.app.log for better logging in both run and TUI modes
|
|
92
|
-
try {
|
|
93
|
-
await client.app.log({
|
|
94
|
-
body: {
|
|
95
|
-
service: "rate-limit-fallback",
|
|
96
|
-
level: "info",
|
|
97
|
-
message: `Plugin loaded, config: ${JSON.stringify(config)}`,
|
|
98
|
-
},
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
catch (e) {
|
|
102
|
-
logToFile(`client.app.log failed: ${e}`);
|
|
103
|
-
console.log("[rate-limit-fallback] Plugin loaded, config:", config);
|
|
104
|
-
}
|
|
105
112
|
if (!config.enabled) {
|
|
106
|
-
logToFile("Plugin disabled, returning empty object");
|
|
107
|
-
try {
|
|
108
|
-
await client.app.log({
|
|
109
|
-
body: {
|
|
110
|
-
service: "rate-limit-fallback",
|
|
111
|
-
level: "info",
|
|
112
|
-
message: "Plugin disabled, returning empty object",
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
console.log("[rate-limit-fallback] Plugin disabled, returning empty object");
|
|
118
|
-
}
|
|
119
113
|
return {};
|
|
120
114
|
}
|
|
121
115
|
const rateLimitedModels = new Map();
|
|
122
116
|
const retryState = new Map();
|
|
123
117
|
const currentSessionModel = new Map();
|
|
124
118
|
const fallbackInProgress = new Map(); // sessionID -> timestamp
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
119
|
+
// Subagent session tracking
|
|
120
|
+
const sessionHierarchies = new Map(); // rootSessionID -> SessionHierarchy
|
|
121
|
+
const sessionToRootMap = new Map(); // sessionID -> rootSessionID
|
|
122
|
+
const maxSubagentDepth = config.maxSubagentDepth ?? 10;
|
|
123
|
+
// Helper functions for session hierarchy management
|
|
124
|
+
function getOrCreateHierarchy(rootSessionID) {
|
|
125
|
+
let hierarchy = sessionHierarchies.get(rootSessionID);
|
|
126
|
+
if (!hierarchy) {
|
|
127
|
+
hierarchy = {
|
|
128
|
+
rootSessionID,
|
|
129
|
+
subagents: new Map(),
|
|
130
|
+
sharedFallbackState: "none",
|
|
131
|
+
sharedConfig: config,
|
|
132
|
+
createdAt: Date.now(),
|
|
133
|
+
lastActivity: Date.now(),
|
|
137
134
|
};
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
service: "rate-limit-fallback",
|
|
141
|
-
level: variantMap[variant],
|
|
142
|
-
message,
|
|
143
|
-
},
|
|
144
|
-
});
|
|
135
|
+
sessionHierarchies.set(rootSessionID, hierarchy);
|
|
136
|
+
sessionToRootMap.set(rootSessionID, rootSessionID);
|
|
145
137
|
}
|
|
138
|
+
return hierarchy;
|
|
146
139
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
body: {
|
|
162
|
-
service: "rate-limit-fallback",
|
|
163
|
-
level: variantMap[variant],
|
|
164
|
-
message: `${title}: ${message}`,
|
|
165
|
-
},
|
|
166
|
-
});
|
|
140
|
+
function registerSubagent(sessionID, parentSessionID) {
|
|
141
|
+
// Validate parent session exists
|
|
142
|
+
// Parent session must either be registered in sessionToRootMap or be a new root session
|
|
143
|
+
const parentRootSessionID = sessionToRootMap.get(parentSessionID);
|
|
144
|
+
// Determine root session - if parent doesn't exist, treat it as a new root
|
|
145
|
+
const rootSessionID = parentRootSessionID || parentSessionID;
|
|
146
|
+
// If parent is not a subagent but we're treating it as a root, create a hierarchy for it
|
|
147
|
+
// This allows sessions to become roots when their first subagent is registered
|
|
148
|
+
const hierarchy = getOrCreateHierarchy(rootSessionID);
|
|
149
|
+
const parentSubagent = hierarchy.subagents.get(parentSessionID);
|
|
150
|
+
const depth = parentSubagent ? parentSubagent.depth + 1 : 1;
|
|
151
|
+
// Enforce max depth
|
|
152
|
+
if (depth > maxSubagentDepth) {
|
|
153
|
+
return false;
|
|
167
154
|
}
|
|
155
|
+
const subagent = {
|
|
156
|
+
sessionID,
|
|
157
|
+
parentSessionID,
|
|
158
|
+
depth,
|
|
159
|
+
fallbackState: "none",
|
|
160
|
+
createdAt: Date.now(),
|
|
161
|
+
lastActivity: Date.now(),
|
|
162
|
+
};
|
|
163
|
+
hierarchy.subagents.set(sessionID, subagent);
|
|
164
|
+
sessionToRootMap.set(sessionID, rootSessionID);
|
|
165
|
+
hierarchy.lastActivity = Date.now();
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
function getRootSession(sessionID) {
|
|
169
|
+
return sessionToRootMap.get(sessionID) || null;
|
|
168
170
|
}
|
|
171
|
+
function getHierarchy(sessionID) {
|
|
172
|
+
const rootSessionID = getRootSession(sessionID);
|
|
173
|
+
return rootSessionID ? sessionHierarchies.get(rootSessionID) || null : null;
|
|
174
|
+
}
|
|
175
|
+
// Cleanup stale session model entries (every 5 minutes)
|
|
176
|
+
const cleanupInterval = setInterval(() => {
|
|
177
|
+
const now = Date.now();
|
|
178
|
+
for (const [sessionID, entry] of currentSessionModel.entries()) {
|
|
179
|
+
// Remove entries older than 1 hour
|
|
180
|
+
if (now - entry.lastUpdated > SESSION_ENTRY_TTL_MS) {
|
|
181
|
+
currentSessionModel.delete(sessionID);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Clean up stale session hierarchies
|
|
185
|
+
for (const [rootSessionID, hierarchy] of sessionHierarchies.entries()) {
|
|
186
|
+
if (now - hierarchy.lastActivity > SESSION_ENTRY_TTL_MS) {
|
|
187
|
+
// Clean up all subagents in this hierarchy
|
|
188
|
+
for (const subagentID of hierarchy.subagents.keys()) {
|
|
189
|
+
sessionToRootMap.delete(subagentID);
|
|
190
|
+
}
|
|
191
|
+
sessionHierarchies.delete(rootSessionID);
|
|
192
|
+
sessionToRootMap.delete(rootSessionID);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}, CLEANUP_INTERVAL_MS);
|
|
196
|
+
activeCleanupIntervals.push(cleanupInterval);
|
|
169
197
|
function isModelRateLimited(providerID, modelID) {
|
|
170
198
|
const key = getModelKey(providerID, modelID);
|
|
171
199
|
const limitedAt = rateLimitedModels.get(key);
|
|
@@ -183,29 +211,17 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
183
211
|
}
|
|
184
212
|
function findNextAvailableModel(currentProviderID, currentModelID, attemptedModels) {
|
|
185
213
|
const currentKey = getModelKey(currentProviderID, currentModelID);
|
|
186
|
-
|
|
187
|
-
// If current model is not in the fallback list,
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
for (let i = 0; i < config.fallbackModels.length; i++) {
|
|
191
|
-
const model = config.fallbackModels[i];
|
|
192
|
-
const key = getModelKey(model.providerID, model.modelID);
|
|
193
|
-
if (!attemptedModels.has(key) && !isModelRateLimited(model.providerID, model.modelID)) {
|
|
194
|
-
return model;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
// Search for the next model after current position
|
|
200
|
-
for (let i = startIndex + 1; i < config.fallbackModels.length; i++) {
|
|
214
|
+
const startIndex = config.fallbackModels.findIndex(m => getModelKey(m.providerID, m.modelID) === currentKey);
|
|
215
|
+
// If current model is not in the fallback list (startIndex is -1), start from 0
|
|
216
|
+
const searchStartIndex = Math.max(0, startIndex);
|
|
217
|
+
for (let i = searchStartIndex + 1; i < config.fallbackModels.length; i++) {
|
|
201
218
|
const model = config.fallbackModels[i];
|
|
202
219
|
const key = getModelKey(model.providerID, model.modelID);
|
|
203
220
|
if (!attemptedModels.has(key) && !isModelRateLimited(model.providerID, model.modelID)) {
|
|
204
221
|
return model;
|
|
205
222
|
}
|
|
206
223
|
}
|
|
207
|
-
|
|
208
|
-
for (let i = 0; i <= startIndex && i < config.fallbackModels.length; i++) {
|
|
224
|
+
for (let i = 0; i <= searchStartIndex && i < config.fallbackModels.length; i++) {
|
|
209
225
|
const model = config.fallbackModels[i];
|
|
210
226
|
const key = getModelKey(model.providerID, model.modelID);
|
|
211
227
|
if (!attemptedModels.has(key) && !isModelRateLimited(model.providerID, model.modelID)) {
|
|
@@ -214,38 +230,77 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
214
230
|
}
|
|
215
231
|
return null;
|
|
216
232
|
}
|
|
217
|
-
async function handleRateLimitFallback(sessionID, currentProviderID, currentModelID
|
|
233
|
+
async function handleRateLimitFallback(sessionID, currentProviderID, currentModelID) {
|
|
234
|
+
// Determine the target session ID for fallback processing
|
|
235
|
+
// For subagent sessions, trigger fallback at the root level (parent-centered approach)
|
|
236
|
+
let targetSessionID = sessionID;
|
|
218
237
|
try {
|
|
219
|
-
//
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
238
|
+
// Check if this is a subagent session
|
|
239
|
+
const hierarchy = getHierarchy(sessionID);
|
|
240
|
+
const rootSessionID = getRootSession(sessionID);
|
|
241
|
+
if (rootSessionID && hierarchy) {
|
|
242
|
+
targetSessionID = rootSessionID;
|
|
243
|
+
// If already processing fallback for this hierarchy, skip
|
|
244
|
+
const lastFallback = fallbackInProgress.get(targetSessionID);
|
|
245
|
+
if (lastFallback && Date.now() - lastFallback < DEDUP_WINDOW_MS) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
fallbackInProgress.set(targetSessionID, Date.now());
|
|
249
|
+
// Update the shared fallback state
|
|
250
|
+
hierarchy.sharedFallbackState = "in_progress";
|
|
251
|
+
hierarchy.lastActivity = Date.now();
|
|
252
|
+
// Update the subagent's state
|
|
253
|
+
const subagent = hierarchy.subagents.get(sessionID);
|
|
254
|
+
if (subagent) {
|
|
255
|
+
subagent.fallbackState = "in_progress";
|
|
256
|
+
subagent.lastActivity = Date.now();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
// Prevent duplicate fallback processing within DEDUP_WINDOW_MS for non-subagent sessions
|
|
261
|
+
const lastFallback = fallbackInProgress.get(targetSessionID);
|
|
262
|
+
if (lastFallback && Date.now() - lastFallback < DEDUP_WINDOW_MS) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
fallbackInProgress.set(targetSessionID, Date.now());
|
|
223
266
|
}
|
|
224
|
-
fallbackInProgress.set(sessionID, Date.now());
|
|
225
267
|
// If no model info provided, try to get from tracked session model
|
|
226
268
|
if (!currentProviderID || !currentModelID) {
|
|
227
|
-
const tracked = currentSessionModel.get(
|
|
269
|
+
const tracked = currentSessionModel.get(targetSessionID);
|
|
228
270
|
if (tracked) {
|
|
229
271
|
currentProviderID = tracked.providerID;
|
|
230
272
|
currentModelID = tracked.modelID;
|
|
231
273
|
}
|
|
232
274
|
}
|
|
233
|
-
//
|
|
234
|
-
|
|
275
|
+
// Abort current session with error handling
|
|
276
|
+
try {
|
|
277
|
+
await client.session.abort({ path: { id: targetSessionID } });
|
|
278
|
+
}
|
|
279
|
+
catch (abortError) {
|
|
280
|
+
// Silently ignore abort errors and continue with fallback
|
|
281
|
+
}
|
|
282
|
+
await client.tui.showToast({
|
|
283
|
+
body: {
|
|
284
|
+
title: "Rate Limit Detected",
|
|
285
|
+
message: `Switching from ${currentModelID || 'current model'}...`,
|
|
286
|
+
variant: "warning",
|
|
287
|
+
duration: 3000,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
const messagesResult = await client.session.messages({ path: { id: targetSessionID } });
|
|
235
291
|
if (!messagesResult.data) {
|
|
236
|
-
fallbackInProgress.delete(
|
|
292
|
+
fallbackInProgress.delete(targetSessionID);
|
|
237
293
|
return;
|
|
238
294
|
}
|
|
239
295
|
const messages = messagesResult.data;
|
|
240
296
|
const lastUserMessage = [...messages].reverse().find(m => m.info.role === "user");
|
|
241
297
|
if (!lastUserMessage) {
|
|
242
|
-
fallbackInProgress.delete(
|
|
298
|
+
fallbackInProgress.delete(targetSessionID);
|
|
243
299
|
return;
|
|
244
300
|
}
|
|
245
|
-
toast("Rate Limit Detected", `Switching from ${currentModelID || 'current model'}...`, "warning").catch(() => { });
|
|
246
301
|
const stateKey = `${sessionID}:${lastUserMessage.info.id}`;
|
|
247
302
|
let state = retryState.get(stateKey);
|
|
248
|
-
if (!state || Date.now() - state.lastAttemptTime >
|
|
303
|
+
if (!state || Date.now() - state.lastAttemptTime > STATE_TIMEOUT_MS) {
|
|
249
304
|
state = { attemptedModels: new Set(), lastAttemptTime: Date.now() };
|
|
250
305
|
retryState.set(stateKey, state);
|
|
251
306
|
}
|
|
@@ -272,7 +327,14 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
272
327
|
if (!isLastModelCurrent && !isModelRateLimited(lastModel.providerID, lastModel.modelID)) {
|
|
273
328
|
// Use the last model for one more try
|
|
274
329
|
nextModel = lastModel;
|
|
275
|
-
|
|
330
|
+
await client.tui.showToast({
|
|
331
|
+
body: {
|
|
332
|
+
title: "Last Resort",
|
|
333
|
+
message: `Trying ${lastModel.modelID} one more time...`,
|
|
334
|
+
variant: "warning",
|
|
335
|
+
duration: 3000,
|
|
336
|
+
},
|
|
337
|
+
});
|
|
276
338
|
}
|
|
277
339
|
else {
|
|
278
340
|
// Last model also failed, reset for next prompt
|
|
@@ -287,221 +349,160 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
287
349
|
// "stop" mode: nextModel remains null, will show error below
|
|
288
350
|
}
|
|
289
351
|
if (!nextModel) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
352
|
+
await client.tui.showToast({
|
|
353
|
+
body: {
|
|
354
|
+
title: "No Fallback Available",
|
|
355
|
+
message: config.fallbackMode === "stop"
|
|
356
|
+
? "All fallback models exhausted"
|
|
357
|
+
: "All models are rate limited",
|
|
358
|
+
variant: "error",
|
|
359
|
+
duration: 5000,
|
|
360
|
+
},
|
|
361
|
+
});
|
|
293
362
|
retryState.delete(stateKey);
|
|
294
|
-
fallbackInProgress.delete(
|
|
363
|
+
fallbackInProgress.delete(targetSessionID);
|
|
295
364
|
return;
|
|
296
365
|
}
|
|
297
366
|
state.attemptedModels.add(getModelKey(nextModel.providerID, nextModel.modelID));
|
|
298
367
|
state.lastAttemptTime = Date.now();
|
|
299
368
|
const parts = lastUserMessage.parts
|
|
300
|
-
.filter((p) =>
|
|
369
|
+
.filter((p) => {
|
|
370
|
+
const part = p;
|
|
371
|
+
return part.type === "text" || part.type === "file";
|
|
372
|
+
})
|
|
301
373
|
.map((p) => {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
374
|
+
const part = p;
|
|
375
|
+
if (part.type === "text")
|
|
376
|
+
return { type: "text", text: String(part.text) };
|
|
377
|
+
if (part.type === "file")
|
|
378
|
+
return { type: "file", path: String(part.path), mediaType: String(part.mediaType) };
|
|
306
379
|
return null;
|
|
307
380
|
})
|
|
308
|
-
.filter(
|
|
381
|
+
.filter((p) => p !== null);
|
|
309
382
|
if (parts.length === 0) {
|
|
310
|
-
fallbackInProgress.delete(
|
|
383
|
+
fallbackInProgress.delete(targetSessionID);
|
|
311
384
|
return;
|
|
312
385
|
}
|
|
386
|
+
await client.tui.showToast({
|
|
387
|
+
body: {
|
|
388
|
+
title: "Retrying",
|
|
389
|
+
message: `Using ${nextModel.providerID}/${nextModel.modelID}`,
|
|
390
|
+
variant: "info",
|
|
391
|
+
duration: 3000,
|
|
392
|
+
},
|
|
393
|
+
});
|
|
313
394
|
// Track the new model for this session
|
|
314
|
-
currentSessionModel.set(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
};
|
|
319
|
-
//
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
logToFile(`abort failed (${Date.now() - t0}ms): ${abortErr}`);
|
|
395
|
+
currentSessionModel.set(targetSessionID, {
|
|
396
|
+
providerID: nextModel.providerID,
|
|
397
|
+
modelID: nextModel.modelID,
|
|
398
|
+
lastUpdated: Date.now(),
|
|
399
|
+
});
|
|
400
|
+
// If this is a root session with subagents, propagate the model to all subagents
|
|
401
|
+
if (hierarchy && hierarchy.rootSessionID === targetSessionID) {
|
|
402
|
+
hierarchy.sharedFallbackState = "completed";
|
|
403
|
+
hierarchy.lastActivity = Date.now();
|
|
404
|
+
// Update model tracking for all subagents
|
|
405
|
+
for (const [subagentID, subagent] of hierarchy.subagents.entries()) {
|
|
406
|
+
currentSessionModel.set(subagentID, {
|
|
407
|
+
providerID: nextModel.providerID,
|
|
408
|
+
modelID: nextModel.modelID,
|
|
409
|
+
lastUpdated: Date.now(),
|
|
410
|
+
});
|
|
411
|
+
subagent.fallbackState = "completed";
|
|
412
|
+
subagent.lastActivity = Date.now();
|
|
333
413
|
}
|
|
334
414
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
415
|
+
// Convert internal MessagePart to SDK-compatible format
|
|
416
|
+
const sdkParts = parts.map((part) => {
|
|
417
|
+
if (part.type === "text") {
|
|
418
|
+
return { type: "text", text: part.text };
|
|
419
|
+
}
|
|
420
|
+
// For file parts, we need to match the FilePartInput format
|
|
421
|
+
// Using path as url since we're dealing with local files
|
|
422
|
+
return {
|
|
423
|
+
type: "file",
|
|
424
|
+
url: part.path,
|
|
425
|
+
mime: part.mediaType || "application/octet-stream",
|
|
426
|
+
};
|
|
427
|
+
});
|
|
428
|
+
await client.session.prompt({
|
|
429
|
+
path: { id: targetSessionID },
|
|
430
|
+
body: {
|
|
431
|
+
parts: sdkParts,
|
|
432
|
+
model: { providerID: nextModel.providerID, modelID: nextModel.modelID },
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
await client.tui.showToast({
|
|
436
|
+
body: {
|
|
437
|
+
title: "Fallback Successful",
|
|
438
|
+
message: `Now using ${nextModel.modelID}`,
|
|
439
|
+
variant: "success",
|
|
440
|
+
duration: 3000,
|
|
441
|
+
},
|
|
339
442
|
});
|
|
340
|
-
logToFile(`promptAsync completed for session ${sessionID} (${Date.now() - t1}ms, total ${Date.now() - t0}ms) with model ${nextModel.providerID}/${nextModel.modelID}`);
|
|
341
|
-
// Toast is best-effort notification. The toast() function (line ~185) has
|
|
342
|
-
// built-in fallback: showToast failure → app.log. After promptAsync the
|
|
343
|
-
// server may already be disposing, so both showToast and app.log could fail.
|
|
344
|
-
// The outer .catch() ensures even total toast() failure never blocks or throws.
|
|
345
|
-
toast("Fallback Active", `Now using ${nextModel.modelID}`, "success").catch(() => { });
|
|
346
443
|
retryState.delete(stateKey);
|
|
347
|
-
//
|
|
348
|
-
|
|
444
|
+
// Explicitly clean up fallbackInProgress after cooldown period
|
|
445
|
+
// This prevents memory leaks while maintaining the deduplication window
|
|
446
|
+
setTimeout(() => {
|
|
447
|
+
fallbackInProgress.delete(targetSessionID);
|
|
448
|
+
}, DEDUP_WINDOW_MS);
|
|
349
449
|
}
|
|
350
450
|
catch (err) {
|
|
351
|
-
|
|
352
|
-
//
|
|
353
|
-
fallbackInProgress.delete(sessionID);
|
|
451
|
+
fallbackInProgress.delete(targetSessionID);
|
|
452
|
+
// Silently ignore fallback errors
|
|
354
453
|
}
|
|
355
454
|
}
|
|
356
455
|
return {
|
|
357
456
|
event: async ({ event }) => {
|
|
358
|
-
|
|
359
|
-
logToFile(`Event received: ${event.type}`);
|
|
360
|
-
try {
|
|
361
|
-
await client.app.log({
|
|
362
|
-
body: {
|
|
363
|
-
service: "rate-limit-fallback",
|
|
364
|
-
level: "debug",
|
|
365
|
-
message: `Event received: ${event.type}`,
|
|
366
|
-
},
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
catch (e) {
|
|
370
|
-
logToFile(`client.app.log failed for event: ${e}`);
|
|
371
|
-
console.log("[rate-limit-fallback] Event received:", event.type);
|
|
372
|
-
}
|
|
373
|
-
if (event.type === "session.error") {
|
|
457
|
+
if (isSessionErrorEvent(event)) {
|
|
374
458
|
const { sessionID, error } = event.properties;
|
|
375
|
-
logToFile(`session.error: sessionID=${sessionID}, error=${JSON.stringify(error)}`);
|
|
376
|
-
try {
|
|
377
|
-
await client.app.log({
|
|
378
|
-
body: {
|
|
379
|
-
service: "rate-limit-fallback",
|
|
380
|
-
level: "debug",
|
|
381
|
-
message: `session.error: sessionID=${sessionID}, error=${JSON.stringify(error)}`,
|
|
382
|
-
},
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
catch {
|
|
386
|
-
console.log("[rate-limit-fallback] session.error:", sessionID, error);
|
|
387
|
-
}
|
|
388
459
|
if (sessionID && error && isRateLimitError(error)) {
|
|
389
|
-
|
|
390
|
-
try {
|
|
391
|
-
await client.app.log({
|
|
392
|
-
body: {
|
|
393
|
-
service: "rate-limit-fallback",
|
|
394
|
-
level: "info",
|
|
395
|
-
message: "Rate limit error detected, attempting fallback",
|
|
396
|
-
},
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
catch {
|
|
400
|
-
console.log("[rate-limit-fallback] Rate limit error detected, attempting fallback");
|
|
401
|
-
}
|
|
402
|
-
await handleRateLimitFallback(sessionID, "", "", true); // skipAbort = true (already in error state)
|
|
460
|
+
await handleRateLimitFallback(sessionID, "", "");
|
|
403
461
|
}
|
|
404
462
|
}
|
|
405
|
-
if (event
|
|
406
|
-
const info = event.properties
|
|
407
|
-
logToFile(`message.updated: ${JSON.stringify(info)}`);
|
|
408
|
-
try {
|
|
409
|
-
await client.app.log({
|
|
410
|
-
body: {
|
|
411
|
-
service: "rate-limit-fallback",
|
|
412
|
-
level: "debug",
|
|
413
|
-
message: `message.updated: ${JSON.stringify(info)}`,
|
|
414
|
-
},
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
catch {
|
|
418
|
-
console.log("[rate-limit-fallback] message.updated:", info);
|
|
419
|
-
}
|
|
420
|
-
// Track assistant message model info for later use in fallback
|
|
421
|
-
if (info?.role === "assistant" && info?.sessionID && info?.providerID && info?.modelID) {
|
|
422
|
-
currentSessionModel.set(info.sessionID, {
|
|
423
|
-
providerID: info.providerID,
|
|
424
|
-
modelID: info.modelID,
|
|
425
|
-
});
|
|
426
|
-
}
|
|
463
|
+
if (isMessageUpdatedEvent(event)) {
|
|
464
|
+
const info = event.properties.info;
|
|
427
465
|
if (info?.error && isRateLimitError(info.error)) {
|
|
428
|
-
|
|
429
|
-
try {
|
|
430
|
-
await client.app.log({
|
|
431
|
-
body: {
|
|
432
|
-
service: "rate-limit-fallback",
|
|
433
|
-
level: "info",
|
|
434
|
-
message: "Rate limit error in message, attempting fallback",
|
|
435
|
-
},
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
catch {
|
|
439
|
-
console.log("[rate-limit-fallback] Rate limit error in message, attempting fallback");
|
|
440
|
-
}
|
|
441
|
-
await handleRateLimitFallback(info.sessionID, info.providerID || "", info.modelID || "", true); // skipAbort = true (already in error state)
|
|
466
|
+
await handleRateLimitFallback(info.sessionID, info.providerID || "", info.modelID || "");
|
|
442
467
|
}
|
|
443
468
|
}
|
|
444
|
-
if (event
|
|
469
|
+
if (isSessionStatusEvent(event)) {
|
|
445
470
|
const props = event.properties;
|
|
446
471
|
const status = props?.status;
|
|
447
|
-
logToFile(`session.status: ${JSON.stringify(status)}`);
|
|
448
|
-
try {
|
|
449
|
-
await client.app.log({
|
|
450
|
-
body: {
|
|
451
|
-
service: "rate-limit-fallback",
|
|
452
|
-
level: "debug",
|
|
453
|
-
message: `session.status: ${JSON.stringify(status)}`,
|
|
454
|
-
},
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
catch {
|
|
458
|
-
console.log("[rate-limit-fallback] session.status:", status);
|
|
459
|
-
}
|
|
460
|
-
// Note: interrupted status is ignored here. Since we no longer call abort
|
|
461
|
-
// for retry status events, interrupted events should not be triggered by
|
|
462
|
-
// our fallback logic. If they occur (e.g., from user action), we let them
|
|
463
|
-
// be handled by the system.
|
|
464
472
|
if (status?.type === "retry" && status?.message) {
|
|
465
473
|
const message = status.message.toLowerCase();
|
|
466
474
|
const isRateLimitRetry = message.includes("usage limit") ||
|
|
467
475
|
message.includes("rate limit") ||
|
|
468
476
|
message.includes("high concurrency") ||
|
|
469
477
|
message.includes("reduce concurrency");
|
|
470
|
-
logToFile(`Is rate limit retry: ${isRateLimitRetry}, message: ${message}`);
|
|
471
|
-
try {
|
|
472
|
-
await client.app.log({
|
|
473
|
-
body: {
|
|
474
|
-
service: "rate-limit-fallback",
|
|
475
|
-
level: "debug",
|
|
476
|
-
message: `Is rate limit retry: ${isRateLimitRetry}, message: ${message}`,
|
|
477
|
-
},
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
catch {
|
|
481
|
-
console.log("[rate-limit-fallback] Is rate limit retry:", isRateLimitRetry, "message:", message);
|
|
482
|
-
}
|
|
483
478
|
if (isRateLimitRetry) {
|
|
484
479
|
// Try fallback on any attempt, handleRateLimitFallback will manage state
|
|
485
|
-
|
|
486
|
-
try {
|
|
487
|
-
await client.app.log({
|
|
488
|
-
body: {
|
|
489
|
-
service: "rate-limit-fallback",
|
|
490
|
-
level: "info",
|
|
491
|
-
message: "Attempting fallback for rate limit retry",
|
|
492
|
-
},
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
catch {
|
|
496
|
-
console.log("[rate-limit-fallback] Attempting fallback for rate limit retry");
|
|
497
|
-
}
|
|
498
|
-
// skipAbort = true for retry status to avoid triggering interrupted events
|
|
499
|
-
// that could cancel the new fallback prompt
|
|
500
|
-
await handleRateLimitFallback(props.sessionID, "", "", true);
|
|
480
|
+
await handleRateLimitFallback(props.sessionID, "", "");
|
|
501
481
|
}
|
|
502
482
|
}
|
|
503
483
|
}
|
|
484
|
+
// Handle subagent session creation events
|
|
485
|
+
// Note: Using type assertion for subagent events since they may not be in the official Event union yet
|
|
486
|
+
const rawEvent = event;
|
|
487
|
+
if (isSubagentSessionCreatedEvent(rawEvent)) {
|
|
488
|
+
const { sessionID, parentSessionID } = rawEvent.properties;
|
|
489
|
+
if (config.enableSubagentFallback !== false) {
|
|
490
|
+
registerSubagent(sessionID, parentSessionID);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
// Cleanup function to prevent memory leaks
|
|
495
|
+
cleanup: () => {
|
|
496
|
+
clearInterval(cleanupInterval);
|
|
497
|
+
const index = activeCleanupIntervals.indexOf(cleanupInterval);
|
|
498
|
+
if (index > -1) {
|
|
499
|
+
activeCleanupIntervals.splice(index, 1);
|
|
500
|
+
}
|
|
501
|
+
// Clean up all session hierarchies
|
|
502
|
+
sessionHierarchies.clear();
|
|
503
|
+
sessionToRootMap.clear();
|
|
504
504
|
},
|
|
505
505
|
};
|
|
506
506
|
};
|
|
507
507
|
export default RateLimitFallback;
|
|
508
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azumag/opencode-rate-limit-fallback",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "OpenCode plugin that automatically switches to fallback models when rate limited",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
5
7
|
"type": "module",
|
|
6
|
-
"main": "./dist/index.js",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"build": "tsc",
|
|
9
|
-
"test": "vitest",
|
|
10
|
-
"prepublishOnly": "npm run build"
|
|
11
|
-
},
|
|
12
8
|
"keywords": [
|
|
13
9
|
"opencode",
|
|
14
10
|
"plugin",
|
|
@@ -26,22 +22,27 @@
|
|
|
26
22
|
"url": "https://github.com/azumag/opencode-rate-limit-fallback/issues"
|
|
27
23
|
},
|
|
28
24
|
"homepage": "https://github.com/azumag/opencode-rate-limit-fallback#readme",
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"clean": "rm -rf dist",
|
|
29
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"test:coverage": "vitest run --coverage"
|
|
33
|
+
},
|
|
29
34
|
"files": [
|
|
30
|
-
"dist"
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE"
|
|
31
38
|
],
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^25.2.2",
|
|
41
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
42
|
+
"typescript": "^5.3.0",
|
|
43
|
+
"vitest": "^4.0.18"
|
|
37
44
|
},
|
|
38
45
|
"dependencies": {
|
|
39
46
|
"@opencode-ai/plugin": "latest"
|
|
40
|
-
},
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@tsconfig/node22": "^22.0.5",
|
|
43
|
-
"@types/node": "^25.2.2",
|
|
44
|
-
"typescript": "^5.9.3",
|
|
45
|
-
"vitest": "^3.2.4"
|
|
46
47
|
}
|
|
47
48
|
}
|