Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions open-sse/executors/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,36 @@ export class DefaultExecutor extends BaseExecutor {
return injectReasoningContent({ provider: this.provider, model, body });
}

/**
* Override parseError to extract precise resetsAtMs from MiniMax 429 errors.
* MiniMax returns: { error: { message: "...", retryAfter: "2025-05-02T12:00:00Z", ... } }
* Without this, the base class returns no resetsAtMs → exponential backoff instead of precise wait.
*/
parseError(response, bodyText) {
if (response.status === 429 && bodyText) {
try {
const json = JSON.parse(bodyText);
const err = json?.error || json;
const retryAfter = err?.retryAfter;
if (retryAfter) {
const ms = new Date(retryAfter).getTime();
const now = Date.now();
if (ms > now) {
return { status: 429, message: err.message || bodyText, resetsAtMs: ms };
}
}
const retryMs = err?.retry_after_ms || err?.retryAfterMs;
if (typeof retryMs === "number" && retryMs > 0) {
const resetsAtMs = now + retryMs;
if (resetsAtMs > now) {
return { status: 429, message: err.message || bodyText, resetsAtMs };
}
}
} catch { /* fall through to default */ }
}
return super.parseError(response, bodyText);
}

buildUrl(model, stream, urlIndex = 0, credentials = null) {
if (this.provider?.startsWith?.("openai-compatible-")) {
const baseUrl = credentials?.providerSpecificData?.baseUrl || "https://api.openai.com/v1";
Expand Down
31 changes: 31 additions & 0 deletions open-sse/executors/opencode-go.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,35 @@ export class OpenCodeGoExecutor extends BaseExecutor {
transformRequest(model, body) {
return injectReasoningContent({ provider: this.provider, model, body });
}

/**
* Override parseError to extract precise resetsAtMs from MiniMax 429 errors.
* MiniMax returns: { error: { message: "...", retryAfter: "2025-05-02T12:00:00Z", ... } }
* Without this, the base class returns no resetsAtMs → exponential backoff instead of precise wait.
*/
parseError(response, bodyText) {
if (response.status === 429 && bodyText) {
try {
const json = JSON.parse(bodyText);
const err = json?.error || json;
const retryAfter = err?.retryAfter;
if (retryAfter) {
const ms = new Date(retryAfter).getTime();
const now = Date.now();
if (ms > now) {
return { status: 429, message: err.message || bodyText, resetsAtMs: ms };
}
}
// Also accept retry_after_ms as numeric milliseconds (some providers use this)
const retryMs = err?.retry_after_ms || err?.retryAfterMs;
if (typeof retryMs === "number" && retryMs > 0) {
const resetsAtMs = now + retryMs;
if (resetsAtMs > now) {
return { status: 429, message: err.message || bodyText, resetsAtMs };
}
}
} catch { /* fall through to default */ }
}
return super.parseError(response, bodyText);
}
}