@burdenoff/be-sdk 2026.525.1 → 2026.527.1
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.d.ts +222 -1
- package/dist/index.js +471 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -42,7 +42,7 @@ export { DEFAULT_CIRCUIT_BREAKER_CONFIG, DEFAULT_GATEWAY_RETRY_CONFIG, DEFAULT_G
|
|
|
42
42
|
import { logger } from './chunk-CBZNP6RF.js';
|
|
43
43
|
export { logger_exports as Logger } from './chunk-CBZNP6RF.js';
|
|
44
44
|
export { dataloader_exports as DataLoader } from './chunk-NUB5GQTU.js';
|
|
45
|
-
import { __name } from './chunk-XE4OXN2W.js';
|
|
45
|
+
import { __name, __export } from './chunk-XE4OXN2W.js';
|
|
46
46
|
import { GraphQLClient } from 'graphql-request';
|
|
47
47
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
48
48
|
|
|
@@ -2192,6 +2192,475 @@ function createTenantBackendClient(config) {
|
|
|
2192
2192
|
}
|
|
2193
2193
|
__name(createTenantBackendClient, "createTenantBackendClient");
|
|
2194
2194
|
|
|
2195
|
+
// src/platform/index.ts
|
|
2196
|
+
var platform_exports = {};
|
|
2197
|
+
__export(platform_exports, {
|
|
2198
|
+
CircuitBreakerOpenError: () => CircuitBreakerOpenError,
|
|
2199
|
+
LLMRouter: () => LLMRouter,
|
|
2200
|
+
__resetHttpClientBreakers: () => __resetHttpClientBreakers,
|
|
2201
|
+
buildCacheKey: () => buildCacheKey,
|
|
2202
|
+
buildLoaders: () => buildLoaders,
|
|
2203
|
+
clampLimit: () => clampLimit,
|
|
2204
|
+
createHttpClient: () => createHttpClient,
|
|
2205
|
+
dropOldPartitions: () => dropOldPartitions,
|
|
2206
|
+
ensureMonthlyPartitions: () => ensureMonthlyPartitions,
|
|
2207
|
+
getNamespaceVersion: () => getNamespaceVersion,
|
|
2208
|
+
httpClient: () => httpClient,
|
|
2209
|
+
invalidateNamespace: () => invalidateNamespace,
|
|
2210
|
+
monthlyPartitionName: () => monthlyPartitionName,
|
|
2211
|
+
paginate: () => paginate,
|
|
2212
|
+
rateLimitIncr: () => rateLimitIncr,
|
|
2213
|
+
resolveReferenceLoader: () => resolveReferenceLoader,
|
|
2214
|
+
withLeaderLock: () => withLeaderLock
|
|
2215
|
+
});
|
|
2216
|
+
|
|
2217
|
+
// src/platform/httpClient.ts
|
|
2218
|
+
var breakers = /* @__PURE__ */ new Map();
|
|
2219
|
+
function hostOf(url) {
|
|
2220
|
+
try {
|
|
2221
|
+
return new URL(url).host;
|
|
2222
|
+
} catch {
|
|
2223
|
+
return url;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
__name(hostOf, "hostOf");
|
|
2227
|
+
function getBreaker(host) {
|
|
2228
|
+
let b = breakers.get(host);
|
|
2229
|
+
if (!b) {
|
|
2230
|
+
b = {
|
|
2231
|
+
failures: 0,
|
|
2232
|
+
openedAt: null,
|
|
2233
|
+
state: "CLOSED"
|
|
2234
|
+
};
|
|
2235
|
+
breakers.set(host, b);
|
|
2236
|
+
}
|
|
2237
|
+
return b;
|
|
2238
|
+
}
|
|
2239
|
+
__name(getBreaker, "getBreaker");
|
|
2240
|
+
function defaultRetryOn(status) {
|
|
2241
|
+
return status === 408 || status === 425 || status === 429 || status >= 500 && status <= 599;
|
|
2242
|
+
}
|
|
2243
|
+
__name(defaultRetryOn, "defaultRetryOn");
|
|
2244
|
+
function jitter(delay) {
|
|
2245
|
+
return Math.floor(Math.random() * delay);
|
|
2246
|
+
}
|
|
2247
|
+
__name(jitter, "jitter");
|
|
2248
|
+
function sleep(ms) {
|
|
2249
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
2250
|
+
}
|
|
2251
|
+
__name(sleep, "sleep");
|
|
2252
|
+
var CircuitBreakerOpenError = class extends Error {
|
|
2253
|
+
static {
|
|
2254
|
+
__name(this, "CircuitBreakerOpenError");
|
|
2255
|
+
}
|
|
2256
|
+
constructor(host) {
|
|
2257
|
+
super(`circuit breaker OPEN for host=${host}`);
|
|
2258
|
+
this.name = "CircuitBreakerOpenError";
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2261
|
+
function __resetHttpClientBreakers() {
|
|
2262
|
+
breakers.clear();
|
|
2263
|
+
}
|
|
2264
|
+
__name(__resetHttpClientBreakers, "__resetHttpClientBreakers");
|
|
2265
|
+
async function executeOnce(url, init, timeoutMs) {
|
|
2266
|
+
const signal = init.signal ?? AbortSignal.timeout(timeoutMs);
|
|
2267
|
+
return fetch(url, {
|
|
2268
|
+
...init,
|
|
2269
|
+
signal
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
__name(executeOnce, "executeOnce");
|
|
2273
|
+
async function httpClient(url, init = {}, config = {}) {
|
|
2274
|
+
const fullUrl = config.baseUrl && !/^https?:\/\//i.test(url) ? `${config.baseUrl.replace(/\/$/, "")}/${url.replace(/^\//, "")}` : url;
|
|
2275
|
+
const host = hostOf(fullUrl);
|
|
2276
|
+
const breaker = getBreaker(host);
|
|
2277
|
+
const breakerThreshold = config.breaker?.threshold ?? 5;
|
|
2278
|
+
const cooldownMs = config.breaker?.cooldownMs ?? 3e4;
|
|
2279
|
+
if (breaker.state === "OPEN") {
|
|
2280
|
+
if (breaker.openedAt && Date.now() - breaker.openedAt >= cooldownMs) {
|
|
2281
|
+
breaker.state = "HALF_OPEN";
|
|
2282
|
+
logger.info("httpClient.breaker.halfOpen", {
|
|
2283
|
+
host
|
|
2284
|
+
});
|
|
2285
|
+
} else {
|
|
2286
|
+
throw new CircuitBreakerOpenError(host);
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
const attempts = init.retry?.attempts ?? config.retry?.attempts ?? 3;
|
|
2290
|
+
const initialDelay = init.retry?.initialDelayMs ?? config.retry?.initialDelayMs ?? 100;
|
|
2291
|
+
const maxDelay = init.retry?.maxDelayMs ?? config.retry?.maxDelayMs ?? 2e3;
|
|
2292
|
+
const retryOn = init.retry?.retryOn ?? config.retry?.retryOn ?? defaultRetryOn;
|
|
2293
|
+
const timeoutMs = init.timeoutMs ?? config.defaultTimeoutMs ?? 5e3;
|
|
2294
|
+
const headers = {
|
|
2295
|
+
...config.defaultHeaders ?? {},
|
|
2296
|
+
...init.headers ?? {}
|
|
2297
|
+
};
|
|
2298
|
+
let lastErr;
|
|
2299
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
2300
|
+
try {
|
|
2301
|
+
const response = await executeOnce(fullUrl, {
|
|
2302
|
+
...init,
|
|
2303
|
+
headers
|
|
2304
|
+
}, timeoutMs);
|
|
2305
|
+
if (response.status >= 500) {
|
|
2306
|
+
breaker.failures += 1;
|
|
2307
|
+
if (breaker.failures >= breakerThreshold) {
|
|
2308
|
+
breaker.state = "OPEN";
|
|
2309
|
+
breaker.openedAt = Date.now();
|
|
2310
|
+
logger.warn("httpClient.breaker.open", {
|
|
2311
|
+
host,
|
|
2312
|
+
failures: breaker.failures
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
if (attempt < attempts && retryOn(response.status)) {
|
|
2316
|
+
const delay = Math.min(initialDelay * 2 ** (attempt - 1), maxDelay);
|
|
2317
|
+
await sleep(jitter(delay));
|
|
2318
|
+
continue;
|
|
2319
|
+
}
|
|
2320
|
+
return response;
|
|
2321
|
+
}
|
|
2322
|
+
breaker.failures = 0;
|
|
2323
|
+
if (breaker.state === "HALF_OPEN") {
|
|
2324
|
+
breaker.state = "CLOSED";
|
|
2325
|
+
breaker.openedAt = null;
|
|
2326
|
+
logger.info("httpClient.breaker.closed", {
|
|
2327
|
+
host
|
|
2328
|
+
});
|
|
2329
|
+
}
|
|
2330
|
+
if (attempt < attempts && retryOn(response.status)) {
|
|
2331
|
+
const delay = Math.min(initialDelay * 2 ** (attempt - 1), maxDelay);
|
|
2332
|
+
await sleep(jitter(delay));
|
|
2333
|
+
continue;
|
|
2334
|
+
}
|
|
2335
|
+
return response;
|
|
2336
|
+
} catch (err) {
|
|
2337
|
+
lastErr = err;
|
|
2338
|
+
breaker.failures += 1;
|
|
2339
|
+
if (breaker.failures >= breakerThreshold) {
|
|
2340
|
+
breaker.state = "OPEN";
|
|
2341
|
+
breaker.openedAt = Date.now();
|
|
2342
|
+
logger.warn("httpClient.breaker.open", {
|
|
2343
|
+
host,
|
|
2344
|
+
failures: breaker.failures
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
if (attempt >= attempts) break;
|
|
2348
|
+
const delay = Math.min(initialDelay * 2 ** (attempt - 1), maxDelay);
|
|
2349
|
+
await sleep(jitter(delay));
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
logger.error("httpClient.exhausted", {
|
|
2353
|
+
url: fullUrl,
|
|
2354
|
+
attempts,
|
|
2355
|
+
host
|
|
2356
|
+
});
|
|
2357
|
+
throw lastErr instanceof Error ? lastErr : new Error(`httpClient failed: ${String(lastErr)}`);
|
|
2358
|
+
}
|
|
2359
|
+
__name(httpClient, "httpClient");
|
|
2360
|
+
function createHttpClient(config = {}) {
|
|
2361
|
+
return (url, init = {}) => httpClient(url, init, config);
|
|
2362
|
+
}
|
|
2363
|
+
__name(createHttpClient, "createHttpClient");
|
|
2364
|
+
|
|
2365
|
+
// src/platform/leaderLock.ts
|
|
2366
|
+
var RELEASE_LUA = `
|
|
2367
|
+
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
2368
|
+
return redis.call("del", KEYS[1])
|
|
2369
|
+
else
|
|
2370
|
+
return 0
|
|
2371
|
+
end
|
|
2372
|
+
`;
|
|
2373
|
+
var RENEW_LUA = `
|
|
2374
|
+
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
2375
|
+
return redis.call("pexpire", KEYS[1], ARGV[2])
|
|
2376
|
+
else
|
|
2377
|
+
return 0
|
|
2378
|
+
end
|
|
2379
|
+
`;
|
|
2380
|
+
function generateToken() {
|
|
2381
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}-${process.pid}`;
|
|
2382
|
+
}
|
|
2383
|
+
__name(generateToken, "generateToken");
|
|
2384
|
+
async function withLeaderLock(redis, name, fn, options) {
|
|
2385
|
+
const key = `besdk:leader:${name}`;
|
|
2386
|
+
const token = generateToken();
|
|
2387
|
+
const ttlMs = options.ttlMs;
|
|
2388
|
+
const renewMs = options.renewMs ?? Math.max(1e3, Math.floor(ttlMs / 3));
|
|
2389
|
+
const ttlSeconds = Math.max(1, Math.ceil(ttlMs / 1e3));
|
|
2390
|
+
const acquired = await redis.set(key, token, "EX", ttlSeconds, "NX");
|
|
2391
|
+
if (acquired !== "OK") {
|
|
2392
|
+
return {
|
|
2393
|
+
acquired: false
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
logger.info("leaderLock.acquired", {
|
|
2397
|
+
name,
|
|
2398
|
+
token,
|
|
2399
|
+
ttlMs
|
|
2400
|
+
});
|
|
2401
|
+
let stopped = false;
|
|
2402
|
+
const renewTimer = setInterval(() => {
|
|
2403
|
+
if (stopped) return;
|
|
2404
|
+
redis.eval(RENEW_LUA, 1, key, token, String(ttlMs)).catch((err) => {
|
|
2405
|
+
logger.warn("leaderLock.renew.failed", {
|
|
2406
|
+
name,
|
|
2407
|
+
err: String(err)
|
|
2408
|
+
});
|
|
2409
|
+
});
|
|
2410
|
+
}, renewMs);
|
|
2411
|
+
if (typeof renewTimer.unref === "function") {
|
|
2412
|
+
renewTimer.unref();
|
|
2413
|
+
}
|
|
2414
|
+
try {
|
|
2415
|
+
const result = await fn();
|
|
2416
|
+
return {
|
|
2417
|
+
acquired: true,
|
|
2418
|
+
result
|
|
2419
|
+
};
|
|
2420
|
+
} finally {
|
|
2421
|
+
stopped = true;
|
|
2422
|
+
clearInterval(renewTimer);
|
|
2423
|
+
try {
|
|
2424
|
+
await redis.eval(RELEASE_LUA, 1, key, token);
|
|
2425
|
+
logger.info("leaderLock.released", {
|
|
2426
|
+
name,
|
|
2427
|
+
token
|
|
2428
|
+
});
|
|
2429
|
+
} catch (err) {
|
|
2430
|
+
logger.warn("leaderLock.release.failed", {
|
|
2431
|
+
name,
|
|
2432
|
+
err: String(err)
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
__name(withLeaderLock, "withLeaderLock");
|
|
2438
|
+
|
|
2439
|
+
// src/platform/rateLimit.ts
|
|
2440
|
+
async function rateLimitIncr(redis, input) {
|
|
2441
|
+
const fullKey = `besdk:rl:${input.key}`;
|
|
2442
|
+
const count = await redis.incr(fullKey);
|
|
2443
|
+
if (count === 1) {
|
|
2444
|
+
await redis.pexpire(fullKey, input.windowMs);
|
|
2445
|
+
}
|
|
2446
|
+
let ttl = -1;
|
|
2447
|
+
try {
|
|
2448
|
+
ttl = await redis.pttl(fullKey);
|
|
2449
|
+
} catch {
|
|
2450
|
+
ttl = input.windowMs;
|
|
2451
|
+
}
|
|
2452
|
+
const resetAt = Date.now() + (ttl > 0 ? ttl : input.windowMs);
|
|
2453
|
+
const remaining = Math.max(0, input.limit - count);
|
|
2454
|
+
return {
|
|
2455
|
+
allowed: count <= input.limit,
|
|
2456
|
+
count,
|
|
2457
|
+
remaining,
|
|
2458
|
+
resetAt
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
__name(rateLimitIncr, "rateLimitIncr");
|
|
2462
|
+
|
|
2463
|
+
// src/platform/paginator.ts
|
|
2464
|
+
function clampLimit(input, opts = {}) {
|
|
2465
|
+
const def = opts.defaultLimit ?? 50;
|
|
2466
|
+
const max = opts.maxLimit ?? 200;
|
|
2467
|
+
const min = opts.minLimit ?? 1;
|
|
2468
|
+
if (input == null || Number.isNaN(input)) return def;
|
|
2469
|
+
return Math.min(Math.max(Math.floor(input), min), max);
|
|
2470
|
+
}
|
|
2471
|
+
__name(clampLimit, "clampLimit");
|
|
2472
|
+
async function paginate(model, input, opts = {}) {
|
|
2473
|
+
const take = clampLimit(input.take ?? void 0, opts);
|
|
2474
|
+
const items = await model.findMany({
|
|
2475
|
+
take: take + 1,
|
|
2476
|
+
skip: input.cursor ? 1 : 0,
|
|
2477
|
+
cursor: input.cursor ? {
|
|
2478
|
+
id: input.cursor
|
|
2479
|
+
} : void 0,
|
|
2480
|
+
where: input.where,
|
|
2481
|
+
orderBy: input.orderBy
|
|
2482
|
+
});
|
|
2483
|
+
const hasMore = items.length > take;
|
|
2484
|
+
const trimmed = hasMore ? items.slice(0, take) : items;
|
|
2485
|
+
const nextCursor = hasMore && trimmed.length > 0 ? trimmed[trimmed.length - 1].id : null;
|
|
2486
|
+
return {
|
|
2487
|
+
items: trimmed,
|
|
2488
|
+
hasMore,
|
|
2489
|
+
nextCursor
|
|
2490
|
+
};
|
|
2491
|
+
}
|
|
2492
|
+
__name(paginate, "paginate");
|
|
2493
|
+
|
|
2494
|
+
// src/platform/dataLoaderFactory.ts
|
|
2495
|
+
function buildLoaders(ctx, builder) {
|
|
2496
|
+
return builder(ctx);
|
|
2497
|
+
}
|
|
2498
|
+
__name(buildLoaders, "buildLoaders");
|
|
2499
|
+
function resolveReferenceLoader(loaderName, keyField = "id") {
|
|
2500
|
+
return async (reference, ctx) => {
|
|
2501
|
+
const loader = ctx.loaders[loaderName];
|
|
2502
|
+
if (!loader) {
|
|
2503
|
+
throw new Error(`resolveReferenceLoader: loader "${loaderName}" not found on ctx.loaders`);
|
|
2504
|
+
}
|
|
2505
|
+
const key = reference[keyField];
|
|
2506
|
+
if (typeof key !== "string") {
|
|
2507
|
+
return null;
|
|
2508
|
+
}
|
|
2509
|
+
return loader.load(key);
|
|
2510
|
+
};
|
|
2511
|
+
}
|
|
2512
|
+
__name(resolveReferenceLoader, "resolveReferenceLoader");
|
|
2513
|
+
|
|
2514
|
+
// src/platform/cacheInvalidation.ts
|
|
2515
|
+
var VERSION_PREFIX = "besdk:cv:";
|
|
2516
|
+
async function getNamespaceVersion(redis, namespace) {
|
|
2517
|
+
const v = await redis.get(`${VERSION_PREFIX}${namespace}`);
|
|
2518
|
+
if (!v) return 1;
|
|
2519
|
+
const n = Number.parseInt(v, 10);
|
|
2520
|
+
return Number.isFinite(n) && n > 0 ? n : 1;
|
|
2521
|
+
}
|
|
2522
|
+
__name(getNamespaceVersion, "getNamespaceVersion");
|
|
2523
|
+
async function buildCacheKey(redis, namespace, ...parts) {
|
|
2524
|
+
const ver = await getNamespaceVersion(redis, namespace);
|
|
2525
|
+
return `${namespace}:v${ver}:${parts.join(":")}`;
|
|
2526
|
+
}
|
|
2527
|
+
__name(buildCacheKey, "buildCacheKey");
|
|
2528
|
+
async function invalidateNamespace(redis, namespace) {
|
|
2529
|
+
return redis.incr(`${VERSION_PREFIX}${namespace}`);
|
|
2530
|
+
}
|
|
2531
|
+
__name(invalidateNamespace, "invalidateNamespace");
|
|
2532
|
+
|
|
2533
|
+
// src/platform/pgPartition.ts
|
|
2534
|
+
function pad2(n) {
|
|
2535
|
+
return n < 10 ? `0${n}` : `${n}`;
|
|
2536
|
+
}
|
|
2537
|
+
__name(pad2, "pad2");
|
|
2538
|
+
function monthlyPartitionName(table, year, month) {
|
|
2539
|
+
return `${table}_y${year}m${pad2(month)}`;
|
|
2540
|
+
}
|
|
2541
|
+
__name(monthlyPartitionName, "monthlyPartitionName");
|
|
2542
|
+
async function ensureMonthlyPartitions(prisma, opts) {
|
|
2543
|
+
const created = [];
|
|
2544
|
+
const cur = new Date(Date.UTC(opts.from.getUTCFullYear(), opts.from.getUTCMonth(), 1));
|
|
2545
|
+
const end = new Date(Date.UTC(opts.to.getUTCFullYear(), opts.to.getUTCMonth(), 1));
|
|
2546
|
+
while (cur <= end) {
|
|
2547
|
+
const y = cur.getUTCFullYear();
|
|
2548
|
+
const m = cur.getUTCMonth() + 1;
|
|
2549
|
+
const next = new Date(Date.UTC(y, m, 1));
|
|
2550
|
+
const partition = monthlyPartitionName(opts.table, y, m);
|
|
2551
|
+
const lower = `${y}-${pad2(m)}-01`;
|
|
2552
|
+
const upper = `${next.getUTCFullYear()}-${pad2(next.getUTCMonth() + 1)}-01`;
|
|
2553
|
+
await prisma.$executeRawUnsafe(`CREATE TABLE IF NOT EXISTS "${partition}" PARTITION OF "${opts.table}" FOR VALUES FROM ('${lower}') TO ('${upper}');`);
|
|
2554
|
+
created.push(partition);
|
|
2555
|
+
cur.setUTCMonth(cur.getUTCMonth() + 1);
|
|
2556
|
+
}
|
|
2557
|
+
return created;
|
|
2558
|
+
}
|
|
2559
|
+
__name(ensureMonthlyPartitions, "ensureMonthlyPartitions");
|
|
2560
|
+
async function dropOldPartitions(prisma, opts) {
|
|
2561
|
+
const rows = await prisma.$queryRawUnsafe(`SELECT child.relname AS partition_name
|
|
2562
|
+
FROM pg_inherits
|
|
2563
|
+
JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
|
|
2564
|
+
JOIN pg_class child ON pg_inherits.inhrelid = child.oid
|
|
2565
|
+
WHERE parent.relname = $1`, opts.table);
|
|
2566
|
+
const dropped = [];
|
|
2567
|
+
const cutoffYear = opts.beforeDate.getUTCFullYear();
|
|
2568
|
+
const cutoffMonth = opts.beforeDate.getUTCMonth() + 1;
|
|
2569
|
+
for (const row of rows) {
|
|
2570
|
+
const m = row.partition_name.match(/_y(\d{4})m(\d{2})$/);
|
|
2571
|
+
if (!m) continue;
|
|
2572
|
+
const y = Number(m[1]);
|
|
2573
|
+
const mo = Number(m[2]);
|
|
2574
|
+
if (y < cutoffYear || y === cutoffYear && mo < cutoffMonth) {
|
|
2575
|
+
await prisma.$executeRawUnsafe(`DROP TABLE IF EXISTS "${row.partition_name}";`);
|
|
2576
|
+
dropped.push(row.partition_name);
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
return dropped;
|
|
2580
|
+
}
|
|
2581
|
+
__name(dropOldPartitions, "dropOldPartitions");
|
|
2582
|
+
|
|
2583
|
+
// src/platform/llmClient.ts
|
|
2584
|
+
var DEFAULTS = {
|
|
2585
|
+
timeoutMs: 3e4,
|
|
2586
|
+
maxAttempts: 3,
|
|
2587
|
+
initialDelayMs: 500,
|
|
2588
|
+
maxOutputTokensCap: 4096
|
|
2589
|
+
};
|
|
2590
|
+
function jitter2(n) {
|
|
2591
|
+
return Math.floor(Math.random() * n);
|
|
2592
|
+
}
|
|
2593
|
+
__name(jitter2, "jitter");
|
|
2594
|
+
function sleep2(ms) {
|
|
2595
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
2596
|
+
}
|
|
2597
|
+
__name(sleep2, "sleep");
|
|
2598
|
+
var LLMRouter = class {
|
|
2599
|
+
static {
|
|
2600
|
+
__name(this, "LLMRouter");
|
|
2601
|
+
}
|
|
2602
|
+
adapters = /* @__PURE__ */ new Map();
|
|
2603
|
+
opts;
|
|
2604
|
+
constructor(opts = {}) {
|
|
2605
|
+
this.opts = {
|
|
2606
|
+
...DEFAULTS,
|
|
2607
|
+
...opts
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2610
|
+
register(adapter) {
|
|
2611
|
+
this.adapters.set(adapter.name, adapter);
|
|
2612
|
+
}
|
|
2613
|
+
resolveChain() {
|
|
2614
|
+
const envChain = process.env["LLM_PROVIDER_CHAIN"]?.trim();
|
|
2615
|
+
if (envChain) {
|
|
2616
|
+
return envChain.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2617
|
+
}
|
|
2618
|
+
return Array.from(this.adapters.keys());
|
|
2619
|
+
}
|
|
2620
|
+
async complete(req) {
|
|
2621
|
+
const cappedReq = {
|
|
2622
|
+
...req,
|
|
2623
|
+
maxTokens: Math.min(req.maxTokens ?? this.opts.maxOutputTokensCap, this.opts.maxOutputTokensCap)
|
|
2624
|
+
};
|
|
2625
|
+
const chain = this.resolveChain();
|
|
2626
|
+
if (chain.length === 0) {
|
|
2627
|
+
throw new Error("LLMRouter: no providers registered");
|
|
2628
|
+
}
|
|
2629
|
+
let lastErr = null;
|
|
2630
|
+
for (let attempt = 0; attempt < this.opts.maxAttempts; attempt++) {
|
|
2631
|
+
const providerName = chain[attempt % chain.length];
|
|
2632
|
+
const adapter = providerName ? this.adapters.get(providerName) : void 0;
|
|
2633
|
+
if (!adapter) {
|
|
2634
|
+
logger.warn("llmRouter.provider.notRegistered", {
|
|
2635
|
+
providerName
|
|
2636
|
+
});
|
|
2637
|
+
continue;
|
|
2638
|
+
}
|
|
2639
|
+
const timeoutMs = req.timeoutMs ?? this.opts.timeoutMs;
|
|
2640
|
+
const ctrl = new AbortController();
|
|
2641
|
+
const t2 = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
2642
|
+
try {
|
|
2643
|
+
const response = await adapter.complete(cappedReq, ctrl.signal);
|
|
2644
|
+
return response;
|
|
2645
|
+
} catch (err) {
|
|
2646
|
+
lastErr = err;
|
|
2647
|
+
logger.warn("llmRouter.attempt.failed", {
|
|
2648
|
+
provider: adapter.name,
|
|
2649
|
+
attempt,
|
|
2650
|
+
err: String(err)
|
|
2651
|
+
});
|
|
2652
|
+
if (attempt < this.opts.maxAttempts - 1) {
|
|
2653
|
+
const delay = this.opts.initialDelayMs * 2 ** attempt;
|
|
2654
|
+
await sleep2(jitter2(delay));
|
|
2655
|
+
}
|
|
2656
|
+
} finally {
|
|
2657
|
+
clearTimeout(t2);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
throw lastErr instanceof Error ? lastErr : new Error(`LLMRouter exhausted ${this.opts.maxAttempts} attempts`);
|
|
2661
|
+
}
|
|
2662
|
+
};
|
|
2663
|
+
|
|
2195
2664
|
// src/index.ts
|
|
2196
2665
|
var BurdenoffSDK = class {
|
|
2197
2666
|
static {
|
|
@@ -2623,6 +3092,6 @@ function createSDK(config, context) {
|
|
|
2623
3092
|
}
|
|
2624
3093
|
__name(createSDK, "createSDK");
|
|
2625
3094
|
|
|
2626
|
-
export { BootstrapModule, BurdenoffGraphQLClient, BurdenoffSDK, ContextManager, PaymentsModule, SearchModule, TenantBackendClient, TranslationModule, createBootstrap, createSDK, createTenantBackendClient, index_default as default };
|
|
3095
|
+
export { BootstrapModule, BurdenoffGraphQLClient, BurdenoffSDK, ContextManager, PaymentsModule, platform_exports as Platform, SearchModule, TenantBackendClient, TranslationModule, createBootstrap, createSDK, createTenantBackendClient, index_default as default };
|
|
2627
3096
|
//# sourceMappingURL=index.js.map
|
|
2628
3097
|
//# sourceMappingURL=index.js.map
|