@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.
Files changed (2) hide show
  1. package/dist/index.js +42 -15
  2. 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
- await client.session.abort({ path: { id: sessionID } });
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
- await toast("Last Resort", `Trying ${lastModel.modelID} one more time...`, "warning");
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
- await toast("No Fallback Available", config.fallbackMode === "stop"
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
- await toast("Retrying", `Using ${nextModel.providerID}/${nextModel.modelID}`, "info");
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
- await client.session.prompt({
307
- path: { id: sessionID },
308
- body: {
309
- parts: parts,
310
- model: { providerID: nextModel.providerID, modelID: nextModel.modelID },
311
- },
312
- });
313
- await toast("Fallback Successful", `Now using ${nextModel.modelID}`, "success");
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azumag/opencode-rate-limit-fallback",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "OpenCode plugin that automatically switches to fallback models when rate limited",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",