@azumag/opencode-rate-limit-fallback 1.0.10 → 1.0.11
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/dist/index.js +42 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -225,8 +225,7 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
225
225
|
currentModelID = tracked.modelID;
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
|
-
|
|
229
|
-
await toast("Rate Limit Detected", `Switching from ${currentModelID || 'current model'}...`, "warning");
|
|
228
|
+
// Fetch messages BEFORE abort — session must still be alive
|
|
230
229
|
const messagesResult = await client.session.messages({ path: { id: sessionID } });
|
|
231
230
|
if (!messagesResult.data)
|
|
232
231
|
return;
|
|
@@ -234,6 +233,7 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
234
233
|
const lastUserMessage = [...messages].reverse().find(m => m.info.role === "user");
|
|
235
234
|
if (!lastUserMessage)
|
|
236
235
|
return;
|
|
236
|
+
toast("Rate Limit Detected", `Switching from ${currentModelID || 'current model'}...`, "warning").catch(() => { });
|
|
237
237
|
const stateKey = `${sessionID}:${lastUserMessage.info.id}`;
|
|
238
238
|
let state = retryState.get(stateKey);
|
|
239
239
|
if (!state || Date.now() - state.lastAttemptTime > 30000) {
|
|
@@ -259,12 +259,11 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
259
259
|
// Try the last model in the list once, then reset on next prompt
|
|
260
260
|
const lastModel = config.fallbackModels[config.fallbackModels.length - 1];
|
|
261
261
|
if (lastModel) {
|
|
262
|
-
const lastKey = getModelKey(lastModel.providerID, lastModel.modelID);
|
|
263
262
|
const isLastModelCurrent = currentProviderID === lastModel.providerID && currentModelID === lastModel.modelID;
|
|
264
263
|
if (!isLastModelCurrent && !isModelRateLimited(lastModel.providerID, lastModel.modelID)) {
|
|
265
264
|
// Use the last model for one more try
|
|
266
265
|
nextModel = lastModel;
|
|
267
|
-
|
|
266
|
+
toast("Last Resort", `Trying ${lastModel.modelID} one more time...`, "warning").catch(() => { });
|
|
268
267
|
}
|
|
269
268
|
else {
|
|
270
269
|
// Last model also failed, reset for next prompt
|
|
@@ -279,9 +278,9 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
279
278
|
// "stop" mode: nextModel remains null, will show error below
|
|
280
279
|
}
|
|
281
280
|
if (!nextModel) {
|
|
282
|
-
|
|
281
|
+
toast("No Fallback Available", config.fallbackMode === "stop"
|
|
283
282
|
? "All fallback models exhausted"
|
|
284
|
-
: "All models are rate limited", "error");
|
|
283
|
+
: "All models are rate limited", "error").catch(() => { });
|
|
285
284
|
retryState.delete(stateKey);
|
|
286
285
|
fallbackInProgress.delete(sessionID);
|
|
287
286
|
return;
|
|
@@ -300,22 +299,43 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
300
299
|
.filter(Boolean);
|
|
301
300
|
if (parts.length === 0)
|
|
302
301
|
return;
|
|
303
|
-
|
|
302
|
+
toast("Retrying", `Using ${nextModel.providerID}/${nextModel.modelID}`, "info").catch(() => { });
|
|
304
303
|
// Track the new model for this session
|
|
305
304
|
currentSessionModel.set(sessionID, { providerID: nextModel.providerID, modelID: nextModel.modelID });
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
305
|
+
const promptBody = {
|
|
306
|
+
parts: parts,
|
|
307
|
+
model: { providerID: nextModel.providerID, modelID: nextModel.modelID },
|
|
308
|
+
};
|
|
309
|
+
// Try promptAsync first (no abort needed — works in both TUI and run modes)
|
|
310
|
+
try {
|
|
311
|
+
await client.session.promptAsync({
|
|
312
|
+
path: { id: sessionID },
|
|
313
|
+
body: promptBody,
|
|
314
|
+
});
|
|
315
|
+
logToFile(`promptAsync sent successfully for session ${sessionID} with model ${nextModel.providerID}/${nextModel.modelID}`);
|
|
316
|
+
}
|
|
317
|
+
catch (promptAsyncErr) {
|
|
318
|
+
// promptAsync failed — fall back to abort + promptAsync
|
|
319
|
+
logToFile(`promptAsync failed (${promptAsyncErr}), falling back to abort + promptAsync`);
|
|
320
|
+
try {
|
|
321
|
+
await client.session.abort({ path: { id: sessionID } });
|
|
322
|
+
}
|
|
323
|
+
catch (abortErr) {
|
|
324
|
+
logToFile(`abort also failed: ${abortErr}`);
|
|
325
|
+
}
|
|
326
|
+
await client.session.promptAsync({
|
|
327
|
+
path: { id: sessionID },
|
|
328
|
+
body: promptBody,
|
|
329
|
+
});
|
|
330
|
+
logToFile(`abort + promptAsync sent successfully for session ${sessionID}`);
|
|
331
|
+
}
|
|
332
|
+
toast("Fallback Successful", `Now using ${nextModel.modelID}`, "success").catch(() => { });
|
|
314
333
|
retryState.delete(stateKey);
|
|
315
334
|
// Clear fallback flag to allow next fallback if needed
|
|
316
335
|
fallbackInProgress.delete(sessionID);
|
|
317
336
|
}
|
|
318
337
|
catch (err) {
|
|
338
|
+
logToFile(`handleRateLimitFallback error: ${err}`);
|
|
319
339
|
// Fallback failed, clear the flag
|
|
320
340
|
fallbackInProgress.delete(sessionID);
|
|
321
341
|
}
|
|
@@ -384,6 +404,13 @@ export const RateLimitFallback = async ({ client, directory }) => {
|
|
|
384
404
|
catch {
|
|
385
405
|
console.log("[rate-limit-fallback] message.updated:", info);
|
|
386
406
|
}
|
|
407
|
+
// Track assistant message model info for later use in fallback
|
|
408
|
+
if (info?.role === "assistant" && info?.sessionID && info?.providerID && info?.modelID) {
|
|
409
|
+
currentSessionModel.set(info.sessionID, {
|
|
410
|
+
providerID: info.providerID,
|
|
411
|
+
modelID: info.modelID,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
387
414
|
if (info?.error && isRateLimitError(info.error)) {
|
|
388
415
|
logToFile("Rate limit error in message, attempting fallback");
|
|
389
416
|
try {
|
package/package.json
CHANGED