@azumag/opencode-rate-limit-fallback 1.0.20 → 1.0.22
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 +6 -8
- package/dist/index.js +20 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,16 +89,14 @@ If no configuration is provided, the following models are used:
|
|
|
89
89
|
|
|
90
90
|
## How It Works
|
|
91
91
|
|
|
92
|
-
1. **Detection**: The plugin listens for rate limit errors via:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
1. **Detection**: The plugin listens for rate limit errors via:
|
|
93
|
+
- `session.error` events
|
|
94
|
+
- `message.updated` events with errors
|
|
95
|
+
- `session.status` events with `type: "retry"`
|
|
96
96
|
|
|
97
|
-
2. **
|
|
97
|
+
2. **Fallback**: The plugin selects the next available model from the fallback list and resends the last user message using the `promptAsync` API.
|
|
98
98
|
|
|
99
|
-
3. **
|
|
100
|
-
|
|
101
|
-
4. **Cooldown**: Rate-limited models are tracked and skipped for the configured cooldown period.
|
|
99
|
+
3. **Cooldown**: Rate-limited models are tracked and skipped for the configured cooldown period.
|
|
102
100
|
|
|
103
101
|
## License
|
|
104
102
|
|
package/dist/index.js
CHANGED
|
@@ -316,29 +316,28 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
316
316
|
parts: parts,
|
|
317
317
|
model: { providerID: nextModel.providerID, modelID: nextModel.modelID },
|
|
318
318
|
};
|
|
319
|
-
// promptAsync → abort: send fallback prompt first, then abort the old request
|
|
320
|
-
// This prevents the abort from killing the new fallback prompt.
|
|
321
|
-
//
|
|
322
|
-
// For session.error events, the request is already in error state, so we
|
|
323
|
-
// only send promptAsync (skipAbort = true). For retry status, we send both.
|
|
324
|
-
//
|
|
325
319
|
// promptAsync: HTTP POST /session/{id}/prompt_async → 204 (SDK sdk.gen.js).
|
|
320
|
+
//
|
|
321
|
+
// For session.error and retry status events, we skip abort (skipAbort = true)
|
|
322
|
+
// because the request is already in an error or delayed state. Calling abort
|
|
323
|
+
// in these cases can trigger interrupted events that cancel the new fallback
|
|
324
|
+
// prompt.
|
|
326
325
|
const t0 = Date.now();
|
|
327
|
-
await client.session.promptAsync({
|
|
328
|
-
path: { id: sessionID },
|
|
329
|
-
body: promptBody,
|
|
330
|
-
});
|
|
331
|
-
const t1 = Date.now();
|
|
332
326
|
if (!skipAbort) {
|
|
333
327
|
try {
|
|
334
328
|
await client.session.abort({ path: { id: sessionID } });
|
|
335
|
-
logToFile(`abort succeeded for session ${sessionID} (${Date.now() -
|
|
329
|
+
logToFile(`abort succeeded for session ${sessionID} (${Date.now() - t0}ms)`);
|
|
336
330
|
}
|
|
337
331
|
catch (abortErr) {
|
|
338
|
-
logToFile(`abort failed (${Date.now() -
|
|
332
|
+
logToFile(`abort failed (${Date.now() - t0}ms): ${abortErr}`);
|
|
339
333
|
}
|
|
340
334
|
}
|
|
341
|
-
|
|
335
|
+
const t1 = Date.now();
|
|
336
|
+
await client.session.promptAsync({
|
|
337
|
+
path: { id: sessionID },
|
|
338
|
+
body: promptBody,
|
|
339
|
+
});
|
|
340
|
+
logToFile(`promptAsync completed for session ${sessionID} (${Date.now() - t1}ms, total ${Date.now() - t0}ms) with model ${nextModel.providerID}/${nextModel.modelID}`);
|
|
342
341
|
// Toast is best-effort notification. The toast() function (line ~185) has
|
|
343
342
|
// built-in fallback: showToast failure → app.log. After promptAsync the
|
|
344
343
|
// server may already be disposing, so both showToast and app.log could fail.
|
|
@@ -458,23 +457,10 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
458
457
|
catch {
|
|
459
458
|
console.log("[rate-limit-fallback] session.status:", status);
|
|
460
459
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
await client.app.log({
|
|
466
|
-
body: {
|
|
467
|
-
service: "rate-limit-fallback",
|
|
468
|
-
level: "info",
|
|
469
|
-
message: "Session interrupted, attempting to resend prompt",
|
|
470
|
-
},
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
catch {
|
|
474
|
-
console.log("[rate-limit-fallback] Session interrupted, attempting to resend prompt");
|
|
475
|
-
}
|
|
476
|
-
await handleRateLimitFallback(props.sessionID, "", "", true); // skipAbort = true
|
|
477
|
-
}
|
|
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.
|
|
478
464
|
if (status?.type === "retry" && status?.message) {
|
|
479
465
|
const message = status.message.toLowerCase();
|
|
480
466
|
const isRateLimitRetry = message.includes("usage limit") ||
|
|
@@ -509,7 +495,9 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
509
495
|
catch {
|
|
510
496
|
console.log("[rate-limit-fallback] Attempting fallback for rate limit retry");
|
|
511
497
|
}
|
|
512
|
-
|
|
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);
|
|
513
501
|
}
|
|
514
502
|
}
|
|
515
503
|
}
|
package/package.json
CHANGED