@ubiquity-os/plugin-sdk 3.11.2 → 3.12.0
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/helpers.d.mts +2 -1
- package/dist/helpers.d.ts +2 -1
- package/dist/helpers.js +141 -18
- package/dist/helpers.mjs +130 -18
- package/package.json +2 -1
package/dist/helpers.d.mts
CHANGED
|
@@ -55,5 +55,6 @@ interface RetryOptions {
|
|
|
55
55
|
isErrorRetryable?: (error: unknown) => Promise<boolean | number> | boolean | number;
|
|
56
56
|
}
|
|
57
57
|
declare function retry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;
|
|
58
|
+
declare function checkLlmRetryableState(error: unknown): boolean | number;
|
|
58
59
|
|
|
59
|
-
export { type OpenRouterError, type OpenRouterModel, getOpenRouterModelTokenLimits, getOpenRouterModels, retry };
|
|
60
|
+
export { type OpenRouterError, type OpenRouterModel, checkLlmRetryableState, getOpenRouterModelTokenLimits, getOpenRouterModels, retry };
|
package/dist/helpers.d.ts
CHANGED
|
@@ -55,5 +55,6 @@ interface RetryOptions {
|
|
|
55
55
|
isErrorRetryable?: (error: unknown) => Promise<boolean | number> | boolean | number;
|
|
56
56
|
}
|
|
57
57
|
declare function retry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;
|
|
58
|
+
declare function checkLlmRetryableState(error: unknown): boolean | number;
|
|
58
59
|
|
|
59
|
-
export { type OpenRouterError, type OpenRouterModel, getOpenRouterModelTokenLimits, getOpenRouterModels, retry };
|
|
60
|
+
export { type OpenRouterError, type OpenRouterModel, checkLlmRetryableState, getOpenRouterModelTokenLimits, getOpenRouterModels, retry };
|
package/dist/helpers.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,11 +17,20 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/helpers/index.ts
|
|
21
31
|
var helpers_exports = {};
|
|
22
32
|
__export(helpers_exports, {
|
|
33
|
+
checkLlmRetryableState: () => checkLlmRetryableState,
|
|
23
34
|
getOpenRouterModelTokenLimits: () => getOpenRouterModelTokenLimits,
|
|
24
35
|
getOpenRouterModels: () => getOpenRouterModels,
|
|
25
36
|
retry: () => retry
|
|
@@ -48,8 +59,31 @@ async function getOpenRouterModelTokenLimits(modelId) {
|
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
// src/helpers/retry.ts
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
var import_typebox = require("@sinclair/typebox");
|
|
63
|
+
var import_ubiquity_os_logger = require("@ubiquity-os/ubiquity-os-logger");
|
|
64
|
+
var import_ms = __toESM(require("ms"));
|
|
65
|
+
function sleep(ms2) {
|
|
66
|
+
return new Promise((resolve) => setTimeout(resolve, ms2));
|
|
67
|
+
}
|
|
68
|
+
async function handleRetryError(err, delay, options, isLastLoop) {
|
|
69
|
+
const lastError = err;
|
|
70
|
+
if (options.onError) {
|
|
71
|
+
await options.onError(err);
|
|
72
|
+
}
|
|
73
|
+
let shouldRetry;
|
|
74
|
+
if (options.isErrorRetryable) {
|
|
75
|
+
shouldRetry = await options.isErrorRetryable(err);
|
|
76
|
+
}
|
|
77
|
+
if (shouldRetry === false || isLastLoop) {
|
|
78
|
+
throw lastError;
|
|
79
|
+
}
|
|
80
|
+
if (typeof shouldRetry === "number" && Number.isFinite(shouldRetry)) {
|
|
81
|
+
await sleep(shouldRetry);
|
|
82
|
+
} else {
|
|
83
|
+
await sleep(delay);
|
|
84
|
+
delay *= 2;
|
|
85
|
+
}
|
|
86
|
+
return { delay, lastError };
|
|
53
87
|
}
|
|
54
88
|
async function retry(fn, options) {
|
|
55
89
|
let delay = 1e3;
|
|
@@ -58,28 +92,117 @@ async function retry(fn, options) {
|
|
|
58
92
|
try {
|
|
59
93
|
return await fn();
|
|
60
94
|
} catch (err) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
let shouldRetry;
|
|
66
|
-
if (options.isErrorRetryable) {
|
|
67
|
-
shouldRetry = await options.isErrorRetryable(err);
|
|
68
|
-
}
|
|
69
|
-
if (shouldRetry === false) {
|
|
70
|
-
throw lastError;
|
|
71
|
-
} else if (typeof shouldRetry === "number" && Number.isFinite(shouldRetry)) {
|
|
72
|
-
await sleep(shouldRetry);
|
|
73
|
-
} else {
|
|
74
|
-
await sleep(delay);
|
|
75
|
-
delay *= 2;
|
|
76
|
-
}
|
|
95
|
+
const result = await handleRetryError(err, delay, options, i >= options.maxRetries);
|
|
96
|
+
delay = result.delay;
|
|
97
|
+
lastError = result.lastError;
|
|
77
98
|
}
|
|
78
99
|
}
|
|
79
100
|
throw lastError;
|
|
80
101
|
}
|
|
102
|
+
function checkLlmRetryableState(error) {
|
|
103
|
+
if (error instanceof SyntaxError || error instanceof import_typebox.TypeBoxError || error instanceof import_ubiquity_os_logger.LogReturn) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
const status = extractStatus(error) ?? extractStatusFromMessage(error);
|
|
107
|
+
if (!status) return false;
|
|
108
|
+
if (status === 429) {
|
|
109
|
+
const headers = extractHeaders(error);
|
|
110
|
+
const tokenDelay = parseRetryAfter(headers, "x-ratelimit-reset-tokens");
|
|
111
|
+
const requestDelay = parseRetryAfter(headers, "x-ratelimit-reset-requests");
|
|
112
|
+
const retryAfter = parseRetryAfter(headers, "retry-after");
|
|
113
|
+
const delays = [tokenDelay, requestDelay, retryAfter].filter((value) => Number.isFinite(value));
|
|
114
|
+
if (!delays.length) return true;
|
|
115
|
+
return Math.max(...delays);
|
|
116
|
+
}
|
|
117
|
+
return status >= 500;
|
|
118
|
+
}
|
|
119
|
+
function extractStatus(error) {
|
|
120
|
+
const direct = getStatusFromSource(error);
|
|
121
|
+
if (direct !== null) return direct;
|
|
122
|
+
if (typeof error !== "object" || error === null || !("cause" in error)) return null;
|
|
123
|
+
return getStatusFromSource(error.cause);
|
|
124
|
+
}
|
|
125
|
+
function getStatusFromSource(source) {
|
|
126
|
+
if (typeof source !== "object" || source === null) return null;
|
|
127
|
+
const maybeError = source;
|
|
128
|
+
const candidates = [maybeError.status, maybeError.statusCode, maybeError.response?.status];
|
|
129
|
+
for (const candidate of candidates) {
|
|
130
|
+
if (typeof candidate === "number") return candidate;
|
|
131
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
132
|
+
const parsed = Number.parseInt(candidate, 10);
|
|
133
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
function extractStatusFromMessage(error) {
|
|
139
|
+
const message = extractMessage(error);
|
|
140
|
+
if (!message) return null;
|
|
141
|
+
const match = STATUS_FROM_MESSAGE_REGEX.exec(message);
|
|
142
|
+
if (!match) return null;
|
|
143
|
+
const status = Number(match[1]);
|
|
144
|
+
return Number.isFinite(status) ? status : null;
|
|
145
|
+
}
|
|
146
|
+
function extractMessage(error) {
|
|
147
|
+
if (typeof error === "string") return error;
|
|
148
|
+
if (typeof error !== "object" || error === null || !("message" in error)) return void 0;
|
|
149
|
+
const maybeMessage = error.message;
|
|
150
|
+
return typeof maybeMessage === "string" ? maybeMessage : void 0;
|
|
151
|
+
}
|
|
152
|
+
function extractHeaders(error) {
|
|
153
|
+
if (typeof error !== "object" || error === null) return void 0;
|
|
154
|
+
const maybeError = error;
|
|
155
|
+
if (maybeError.headers) return maybeError.headers;
|
|
156
|
+
if (maybeError.response?.headers) return maybeError.response.headers;
|
|
157
|
+
const maybeCause = maybeError.cause;
|
|
158
|
+
if (typeof maybeCause === "object" && maybeCause !== null) {
|
|
159
|
+
const cause = maybeCause;
|
|
160
|
+
if (cause.headers) return cause.headers;
|
|
161
|
+
if (cause.response?.headers) return cause.response.headers;
|
|
162
|
+
}
|
|
163
|
+
return void 0;
|
|
164
|
+
}
|
|
165
|
+
function parseRetryAfter(headers, headerName) {
|
|
166
|
+
const value = getHeaderValue(headers, headerName);
|
|
167
|
+
if (!value) return null;
|
|
168
|
+
const delay = parseDelayMs(value, headerName.toLowerCase() === "retry-after");
|
|
169
|
+
return Number.isFinite(delay) ? delay : null;
|
|
170
|
+
}
|
|
171
|
+
function getHeaderValue(headers, headerName) {
|
|
172
|
+
if (!headers || typeof headers !== "object") return void 0;
|
|
173
|
+
const maybeHeaders = headers;
|
|
174
|
+
if (typeof maybeHeaders.get === "function") {
|
|
175
|
+
const value = maybeHeaders.get(headerName);
|
|
176
|
+
return typeof value === "string" ? value : void 0;
|
|
177
|
+
}
|
|
178
|
+
const direct = maybeHeaders[headerName] ?? maybeHeaders[headerName.toLowerCase()] ?? maybeHeaders[headerName.toUpperCase()];
|
|
179
|
+
return typeof direct === "string" ? direct : void 0;
|
|
180
|
+
}
|
|
181
|
+
var STATUS_FROM_MESSAGE_REGEX = /\b(?:status(?: code)?|http|error)\b\D*(\d{3})(?!\d)/i;
|
|
182
|
+
function parseDelayMs(value, numericIsSeconds) {
|
|
183
|
+
const trimmed = value.trim();
|
|
184
|
+
if (!trimmed) return null;
|
|
185
|
+
if (isNumericString(trimmed)) {
|
|
186
|
+
const numeric = Number(trimmed);
|
|
187
|
+
if (!Number.isFinite(numeric)) return null;
|
|
188
|
+
return numericIsSeconds ? numeric * 1e3 : numeric;
|
|
189
|
+
}
|
|
190
|
+
const duration = (0, import_ms.default)(trimmed);
|
|
191
|
+
if (Number.isFinite(duration)) return duration;
|
|
192
|
+
return parseDateDelay(trimmed);
|
|
193
|
+
}
|
|
194
|
+
function parseDateDelay(value) {
|
|
195
|
+
const parsedDate = Date.parse(value);
|
|
196
|
+
if (Number.isNaN(parsedDate)) return null;
|
|
197
|
+
const delay = parsedDate - Date.now();
|
|
198
|
+
return delay > 0 ? delay : 0;
|
|
199
|
+
}
|
|
200
|
+
function isNumericString(value) {
|
|
201
|
+
return /^\d+(?:\.\d+)?$/.test(value);
|
|
202
|
+
}
|
|
81
203
|
// Annotate the CommonJS export names for ESM import in node:
|
|
82
204
|
0 && (module.exports = {
|
|
205
|
+
checkLlmRetryableState,
|
|
83
206
|
getOpenRouterModelTokenLimits,
|
|
84
207
|
getOpenRouterModels,
|
|
85
208
|
retry
|
package/dist/helpers.mjs
CHANGED
|
@@ -20,8 +20,31 @@ async function getOpenRouterModelTokenLimits(modelId) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// src/helpers/retry.ts
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
import { TypeBoxError } from "@sinclair/typebox";
|
|
24
|
+
import { LogReturn } from "@ubiquity-os/ubiquity-os-logger";
|
|
25
|
+
import ms from "ms";
|
|
26
|
+
function sleep(ms2) {
|
|
27
|
+
return new Promise((resolve) => setTimeout(resolve, ms2));
|
|
28
|
+
}
|
|
29
|
+
async function handleRetryError(err, delay, options, isLastLoop) {
|
|
30
|
+
const lastError = err;
|
|
31
|
+
if (options.onError) {
|
|
32
|
+
await options.onError(err);
|
|
33
|
+
}
|
|
34
|
+
let shouldRetry;
|
|
35
|
+
if (options.isErrorRetryable) {
|
|
36
|
+
shouldRetry = await options.isErrorRetryable(err);
|
|
37
|
+
}
|
|
38
|
+
if (shouldRetry === false || isLastLoop) {
|
|
39
|
+
throw lastError;
|
|
40
|
+
}
|
|
41
|
+
if (typeof shouldRetry === "number" && Number.isFinite(shouldRetry)) {
|
|
42
|
+
await sleep(shouldRetry);
|
|
43
|
+
} else {
|
|
44
|
+
await sleep(delay);
|
|
45
|
+
delay *= 2;
|
|
46
|
+
}
|
|
47
|
+
return { delay, lastError };
|
|
25
48
|
}
|
|
26
49
|
async function retry(fn, options) {
|
|
27
50
|
let delay = 1e3;
|
|
@@ -30,27 +53,116 @@ async function retry(fn, options) {
|
|
|
30
53
|
try {
|
|
31
54
|
return await fn();
|
|
32
55
|
} catch (err) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
let shouldRetry;
|
|
38
|
-
if (options.isErrorRetryable) {
|
|
39
|
-
shouldRetry = await options.isErrorRetryable(err);
|
|
40
|
-
}
|
|
41
|
-
if (shouldRetry === false) {
|
|
42
|
-
throw lastError;
|
|
43
|
-
} else if (typeof shouldRetry === "number" && Number.isFinite(shouldRetry)) {
|
|
44
|
-
await sleep(shouldRetry);
|
|
45
|
-
} else {
|
|
46
|
-
await sleep(delay);
|
|
47
|
-
delay *= 2;
|
|
48
|
-
}
|
|
56
|
+
const result = await handleRetryError(err, delay, options, i >= options.maxRetries);
|
|
57
|
+
delay = result.delay;
|
|
58
|
+
lastError = result.lastError;
|
|
49
59
|
}
|
|
50
60
|
}
|
|
51
61
|
throw lastError;
|
|
52
62
|
}
|
|
63
|
+
function checkLlmRetryableState(error) {
|
|
64
|
+
if (error instanceof SyntaxError || error instanceof TypeBoxError || error instanceof LogReturn) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
const status = extractStatus(error) ?? extractStatusFromMessage(error);
|
|
68
|
+
if (!status) return false;
|
|
69
|
+
if (status === 429) {
|
|
70
|
+
const headers = extractHeaders(error);
|
|
71
|
+
const tokenDelay = parseRetryAfter(headers, "x-ratelimit-reset-tokens");
|
|
72
|
+
const requestDelay = parseRetryAfter(headers, "x-ratelimit-reset-requests");
|
|
73
|
+
const retryAfter = parseRetryAfter(headers, "retry-after");
|
|
74
|
+
const delays = [tokenDelay, requestDelay, retryAfter].filter((value) => Number.isFinite(value));
|
|
75
|
+
if (!delays.length) return true;
|
|
76
|
+
return Math.max(...delays);
|
|
77
|
+
}
|
|
78
|
+
return status >= 500;
|
|
79
|
+
}
|
|
80
|
+
function extractStatus(error) {
|
|
81
|
+
const direct = getStatusFromSource(error);
|
|
82
|
+
if (direct !== null) return direct;
|
|
83
|
+
if (typeof error !== "object" || error === null || !("cause" in error)) return null;
|
|
84
|
+
return getStatusFromSource(error.cause);
|
|
85
|
+
}
|
|
86
|
+
function getStatusFromSource(source) {
|
|
87
|
+
if (typeof source !== "object" || source === null) return null;
|
|
88
|
+
const maybeError = source;
|
|
89
|
+
const candidates = [maybeError.status, maybeError.statusCode, maybeError.response?.status];
|
|
90
|
+
for (const candidate of candidates) {
|
|
91
|
+
if (typeof candidate === "number") return candidate;
|
|
92
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
93
|
+
const parsed = Number.parseInt(candidate, 10);
|
|
94
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
function extractStatusFromMessage(error) {
|
|
100
|
+
const message = extractMessage(error);
|
|
101
|
+
if (!message) return null;
|
|
102
|
+
const match = STATUS_FROM_MESSAGE_REGEX.exec(message);
|
|
103
|
+
if (!match) return null;
|
|
104
|
+
const status = Number(match[1]);
|
|
105
|
+
return Number.isFinite(status) ? status : null;
|
|
106
|
+
}
|
|
107
|
+
function extractMessage(error) {
|
|
108
|
+
if (typeof error === "string") return error;
|
|
109
|
+
if (typeof error !== "object" || error === null || !("message" in error)) return void 0;
|
|
110
|
+
const maybeMessage = error.message;
|
|
111
|
+
return typeof maybeMessage === "string" ? maybeMessage : void 0;
|
|
112
|
+
}
|
|
113
|
+
function extractHeaders(error) {
|
|
114
|
+
if (typeof error !== "object" || error === null) return void 0;
|
|
115
|
+
const maybeError = error;
|
|
116
|
+
if (maybeError.headers) return maybeError.headers;
|
|
117
|
+
if (maybeError.response?.headers) return maybeError.response.headers;
|
|
118
|
+
const maybeCause = maybeError.cause;
|
|
119
|
+
if (typeof maybeCause === "object" && maybeCause !== null) {
|
|
120
|
+
const cause = maybeCause;
|
|
121
|
+
if (cause.headers) return cause.headers;
|
|
122
|
+
if (cause.response?.headers) return cause.response.headers;
|
|
123
|
+
}
|
|
124
|
+
return void 0;
|
|
125
|
+
}
|
|
126
|
+
function parseRetryAfter(headers, headerName) {
|
|
127
|
+
const value = getHeaderValue(headers, headerName);
|
|
128
|
+
if (!value) return null;
|
|
129
|
+
const delay = parseDelayMs(value, headerName.toLowerCase() === "retry-after");
|
|
130
|
+
return Number.isFinite(delay) ? delay : null;
|
|
131
|
+
}
|
|
132
|
+
function getHeaderValue(headers, headerName) {
|
|
133
|
+
if (!headers || typeof headers !== "object") return void 0;
|
|
134
|
+
const maybeHeaders = headers;
|
|
135
|
+
if (typeof maybeHeaders.get === "function") {
|
|
136
|
+
const value = maybeHeaders.get(headerName);
|
|
137
|
+
return typeof value === "string" ? value : void 0;
|
|
138
|
+
}
|
|
139
|
+
const direct = maybeHeaders[headerName] ?? maybeHeaders[headerName.toLowerCase()] ?? maybeHeaders[headerName.toUpperCase()];
|
|
140
|
+
return typeof direct === "string" ? direct : void 0;
|
|
141
|
+
}
|
|
142
|
+
var STATUS_FROM_MESSAGE_REGEX = /\b(?:status(?: code)?|http|error)\b\D*(\d{3})(?!\d)/i;
|
|
143
|
+
function parseDelayMs(value, numericIsSeconds) {
|
|
144
|
+
const trimmed = value.trim();
|
|
145
|
+
if (!trimmed) return null;
|
|
146
|
+
if (isNumericString(trimmed)) {
|
|
147
|
+
const numeric = Number(trimmed);
|
|
148
|
+
if (!Number.isFinite(numeric)) return null;
|
|
149
|
+
return numericIsSeconds ? numeric * 1e3 : numeric;
|
|
150
|
+
}
|
|
151
|
+
const duration = ms(trimmed);
|
|
152
|
+
if (Number.isFinite(duration)) return duration;
|
|
153
|
+
return parseDateDelay(trimmed);
|
|
154
|
+
}
|
|
155
|
+
function parseDateDelay(value) {
|
|
156
|
+
const parsedDate = Date.parse(value);
|
|
157
|
+
if (Number.isNaN(parsedDate)) return null;
|
|
158
|
+
const delay = parsedDate - Date.now();
|
|
159
|
+
return delay > 0 ? delay : 0;
|
|
160
|
+
}
|
|
161
|
+
function isNumericString(value) {
|
|
162
|
+
return /^\d+(?:\.\d+)?$/.test(value);
|
|
163
|
+
}
|
|
53
164
|
export {
|
|
165
|
+
checkLlmRetryableState,
|
|
54
166
|
getOpenRouterModelTokenLimits,
|
|
55
167
|
getOpenRouterModels,
|
|
56
168
|
retry
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ubiquity-os/plugin-sdk",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.12.0",
|
|
4
4
|
"description": "SDK for plugin support.",
|
|
5
5
|
"author": "Ubiquity DAO",
|
|
6
6
|
"license": "MIT",
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
"@ubiquity-os/ubiquity-os-logger": "^1.4.0",
|
|
122
122
|
"hono": "^4.10.6",
|
|
123
123
|
"js-yaml": "^4.1.1",
|
|
124
|
+
"ms": "^2.1.3",
|
|
124
125
|
"openai": "^6.15.0"
|
|
125
126
|
},
|
|
126
127
|
"peerDependencies": {
|