vidspotai-shared 1.0.126 → 1.0.127

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.
@@ -1 +1 @@
1
- {"version":3,"file":"aiGenFactory.service.d.ts","sourceRoot":"","sources":["../../../src/services/aiGen/aiGenFactory.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EACL,iBAAiB,EAEjB,eAAe,EACf,aAAa,EACb,cAAc,EACd,qBAAqB,EAErB,qBAAqB,EAErB,eAAe,EACf,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAgBhC,KAAK,uBAAuB,GAAG,UAAU,wBAAwB,CAAC;AASlE,eAAO,MAAM,gBAAgB,EAAE,OAAO,CACpC,MAAM,CAAC,eAAe,EAAE,uBAAuB,CAAC,CA+FjD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CACnC,iBAAiB,EACjB,uBAAuB,CAsDxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CACnC,eAAe,EACf,uBAAuB,CAMxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CACjC,aAAa,EACb,uBAAuB,CAGxB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,uBAAuB,CAsBzE,CAAC;AAEJ;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,qBAAqB,EACrB,uBAAuB,CAGxB,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,qBAAqB,EACrB,uBAAuB,CAGxB,CAAC;AAEF,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,qBAAqB,GAC9B,wBAAwB,CAM1B;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,qBAAqB,GAC9B,wBAAwB,CAM1B;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,WAAW,GACpB,wBAAwB,CAmB1B"}
1
+ {"version":3,"file":"aiGenFactory.service.d.ts","sourceRoot":"","sources":["../../../src/services/aiGen/aiGenFactory.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EACL,iBAAiB,EAEjB,eAAe,EACf,aAAa,EACb,cAAc,EACd,qBAAqB,EAErB,qBAAqB,EAErB,eAAe,EACf,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAgBhC,KAAK,uBAAuB,GAAG,UAAU,wBAAwB,CAAC;AASlE,eAAO,MAAM,gBAAgB,EAAE,OAAO,CACpC,MAAM,CAAC,eAAe,EAAE,uBAAuB,CAAC,CAiGjD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CACnC,iBAAiB,EACjB,uBAAuB,CAsDxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CACnC,eAAe,EACf,uBAAuB,CAMxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CACjC,aAAa,EACb,uBAAuB,CAGxB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,uBAAuB,CAsBzE,CAAC;AAEJ;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,qBAAqB,EACrB,uBAAuB,CAGxB,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,qBAAqB,EACrB,uBAAuB,CAGxB,CAAC;AAEF,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,qBAAqB,GAC9B,wBAAwB,CAM1B;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,qBAAqB,GAC9B,wBAAwB,CAM1B;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,WAAW,GACpB,wBAAwB,CAmB1B"}
@@ -82,6 +82,8 @@ exports.videoServicesMap = {
82
82
  "kling-motion-control": kling_service_1.KlingService,
83
83
  "kling-lip-sync": kling_service_1.KlingService,
84
84
  // --- MiniMax ---
85
+ "minimax-h3": minimax_service_1.MinimaxService,
86
+ "minimax-h3-max": minimax_service_1.MinimaxService,
85
87
  "minimax-hailuo-2.3-fast": minimax_service_1.MinimaxService,
86
88
  "minimax-hailuo-2.3": minimax_service_1.MinimaxService,
87
89
  "minimax-hailuo-02": minimax_service_1.MinimaxService,
@@ -1 +1 @@
1
- {"version":3,"file":"bytedance.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/bytedance/bytedance.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AA6BlB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQlF;AAsBD,qBAAa,gBAAiB,SAAQ,wBAAwB;IAC5D,OAAO,CAAC,QAAQ,CAAC,OAAO,CACsD;IAQ9E,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,WAAW;IAIb,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAoP3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAqIjD;;;;;;;;;;;OAWG;IACG,aAAa,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAyIlF,+EAA+E;YACjE,mBAAmB;IAoCjC;;;OAGG;YACW,eAAe;IAgD7B,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAsFjD"}
1
+ {"version":3,"file":"bytedance.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/bytedance/bytedance.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AA8BlB;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQlF;AAsBD,qBAAa,gBAAiB,SAAQ,wBAAwB;IAC5D,OAAO,CAAC,QAAQ,CAAC,OAAO,CACsD;IAQ9E,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,WAAW;IAIb,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IA+Q3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkJjD;;;;;;;;;;;OAWG;IACG,aAAa,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAyIlF,+EAA+E;YACjE,mBAAmB;IAoCjC;;;OAGG;YACW,eAAe;IAgD7B,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM;CAsFjD"}
@@ -16,6 +16,7 @@ const image_model_1 = require("../../../../models/image.model");
16
16
  const helpers_2 = require("../../../../utils/helpers");
17
17
  const errors_1 = require("../../../../utils/errors");
18
18
  const logger_1 = require("../../../../utils/logger");
19
+ const transientRetry_1 = require("../../transientRetry");
19
20
  const crypto_1 = __importDefault(require("crypto"));
20
21
  const SEEDREAM_URL = "https://ark.ap-southeast.bytepluses.com/api/v3/images/generations";
21
22
  /** Long-edge-equivalent of each `size` tier (tier² = target pixel count). */
@@ -197,13 +198,32 @@ class ByteDanceService extends baseAiGenProvider_service_1.BaseAiGenProviderServ
197
198
  .digest("hex")
198
199
  .slice(0, 16);
199
200
  }
200
- const resp = await fetch(this.baseUrl, {
201
- method: "POST",
202
- headers: {
203
- "Content-Type": "application/json",
204
- Authorization: `Bearer ${process.env.BYTEDANCE_API_KEY}`,
205
- },
206
- body: JSON.stringify(body),
201
+ // ModelArk intermittently answers task creation with a 500
202
+ // InternalServiceError (prod 2026-10-06, several in an hour) that
203
+ // succeeds on resubmit. Retry 5xx only; 4xx stays permanent and 429 is
204
+ // left to the worker's rate limiter.
205
+ const resp = await (0, transientRetry_1.withTransientRetry)("bytedance:generateVideo", async () => {
206
+ const r = await fetch(this.baseUrl, {
207
+ method: "POST",
208
+ headers: {
209
+ "Content-Type": "application/json",
210
+ Authorization: `Bearer ${process.env.BYTEDANCE_API_KEY}`,
211
+ },
212
+ body: JSON.stringify(body),
213
+ });
214
+ if (r.status >= 500) {
215
+ throw new transientRetry_1.TransientHttpError(r.status, `ByteDance generateVideo failed (${r.status}): ${await r.text()}`);
216
+ }
217
+ return r;
218
+ }, {
219
+ baseBackoffMs: 2000,
220
+ onRetry: ({ attempt, maxAttempts, backoffMs, err }) => logger_1.logger.warn("Seedance: transient create error, retrying", {
221
+ modelKey: params.modelKey,
222
+ attempt,
223
+ maxAttempts,
224
+ backoffMs,
225
+ error: err.message.slice(0, 300),
226
+ }),
207
227
  });
208
228
  if (!resp.ok) {
209
229
  const errText = await resp.text();
@@ -310,6 +330,17 @@ class ByteDanceService extends baseAiGenProvider_service_1.BaseAiGenProviderServ
310
330
  errorMessage: JSON.stringify(data.error),
311
331
  };
312
332
  }
333
+ // ModelArk copies the input image to its own storage and transcodes it
334
+ // (`x-tos-process=image/format,jpg`); an image it can't decode fails
335
+ // asynchronously as InvalidParameter "Error while downloading: …ark-…
336
+ // status code: 400" (prod 2026-10-06, a user-uploaded PNG). That's the
337
+ // input file, not our system — tell the user to re-export it.
338
+ if (errCode === "InvalidParameter" && /error while downloading/i.test(errMessage)) {
339
+ return {
340
+ status: types_1.EVideoSceneStatus.FAILED,
341
+ errorMessage: "user_input_error: This model couldn't process your input image. Please re-save it as a standard JPEG or PNG (or use a different image) and try again.",
342
+ };
343
+ }
313
344
  return {
314
345
  status: types_1.EVideoSceneStatus.FAILED,
315
346
  errorMessage: data.error
@@ -1 +1 @@
1
- {"version":3,"file":"runway.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/runway/runway.service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAWlB,qBAAa,aAAc,SAAQ,wBAAwB;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;YAUlB,OAAO;IAgCf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAyJ3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAuDjD,aAAa,CAAC,EACZ,QAAQ,EACR,QAAY,EACZ,SAAiB,EACjB,SAAa,EACb,WAAW,EACX,WAAW,EACX,UAAU,EACV,KAAK,EACL,aAAa,EACb,qBAAqB,EACrB,cAAkB,EAClB,cAAkB,GACnB,EAAE,iBAAiB,GAAG,MAAM;IAwD7B;;;;;;;;OAQG;IACG,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;CA+ElC"}
1
+ {"version":3,"file":"runway.service.d.ts","sourceRoot":"","sources":["../../../../../src/services/aiGen/providers/runway/runway.service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAWlB,qBAAa,aAAc,SAAQ,wBAAwB;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;;YAUlB,OAAO;IA8Cf,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAyJ3B,gBAAgB,CAAC,EACrB,IAAI,EACJ,cAAc,EACd,cAAyB,GAC1B,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAuDjD,aAAa,CAAC,EACZ,QAAQ,EACR,QAAY,EACZ,SAAiB,EACjB,SAAa,EACb,WAAW,EACX,WAAW,EACX,UAAU,EACV,KAAK,EACL,aAAa,EACb,qBAAqB,EACrB,cAAkB,EAClB,cAAkB,GACnB,EAAE,iBAAiB,GAAG,MAAM;IAwD7B;;;;;;;;OAQG;IACG,aAAa,CACjB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;CA+ElC"}
@@ -41,6 +41,14 @@ class RunwayService extends baseAiGenProvider_service_1.BaseAiGenProviderService
41
41
  validateStatus: () => true, // manually handle status
42
42
  });
43
43
  if (response.status < 200 || response.status >= 300) {
44
+ // Runway validates promptText length server-side (1000 chars on its
45
+ // native models; prod 2026-10-06 runway-gen4.5). That's the user's
46
+ // prompt, not a system fault — surface it like every other provider.
47
+ const issues = response.status === 400 && Array.isArray(response.data?.issues) ? response.data.issues : [];
48
+ const tooLong = issues.find((i) => i.code === "too_big" && Array.isArray(i.path) && i.path[0] === "promptText");
49
+ if (tooLong) {
50
+ throw new errors_1.UserFacingError(`Your prompt is too long for this model${tooLong.maximum ? ` (max ${tooLong.maximum} characters)` : ""}. Please shorten it and try again.`, errors_1.USER_FACING_ERROR_CODES.PROMPT_TOO_LONG);
51
+ }
44
52
  throw new Error(`Runway API Error: ${response.status} ${response.statusText} - ${JSON.stringify(response.data)}`);
45
53
  }
46
54
  return response.data;
@@ -1 +1 @@
1
- {"version":3,"file":"translation.service.d.ts","sourceRoot":"","sources":["../../../src/services/translation/translation.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAiDvD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAkB,GAAG,MAAM,EAAE,CA8BjF;AA4ID;;;;;;GAMG;AACH,iBAAe,SAAS,CACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,EACpB,EAAE,GAAE,cAAkC,EACtC,OAAO,GAAE,IAAI,GAAG,IAAW,GAC1B,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAiGD;;;;;;;;;;;;;;GAcG;AACH,iBAAe,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8ChE;AAOD,eAAO,MAAM,kBAAkB;;;CAG9B,CAAC;AAGF,qDAAqD;AACrD,eAAO,MAAM,qBAAqB,kBAAY,CAAC;AAC/C,0DAA0D;AAC1D,eAAO,MAAM,UAAU,uBAAiB,CAAC"}
1
+ {"version":3,"file":"translation.service.d.ts","sourceRoot":"","sources":["../../../src/services/translation/translation.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,eAAO,MAAM,kBAAkB,uBAAuB,CAAC;AAiDvD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAkB,GAAG,MAAM,EAAE,CA8BjF;AA4ID;;;;;;GAMG;AACH,iBAAe,SAAS,CACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,EACpB,EAAE,GAAE,cAAkC,EACtC,OAAO,GAAE,IAAI,GAAG,IAAW,GAC1B,OAAO,CAAC,MAAM,CAAC,CAiBjB;AA+ID;;;;;;;;;;;;;;GAcG;AACH,iBAAe,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8ChE;AAOD,eAAO,MAAM,kBAAkB;;;CAG9B,CAAC;AAGF,qDAAqD;AACrD,eAAO,MAAM,qBAAqB,kBAAY,CAAC;AAC/C,0DAA0D;AAC1D,eAAO,MAAM,UAAU,uBAAiB,CAAC"}
@@ -276,33 +276,72 @@ async function translate(text, from, to = types_1.ELANGUAGE_CODE.en, version = "
276
276
  });
277
277
  return translated.join("");
278
278
  }
279
- /** One chunk through primary → LLM fallback. */
279
+ // 2026-10-07: chunking fixed the long-prompt failures, but the two layers
280
+ // still ran strictly in sequence, so a primary that was merely SLOW (not down)
281
+ // burned up to 2 × primaryTimeoutFor() ≈ 17s before the LLM even started —
282
+ // prod paged "slow step >30s: getTranslationWrapper" at 30-40s for short `bg`
283
+ // / `ru` prompts. Hedge instead: if the primary hasn't answered within
284
+ // HEDGE_DELAY_MS (or fails sooner), start the LLM alongside it and take
285
+ // whichever succeeds first. Healthy primaries answer well inside the delay,
286
+ // so the LLM still only runs on the slow/failing tail.
287
+ const HEDGE_DELAY_MS = 3000;
288
+ /** Resolves with the first promise to fulfil; rejects with every error only
289
+ * once all of them have rejected (Promise.any without the ES2021 lib). */
290
+ function firstSuccess(promises) {
291
+ return new Promise((resolve, reject) => {
292
+ const errors = new Array(promises.length);
293
+ let pending = promises.length;
294
+ promises.forEach((p, i) => {
295
+ p.then(resolve, (err) => {
296
+ errors[i] = err;
297
+ if (--pending === 0)
298
+ reject(errors);
299
+ });
300
+ });
301
+ });
302
+ }
303
+ /** One chunk through primary, hedged with the LLM fallback. */
280
304
  async function translateChunk(text, from, to, version) {
281
305
  // ── Layer 1: primary Cloud Function ──────────────────────────────────────
282
306
  let primaryErr;
283
- for (let attempt = 1; attempt <= PRIMARY_MAX_ATTEMPTS; attempt++) {
284
- try {
285
- return await callPrimaryTranslate(text, from, to, version);
286
- }
287
- catch (err) {
288
- primaryErr = err;
289
- if (attempt < PRIMARY_MAX_ATTEMPTS) {
290
- await new Promise((r) => setTimeout(r, 200 * attempt));
307
+ const primary = (async () => {
308
+ for (let attempt = 1; attempt <= PRIMARY_MAX_ATTEMPTS; attempt++) {
309
+ try {
310
+ return await callPrimaryTranslate(text, from, to, version);
311
+ }
312
+ catch (err) {
313
+ primaryErr = err;
314
+ if (attempt < PRIMARY_MAX_ATTEMPTS) {
315
+ await new Promise((r) => setTimeout(r, 200 * attempt));
316
+ }
291
317
  }
292
318
  }
293
- }
319
+ throw primaryErr;
320
+ })();
294
321
  // ── Layer 2: LLM fallback (the common recovery path — WARN, not Slack) ────
295
- logger_1.logger.warn("translation: primary failed, falling back to LLM", {
296
- sourceLang: from,
297
- targetLang: to,
298
- primaryAttempts: PRIMARY_MAX_ATTEMPTS,
299
- fallbackModel: FALLBACK_MODEL,
300
- err: errMsg(primaryErr),
322
+ // Starts on primary failure or after HEDGE_DELAY_MS, whichever comes first.
323
+ let startFallback;
324
+ const fallbackGate = new Promise((r) => (startFallback = r));
325
+ const hedgeTimer = setTimeout(() => startFallback(), HEDGE_DELAY_MS);
326
+ primary.catch(() => startFallback());
327
+ const fallback = fallbackGate.then(() => {
328
+ logger_1.logger.warn(primaryErr
329
+ ? "translation: primary failed, falling back to LLM"
330
+ : "translation: primary slow, hedging with LLM", {
331
+ sourceLang: from,
332
+ targetLang: to,
333
+ primaryAttempts: PRIMARY_MAX_ATTEMPTS,
334
+ hedgeDelayMs: HEDGE_DELAY_MS,
335
+ fallbackModel: FALLBACK_MODEL,
336
+ err: primaryErr ? errMsg(primaryErr) : undefined,
337
+ });
338
+ return callLlmTranslate(text, from, to);
301
339
  });
302
340
  try {
303
- return await callLlmTranslate(text, from, to);
341
+ return await firstSuccess([primary, fallback]);
304
342
  }
305
- catch (fallbackErr) {
343
+ catch (errors) {
344
+ const [, fallbackErr] = errors;
306
345
  // Both layers down — this is the only case worth a Slack alert.
307
346
  logger_1.logger.error("translation failed on BOTH primary and LLM fallback", {
308
347
  sourceLang: from,
@@ -314,6 +353,9 @@ async function translateChunk(text, from, to, version) {
314
353
  e.cause = fallbackErr;
315
354
  throw e;
316
355
  }
356
+ finally {
357
+ clearTimeout(hedgeTimer);
358
+ }
317
359
  }
318
360
  /** One un-retried LLM detect attempt. */
319
361
  async function callLlmDetectOnce(text) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vidspotai-shared",
3
- "version": "1.0.126",
3
+ "version": "1.0.127",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "exports": {