@telepat/ideon 0.1.30 → 0.1.31
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/ideon.js +317 -29
- package/package.json +1 -1
package/dist/ideon.js
CHANGED
|
@@ -552,7 +552,8 @@ var baseT2ISettingsSchema = z2.preprocess(
|
|
|
552
552
|
z2.object({
|
|
553
553
|
modelId: z2.string().default(DEFAULT_LIMN_MODEL_ID),
|
|
554
554
|
replicateModelId: z2.string().optional(),
|
|
555
|
-
inputOverrides: z2.record(z2.string(), z2.unknown()).default({})
|
|
555
|
+
inputOverrides: z2.record(z2.string(), z2.unknown()).default({}),
|
|
556
|
+
maxAttempts: z2.number().int().min(1).max(10).default(4)
|
|
556
557
|
})
|
|
557
558
|
);
|
|
558
559
|
var notificationsSettingsSchema = z2.object({
|
|
@@ -562,6 +563,7 @@ var appSettingsSchema = z2.object({
|
|
|
562
563
|
model: z2.string().default("deepseek/deepseek-v4-pro"),
|
|
563
564
|
modelSettings: modelSettingsSchema.default(modelSettingsSchema.parse({})),
|
|
564
565
|
modelRequestTimeoutMs: z2.number().int().positive().default(9e4),
|
|
566
|
+
modelRequestMaxAttempts: z2.number().int().min(1).max(10).default(4),
|
|
565
567
|
t2i: baseT2ISettingsSchema.default(baseT2ISettingsSchema.parse({})),
|
|
566
568
|
notifications: notificationsSettingsSchema.default(notificationsSettingsSchema.parse({})),
|
|
567
569
|
contentTargets: z2.array(contentTargetSchema).min(1).refine((targets) => targets.filter((target) => target.role === "primary").length === 1, {
|
|
@@ -580,6 +582,7 @@ var envSettingsSchema = z2.object({
|
|
|
580
582
|
maxTokens: z2.number().int().positive().optional(),
|
|
581
583
|
topP: z2.number().min(0).max(1).optional(),
|
|
582
584
|
modelRequestTimeoutMs: z2.number().int().positive().optional(),
|
|
585
|
+
modelRequestMaxAttempts: z2.number().int().min(1).max(10).optional(),
|
|
583
586
|
notificationsEnabled: z2.boolean().optional(),
|
|
584
587
|
style: z2.enum(writingStyleValues).optional(),
|
|
585
588
|
intent: z2.enum(contentIntentValues).optional(),
|
|
@@ -624,6 +627,7 @@ function readEnvSettings(env = process.env) {
|
|
|
624
627
|
maxTokens: parseNumber(env.IDEON_MAX_TOKENS),
|
|
625
628
|
topP: parseNumber(env.IDEON_TOP_P),
|
|
626
629
|
modelRequestTimeoutMs: parseNumber(env.IDEON_MODEL_REQUEST_TIMEOUT_MS),
|
|
630
|
+
modelRequestMaxAttempts: parseNumber(env.IDEON_MODEL_REQUEST_MAX_ATTEMPTS),
|
|
627
631
|
notificationsEnabled: parseBoolean(env.IDEON_NOTIFICATIONS_ENABLED),
|
|
628
632
|
style: env.IDEON_STYLE,
|
|
629
633
|
intent: env.IDEON_INTENT,
|
|
@@ -1424,7 +1428,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
1424
1428
|
// package.json
|
|
1425
1429
|
var package_default = {
|
|
1426
1430
|
name: "@telepat/ideon",
|
|
1427
|
-
version: "0.1.
|
|
1431
|
+
version: "0.1.31",
|
|
1428
1432
|
description: "CLI for generating rich articles and images from ideas.",
|
|
1429
1433
|
type: "module",
|
|
1430
1434
|
repository: {
|
|
@@ -1547,6 +1551,7 @@ async function resolveRunInput(input) {
|
|
|
1547
1551
|
...job?.settings ?? {},
|
|
1548
1552
|
...envSettings.model ? { model: envSettings.model } : {},
|
|
1549
1553
|
...envSettings.modelRequestTimeoutMs !== void 0 ? { modelRequestTimeoutMs: envSettings.modelRequestTimeoutMs } : {},
|
|
1554
|
+
...envSettings.modelRequestMaxAttempts !== void 0 ? { modelRequestMaxAttempts: envSettings.modelRequestMaxAttempts } : {},
|
|
1550
1555
|
...envSettings.notificationsEnabled !== void 0 ? {
|
|
1551
1556
|
notifications: {
|
|
1552
1557
|
...savedSettings.notifications,
|
|
@@ -3181,6 +3186,212 @@ function buildImagePromptMessages(plan, image, section) {
|
|
|
3181
3186
|
];
|
|
3182
3187
|
}
|
|
3183
3188
|
|
|
3189
|
+
// src/llm/retry.ts
|
|
3190
|
+
var DEFAULT_BASE_BACKOFF_MS = 1500;
|
|
3191
|
+
var DEFAULT_MAX_BACKOFF_MS = 6e4;
|
|
3192
|
+
var DEFAULT_JITTER_MS = 250;
|
|
3193
|
+
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 409, 425, 429]);
|
|
3194
|
+
var TRANSIENT_ERROR_PATTERN = /timeout|network|fetch|temporarily|aborted|ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|ETIMEDOUT|socket hang up/i;
|
|
3195
|
+
async function withRetry(op, opts) {
|
|
3196
|
+
const maxAttempts = Math.max(1, Math.floor(opts.maxAttempts));
|
|
3197
|
+
const baseBackoffMs = opts.baseBackoffMs ?? DEFAULT_BASE_BACKOFF_MS;
|
|
3198
|
+
const maxBackoffMs = opts.maxBackoffMs ?? DEFAULT_MAX_BACKOFF_MS;
|
|
3199
|
+
const jitterMs = opts.jitterMs ?? DEFAULT_JITTER_MS;
|
|
3200
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
3201
|
+
const randomFraction = opts.randomFraction ?? Math.random;
|
|
3202
|
+
let lastError;
|
|
3203
|
+
let lastClassification = null;
|
|
3204
|
+
let attemptsMade = 0;
|
|
3205
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
3206
|
+
attemptsMade = attempt;
|
|
3207
|
+
try {
|
|
3208
|
+
return await op(attempt);
|
|
3209
|
+
} catch (error) {
|
|
3210
|
+
lastError = error;
|
|
3211
|
+
const classification = classifyHttpError(error);
|
|
3212
|
+
lastClassification = classification;
|
|
3213
|
+
if (!classification.retryable || attempt >= maxAttempts) {
|
|
3214
|
+
break;
|
|
3215
|
+
}
|
|
3216
|
+
const delayMs = computeDelayMs({
|
|
3217
|
+
retryAfterMs: classification.retryAfterMs,
|
|
3218
|
+
attempt,
|
|
3219
|
+
baseBackoffMs,
|
|
3220
|
+
maxBackoffMs,
|
|
3221
|
+
jitterMs,
|
|
3222
|
+
randomFraction
|
|
3223
|
+
});
|
|
3224
|
+
opts.onRetry?.({
|
|
3225
|
+
attempt,
|
|
3226
|
+
delayMs,
|
|
3227
|
+
reason: classification.reason,
|
|
3228
|
+
statusCode: classification.statusCode
|
|
3229
|
+
});
|
|
3230
|
+
await sleep(delayMs);
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
throw buildFinalError(opts.operationLabel, attemptsMade, lastError, lastClassification);
|
|
3234
|
+
}
|
|
3235
|
+
function computeDelayMs(input) {
|
|
3236
|
+
if (typeof input.retryAfterMs === "number" && input.retryAfterMs > 0) {
|
|
3237
|
+
return Math.min(input.maxBackoffMs, input.retryAfterMs);
|
|
3238
|
+
}
|
|
3239
|
+
const exponential = input.baseBackoffMs * 2 ** (input.attempt - 1);
|
|
3240
|
+
const capped = Math.min(input.maxBackoffMs, exponential);
|
|
3241
|
+
const jitter = input.jitterMs > 0 ? input.randomFraction() * input.jitterMs : 0;
|
|
3242
|
+
return Math.floor(capped + jitter);
|
|
3243
|
+
}
|
|
3244
|
+
function classifyHttpError(error) {
|
|
3245
|
+
if (!error) {
|
|
3246
|
+
return { retryable: true, reason: "Empty error value treated as transient." };
|
|
3247
|
+
}
|
|
3248
|
+
const message = errorMessage(error);
|
|
3249
|
+
const statusFromObject = extractStatusFromObject(error);
|
|
3250
|
+
const statusFromMessage = statusFromObject ?? extractStatusFromMessage(message);
|
|
3251
|
+
const retryAfterMs = extractRetryAfterMs(error, message);
|
|
3252
|
+
if (statusFromMessage !== void 0) {
|
|
3253
|
+
if (RETRYABLE_STATUS_CODES.has(statusFromMessage) || statusFromMessage >= 500) {
|
|
3254
|
+
return {
|
|
3255
|
+
retryable: true,
|
|
3256
|
+
statusCode: statusFromMessage,
|
|
3257
|
+
retryAfterMs,
|
|
3258
|
+
reason: `HTTP ${statusFromMessage}`
|
|
3259
|
+
};
|
|
3260
|
+
}
|
|
3261
|
+
return {
|
|
3262
|
+
retryable: false,
|
|
3263
|
+
statusCode: statusFromMessage,
|
|
3264
|
+
reason: `HTTP ${statusFromMessage}`
|
|
3265
|
+
};
|
|
3266
|
+
}
|
|
3267
|
+
if (TRANSIENT_ERROR_PATTERN.test(message)) {
|
|
3268
|
+
return {
|
|
3269
|
+
retryable: true,
|
|
3270
|
+
retryAfterMs,
|
|
3271
|
+
reason: "Transient network or timeout error."
|
|
3272
|
+
};
|
|
3273
|
+
}
|
|
3274
|
+
return {
|
|
3275
|
+
retryable: true,
|
|
3276
|
+
retryAfterMs,
|
|
3277
|
+
reason: "Unknown error treated as transient."
|
|
3278
|
+
};
|
|
3279
|
+
}
|
|
3280
|
+
function extractStatusFromObject(error) {
|
|
3281
|
+
if (typeof error !== "object" || error === null) {
|
|
3282
|
+
return void 0;
|
|
3283
|
+
}
|
|
3284
|
+
const record = error;
|
|
3285
|
+
const direct = numberFrom(record.status);
|
|
3286
|
+
if (direct !== void 0) {
|
|
3287
|
+
return direct;
|
|
3288
|
+
}
|
|
3289
|
+
const response = record.response;
|
|
3290
|
+
if (typeof response === "object" && response !== null) {
|
|
3291
|
+
const responseStatus = numberFrom(response.status);
|
|
3292
|
+
if (responseStatus !== void 0) {
|
|
3293
|
+
return responseStatus;
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
return void 0;
|
|
3297
|
+
}
|
|
3298
|
+
function extractStatusFromMessage(message) {
|
|
3299
|
+
const match = message.match(/status\s+(\d{3})/i);
|
|
3300
|
+
if (!match) {
|
|
3301
|
+
return void 0;
|
|
3302
|
+
}
|
|
3303
|
+
const parsed = Number.parseInt(match[1], 10);
|
|
3304
|
+
return Number.isInteger(parsed) ? parsed : void 0;
|
|
3305
|
+
}
|
|
3306
|
+
function extractRetryAfterMs(error, message) {
|
|
3307
|
+
const headerSeconds = extractRetryAfterHeader(error);
|
|
3308
|
+
if (headerSeconds !== void 0) {
|
|
3309
|
+
return Math.round(headerSeconds * 1e3);
|
|
3310
|
+
}
|
|
3311
|
+
const bodyMatch = message.match(/"?retry_after"?\s*[:=]\s*(\d+(?:\.\d+)?)/i);
|
|
3312
|
+
if (bodyMatch) {
|
|
3313
|
+
const seconds = Number.parseFloat(bodyMatch[1]);
|
|
3314
|
+
if (Number.isFinite(seconds) && seconds > 0) {
|
|
3315
|
+
return Math.round(seconds * 1e3);
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
return void 0;
|
|
3319
|
+
}
|
|
3320
|
+
function extractRetryAfterHeader(error) {
|
|
3321
|
+
if (typeof error !== "object" || error === null) {
|
|
3322
|
+
return void 0;
|
|
3323
|
+
}
|
|
3324
|
+
const response = error.response;
|
|
3325
|
+
if (typeof response !== "object" || response === null) {
|
|
3326
|
+
return void 0;
|
|
3327
|
+
}
|
|
3328
|
+
const headers = response.headers;
|
|
3329
|
+
if (!headers) {
|
|
3330
|
+
return void 0;
|
|
3331
|
+
}
|
|
3332
|
+
let rawValue;
|
|
3333
|
+
if (typeof headers === "object" && typeof headers.get === "function") {
|
|
3334
|
+
rawValue = headers.get("retry-after");
|
|
3335
|
+
} else if (typeof headers === "object") {
|
|
3336
|
+
const entries = headers;
|
|
3337
|
+
rawValue = entries["retry-after"] ?? entries["Retry-After"];
|
|
3338
|
+
}
|
|
3339
|
+
if (typeof rawValue !== "string" && typeof rawValue !== "number") {
|
|
3340
|
+
return void 0;
|
|
3341
|
+
}
|
|
3342
|
+
const stringValue = String(rawValue).trim();
|
|
3343
|
+
if (!stringValue) {
|
|
3344
|
+
return void 0;
|
|
3345
|
+
}
|
|
3346
|
+
const numeric = Number.parseFloat(stringValue);
|
|
3347
|
+
if (Number.isFinite(numeric) && numeric > 0) {
|
|
3348
|
+
return numeric;
|
|
3349
|
+
}
|
|
3350
|
+
const dateMs = Date.parse(stringValue);
|
|
3351
|
+
if (Number.isFinite(dateMs)) {
|
|
3352
|
+
const diffSeconds = (dateMs - Date.now()) / 1e3;
|
|
3353
|
+
return diffSeconds > 0 ? diffSeconds : void 0;
|
|
3354
|
+
}
|
|
3355
|
+
return void 0;
|
|
3356
|
+
}
|
|
3357
|
+
function numberFrom(value2) {
|
|
3358
|
+
if (typeof value2 === "number" && Number.isFinite(value2)) {
|
|
3359
|
+
return value2;
|
|
3360
|
+
}
|
|
3361
|
+
if (typeof value2 === "string") {
|
|
3362
|
+
const parsed = Number.parseFloat(value2);
|
|
3363
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
3364
|
+
}
|
|
3365
|
+
return void 0;
|
|
3366
|
+
}
|
|
3367
|
+
function errorMessage(error) {
|
|
3368
|
+
if (error instanceof Error) {
|
|
3369
|
+
return error.message;
|
|
3370
|
+
}
|
|
3371
|
+
if (typeof error === "string") {
|
|
3372
|
+
return error;
|
|
3373
|
+
}
|
|
3374
|
+
try {
|
|
3375
|
+
return JSON.stringify(error);
|
|
3376
|
+
} catch {
|
|
3377
|
+
return String(error);
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
function buildFinalError(operationLabel, attempts, cause, classification) {
|
|
3381
|
+
const detail = errorMessage(cause) || classification?.reason || "unknown error";
|
|
3382
|
+
const message = `${operationLabel} failed after ${attempts} attempt${attempts === 1 ? "" : "s"}: ${detail}`;
|
|
3383
|
+
const wrapped = new Error(message, cause instanceof Error ? { cause } : void 0);
|
|
3384
|
+
if (!(cause instanceof Error) && cause !== void 0) {
|
|
3385
|
+
wrapped.cause = cause;
|
|
3386
|
+
}
|
|
3387
|
+
return wrapped;
|
|
3388
|
+
}
|
|
3389
|
+
function defaultSleep(ms) {
|
|
3390
|
+
return new Promise((resolve) => {
|
|
3391
|
+
setTimeout(resolve, ms);
|
|
3392
|
+
});
|
|
3393
|
+
}
|
|
3394
|
+
|
|
3184
3395
|
// src/pipeline/analytics.ts
|
|
3185
3396
|
var LLM_USD_PER_1K_TOKENS = {
|
|
3186
3397
|
// AUTO-GENERATED:OPENROUTER_PRICING_START
|
|
@@ -3433,8 +3644,26 @@ async function renderExpandedImages({
|
|
|
3433
3644
|
...replicateModelOverride && isReplicateModelIdForFamily(family, replicateModelOverride) ? { replicateModel: replicateModelOverride } : {}
|
|
3434
3645
|
};
|
|
3435
3646
|
const renderStartedAtMs = Date.now();
|
|
3647
|
+
let attemptsMade = 0;
|
|
3648
|
+
let retryCount = 0;
|
|
3649
|
+
let retryBackoffMs = 0;
|
|
3436
3650
|
try {
|
|
3437
|
-
const result = await
|
|
3651
|
+
const result = await withRetry(
|
|
3652
|
+
() => {
|
|
3653
|
+
attemptsMade += 1;
|
|
3654
|
+
return limn.generate(prompt.prompt, family, limnOptions);
|
|
3655
|
+
},
|
|
3656
|
+
{
|
|
3657
|
+
operationLabel: `Replicate ${prompt.kind} image (${prompt.id})`,
|
|
3658
|
+
maxAttempts: settings.t2i.maxAttempts,
|
|
3659
|
+
baseBackoffMs: 1500,
|
|
3660
|
+
maxBackoffMs: 6e4,
|
|
3661
|
+
onRetry({ delayMs }) {
|
|
3662
|
+
retryCount += 1;
|
|
3663
|
+
retryBackoffMs += delayMs;
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
);
|
|
3438
3667
|
const ext = mimeTypeToExtension(result.mimeType);
|
|
3439
3668
|
const liveFileName = `${prompt.kind === "cover" ? "cover" : `inline-${prompt.anchorAfterSection}`}-${index + 1}.${ext}`;
|
|
3440
3669
|
const liveOutputPath = path6.join(assetDir, liveFileName);
|
|
@@ -3456,9 +3685,9 @@ async function renderExpandedImages({
|
|
|
3456
3685
|
kind: prompt.kind,
|
|
3457
3686
|
modelId: result.modelSlug,
|
|
3458
3687
|
durationMs: result.analytics.totalDurationMs,
|
|
3459
|
-
attempts:
|
|
3460
|
-
retries:
|
|
3461
|
-
retryBackoffMs
|
|
3688
|
+
attempts: attemptsMade,
|
|
3689
|
+
retries: retryCount,
|
|
3690
|
+
retryBackoffMs,
|
|
3462
3691
|
outputBytes: result.image.byteLength,
|
|
3463
3692
|
costUsd: result.analytics.totalEstimatedCostUsd,
|
|
3464
3693
|
costSource
|
|
@@ -3472,9 +3701,9 @@ async function renderExpandedImages({
|
|
|
3472
3701
|
startedAt: new Date(renderStartedAtMs).toISOString(),
|
|
3473
3702
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3474
3703
|
durationMs: result.analytics.totalDurationMs,
|
|
3475
|
-
attempts:
|
|
3476
|
-
retries:
|
|
3477
|
-
retryBackoffMs
|
|
3704
|
+
attempts: attemptsMade,
|
|
3705
|
+
retries: retryCount,
|
|
3706
|
+
retryBackoffMs,
|
|
3478
3707
|
status: "succeeded",
|
|
3479
3708
|
prompt: prompt.prompt,
|
|
3480
3709
|
input: {},
|
|
@@ -3491,9 +3720,9 @@ async function renderExpandedImages({
|
|
|
3491
3720
|
startedAt: new Date(renderStartedAtMs).toISOString(),
|
|
3492
3721
|
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3493
3722
|
durationMs,
|
|
3494
|
-
attempts: 1,
|
|
3495
|
-
retries:
|
|
3496
|
-
retryBackoffMs
|
|
3723
|
+
attempts: Math.max(attemptsMade, 1),
|
|
3724
|
+
retries: retryCount,
|
|
3725
|
+
retryBackoffMs,
|
|
3497
3726
|
status: "failed",
|
|
3498
3727
|
prompt: prompt.prompt,
|
|
3499
3728
|
input: {},
|
|
@@ -3553,8 +3782,9 @@ var OpenRouterClient = class {
|
|
|
3553
3782
|
}
|
|
3554
3783
|
async requestStructured(request) {
|
|
3555
3784
|
let aggregatedMetrics = null;
|
|
3785
|
+
const maxParseAttempts = Math.max(1, request.settings.modelRequestMaxAttempts);
|
|
3556
3786
|
try {
|
|
3557
|
-
for (let attempt = 0; attempt <
|
|
3787
|
+
for (let attempt = 0; attempt < maxParseAttempts; attempt += 1) {
|
|
3558
3788
|
const response = await this.sendCompletion({
|
|
3559
3789
|
messages: request.messages,
|
|
3560
3790
|
settings: request.settings,
|
|
@@ -3579,7 +3809,7 @@ var OpenRouterClient = class {
|
|
|
3579
3809
|
request.onMetrics?.(aggregatedMetrics);
|
|
3580
3810
|
return structured;
|
|
3581
3811
|
} catch (parseError) {
|
|
3582
|
-
if (attempt <
|
|
3812
|
+
if (attempt < maxParseAttempts - 1 && shouldRetryStructuredParseError(parseError)) {
|
|
3583
3813
|
const backoff = backoffMs(attempt);
|
|
3584
3814
|
aggregatedMetrics = recordParseRetryMetrics(aggregatedMetrics, backoff);
|
|
3585
3815
|
await wait(backoff);
|
|
@@ -3645,7 +3875,8 @@ var OpenRouterClient = class {
|
|
|
3645
3875
|
let attempts = 0;
|
|
3646
3876
|
let retries = 0;
|
|
3647
3877
|
let retryBackoffMs = 0;
|
|
3648
|
-
|
|
3878
|
+
const maxAttempts = Math.max(1, settings.modelRequestMaxAttempts);
|
|
3879
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
3649
3880
|
attempts = attempt + 1;
|
|
3650
3881
|
const attemptStartedAtMs = Date.now();
|
|
3651
3882
|
const controller = new AbortController();
|
|
@@ -3695,8 +3926,9 @@ var OpenRouterClient = class {
|
|
|
3695
3926
|
const providerMessage = json?.error?.message ?? `OpenRouter request failed with status ${response.status}`;
|
|
3696
3927
|
const raw = json?.error?.metadata?.raw;
|
|
3697
3928
|
const message = raw ? `${raw} (OpenRouter: ${providerMessage})` : providerMessage;
|
|
3698
|
-
if (shouldRetryStatus(response.status) && attempt <
|
|
3699
|
-
const
|
|
3929
|
+
if (shouldRetryStatus(response.status) && attempt < maxAttempts - 1) {
|
|
3930
|
+
const advisedMs = extractRetryAfterFromResponse(response, json, rawBody);
|
|
3931
|
+
const backoff = advisedMs !== null ? Math.min(MAX_RETRY_BACKOFF_MS, advisedMs) : backoffMs(attempt);
|
|
3700
3932
|
retries += 1;
|
|
3701
3933
|
retryBackoffMs += backoff;
|
|
3702
3934
|
onInteraction?.({
|
|
@@ -3722,7 +3954,7 @@ var OpenRouterClient = class {
|
|
|
3722
3954
|
throw new Error(message);
|
|
3723
3955
|
}
|
|
3724
3956
|
const content = json.choices?.[0]?.message?.content;
|
|
3725
|
-
if (!content && attempt <
|
|
3957
|
+
if (!content && attempt < maxAttempts - 1) {
|
|
3726
3958
|
const backoff = backoffMs(attempt);
|
|
3727
3959
|
retries += 1;
|
|
3728
3960
|
retryBackoffMs += backoff;
|
|
@@ -3798,7 +4030,7 @@ var OpenRouterClient = class {
|
|
|
3798
4030
|
responseBody: responseBodyRaw,
|
|
3799
4031
|
errorMessage: lastError.message
|
|
3800
4032
|
});
|
|
3801
|
-
if (attempt <
|
|
4033
|
+
if (attempt < maxAttempts - 1 && shouldRetryError(lastError)) {
|
|
3802
4034
|
const backoff = backoffMs(attempt);
|
|
3803
4035
|
retries += 1;
|
|
3804
4036
|
retryBackoffMs += backoff;
|
|
@@ -3925,6 +4157,61 @@ function normalizeClientError(error, timeoutMs) {
|
|
|
3925
4157
|
function backoffMs(attempt) {
|
|
3926
4158
|
return 500 * (attempt + 1);
|
|
3927
4159
|
}
|
|
4160
|
+
var MAX_RETRY_BACKOFF_MS = 6e4;
|
|
4161
|
+
function extractRetryAfterFromResponse(response, json, rawBody) {
|
|
4162
|
+
const headerValue = typeof response.headers?.get === "function" ? response.headers.get("retry-after") : null;
|
|
4163
|
+
if (headerValue) {
|
|
4164
|
+
const numeric = Number.parseFloat(headerValue);
|
|
4165
|
+
if (Number.isFinite(numeric) && numeric > 0) {
|
|
4166
|
+
return Math.round(numeric * 1e3);
|
|
4167
|
+
}
|
|
4168
|
+
const dateMs = Date.parse(headerValue);
|
|
4169
|
+
if (Number.isFinite(dateMs)) {
|
|
4170
|
+
const diff = dateMs - Date.now();
|
|
4171
|
+
if (diff > 0) {
|
|
4172
|
+
return diff;
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
const metadata = json?.error?.metadata;
|
|
4177
|
+
if (metadata) {
|
|
4178
|
+
const fromMetadata = metadata["retry_after"] ?? metadata["retryAfter"];
|
|
4179
|
+
const numeric = toFiniteNumber(fromMetadata);
|
|
4180
|
+
if (numeric !== null && numeric > 0) {
|
|
4181
|
+
return Math.round(numeric * 1e3);
|
|
4182
|
+
}
|
|
4183
|
+
const rawCandidate = metadata["raw"];
|
|
4184
|
+
if (typeof rawCandidate === "string") {
|
|
4185
|
+
const fromRaw = extractRetryAfterFromString(rawCandidate);
|
|
4186
|
+
if (fromRaw !== null) {
|
|
4187
|
+
return fromRaw;
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
const fromBody = extractRetryAfterFromString(rawBody);
|
|
4192
|
+
if (fromBody !== null) {
|
|
4193
|
+
return fromBody;
|
|
4194
|
+
}
|
|
4195
|
+
return null;
|
|
4196
|
+
}
|
|
4197
|
+
function extractRetryAfterFromString(value2) {
|
|
4198
|
+
const bodyMatch = value2.match(/\\?"retry_after\\?"\s*:\s*(\d+(?:\.\d+)?)/i);
|
|
4199
|
+
if (!bodyMatch) {
|
|
4200
|
+
return null;
|
|
4201
|
+
}
|
|
4202
|
+
const numeric = Number.parseFloat(bodyMatch[1]);
|
|
4203
|
+
return Number.isFinite(numeric) && numeric > 0 ? Math.round(numeric * 1e3) : null;
|
|
4204
|
+
}
|
|
4205
|
+
function toFiniteNumber(value2) {
|
|
4206
|
+
if (typeof value2 === "number" && Number.isFinite(value2)) {
|
|
4207
|
+
return value2;
|
|
4208
|
+
}
|
|
4209
|
+
if (typeof value2 === "string") {
|
|
4210
|
+
const parsed = Number.parseFloat(value2);
|
|
4211
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
4212
|
+
}
|
|
4213
|
+
return null;
|
|
4214
|
+
}
|
|
3928
4215
|
function aggregateLlmMetrics(total, next) {
|
|
3929
4216
|
if (!total) {
|
|
3930
4217
|
return { ...next };
|
|
@@ -4333,7 +4620,7 @@ async function runPipelineShell(input, options = {}) {
|
|
|
4333
4620
|
const llmInteractions = [];
|
|
4334
4621
|
const t2iInteractions = [];
|
|
4335
4622
|
let writeSession;
|
|
4336
|
-
const applyRetryUpdate = (stageId, retryIncrement,
|
|
4623
|
+
const applyRetryUpdate = (stageId, retryIncrement, errorMessage2) => {
|
|
4337
4624
|
if (retryIncrement <= 0) {
|
|
4338
4625
|
return;
|
|
4339
4626
|
}
|
|
@@ -4344,7 +4631,7 @@ async function runPipelineShell(input, options = {}) {
|
|
|
4344
4631
|
const existing = stageRetryState.get(stageId) ?? { retries: 0, lastError: null };
|
|
4345
4632
|
const next = {
|
|
4346
4633
|
retries: existing.retries + retryIncrement,
|
|
4347
|
-
lastError:
|
|
4634
|
+
lastError: errorMessage2 && errorMessage2.trim().length > 0 ? errorMessage2 : existing.lastError
|
|
4348
4635
|
};
|
|
4349
4636
|
stageRetryState.set(stageId, next);
|
|
4350
4637
|
stages[stageIndex] = {
|
|
@@ -7384,7 +7671,8 @@ function SettingsFlow({ initialSettings, initialSecrets, onDone }) {
|
|
|
7384
7671
|
t2i: {
|
|
7385
7672
|
modelId: item.value,
|
|
7386
7673
|
replicateModelId: current.t2i.replicateModelId && isReplicateModelIdForFamily(item.value, current.t2i.replicateModelId) ? current.t2i.replicateModelId : void 0,
|
|
7387
|
-
inputOverrides: {}
|
|
7674
|
+
inputOverrides: {},
|
|
7675
|
+
maxAttempts: current.t2i.maxAttempts
|
|
7388
7676
|
}
|
|
7389
7677
|
}));
|
|
7390
7678
|
setShowModelSelect(false);
|
|
@@ -9710,13 +9998,13 @@ function PipelinePresenter({
|
|
|
9710
9998
|
prompt,
|
|
9711
9999
|
stages,
|
|
9712
10000
|
result,
|
|
9713
|
-
errorMessage
|
|
10001
|
+
errorMessage: errorMessage2
|
|
9714
10002
|
}) {
|
|
9715
10003
|
const visibleStages = getVisibleStages(stages);
|
|
9716
10004
|
const maxVisibleItems = computeMaxVisibleItemsPerStage({
|
|
9717
10005
|
terminalRows: process.stdout.rows,
|
|
9718
10006
|
visibleStageCount: visibleStages.length,
|
|
9719
|
-
hasError: Boolean(
|
|
10007
|
+
hasError: Boolean(errorMessage2),
|
|
9720
10008
|
hasResult: Boolean(result)
|
|
9721
10009
|
});
|
|
9722
10010
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", paddingX: 1, children: [
|
|
@@ -9732,7 +10020,7 @@ function PipelinePresenter({
|
|
|
9732
10020
|
},
|
|
9733
10021
|
stage.id
|
|
9734
10022
|
)) }),
|
|
9735
|
-
|
|
10023
|
+
errorMessage2 ? /* @__PURE__ */ jsx5(Box4, { marginTop: 1, borderStyle: "round", borderColor: "red", paddingX: 1, children: /* @__PURE__ */ jsx5(Text4, { color: "red", children: errorMessage2 }) }) : null,
|
|
9736
10024
|
result ? /* @__PURE__ */ jsx5(Box4, { marginTop: 1, children: /* @__PURE__ */ jsx5(FinalSummary, { artifact: result.artifact, analytics: result.analytics }) }) : null
|
|
9737
10025
|
] });
|
|
9738
10026
|
}
|
|
@@ -10267,7 +10555,7 @@ function WriteApp({
|
|
|
10267
10555
|
() => createInitialStages()
|
|
10268
10556
|
);
|
|
10269
10557
|
const [result, setResult] = useState4(null);
|
|
10270
|
-
const [
|
|
10558
|
+
const [errorMessage2, setErrorMessage] = useState4(null);
|
|
10271
10559
|
useEffect3(() => {
|
|
10272
10560
|
let mounted = true;
|
|
10273
10561
|
void (async () => {
|
|
@@ -10320,7 +10608,7 @@ function WriteApp({
|
|
|
10320
10608
|
};
|
|
10321
10609
|
}, [dryRun, enrichLinks2, input, links, unlinks, maxLinks, maxImages, onError, runMode]);
|
|
10322
10610
|
useEffect3(() => {
|
|
10323
|
-
if (!result && !
|
|
10611
|
+
if (!result && !errorMessage2) {
|
|
10324
10612
|
return;
|
|
10325
10613
|
}
|
|
10326
10614
|
const exitTimer = setTimeout(() => {
|
|
@@ -10329,8 +10617,8 @@ function WriteApp({
|
|
|
10329
10617
|
return () => {
|
|
10330
10618
|
clearTimeout(exitTimer);
|
|
10331
10619
|
};
|
|
10332
|
-
}, [
|
|
10333
|
-
return /* @__PURE__ */ jsx7(PipelinePresenter, { prompt: input.idea, stages, result, errorMessage });
|
|
10620
|
+
}, [errorMessage2, exit, result]);
|
|
10621
|
+
return /* @__PURE__ */ jsx7(PipelinePresenter, { prompt: input.idea, stages, result, errorMessage: errorMessage2 });
|
|
10334
10622
|
}
|
|
10335
10623
|
async function runWriteCommand(options) {
|
|
10336
10624
|
const input = await resolveInputWithInteractiveIdeaFallback(options);
|