scribe-cms 0.0.14 → 0.0.15

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":"translate-progress.d.ts","sourceRoot":"","sources":["../../cli/translate-progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,qCAAqC,CAAC;AAqG7C,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACjD,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,+BAA+B,CAAC,OAAO,GAAE;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,yBAAyB,CA+QjC"}
1
+ {"version":3,"file":"translate-progress.d.ts","sourceRoot":"","sources":["../../cli/translate-progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,sBAAsB,EAEvB,MAAM,qCAAqC,CAAC;AA4G7C,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACjD,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,+BAA+B,CAAC,OAAO,GAAE;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,yBAAyB,CA8SjC"}
package/dist/index.cjs CHANGED
@@ -2840,6 +2840,13 @@ function buildOutputFormatLine(hasFrontmatter, slugStrategy) {
2840
2840
  }
2841
2841
  return slugStrategy === "localized" ? "`frontmatter` (object with translated frontmatter fields), `body` (string, full MDX body), `slug` (string)." : "`frontmatter` (object), `body` (string).";
2842
2842
  }
2843
+ function buildRetryContextSection(previousError) {
2844
+ return [
2845
+ "",
2846
+ "## Previous attempt",
2847
+ `A previous attempt at this translation was rejected with the following validation errors: ${previousError}. Produce a corrected translation that fixes these issues while re-checking every schema constraint (required fields, array minimums, maximum lengths).`
2848
+ ];
2849
+ }
2843
2850
  function buildPageTranslationPrompt(input) {
2844
2851
  const localeName = LOCALE_NAMES[input.targetLocale] ?? input.targetLocale;
2845
2852
  const localizationPrompt = input.resolved.promptOverride ?? defaultLocalizationPrompt(localeName, input.targetLocale);
@@ -2870,7 +2877,10 @@ function buildPageTranslationPrompt(input) {
2870
2877
  buildOutputFormatLine(
2871
2878
  Object.keys(input.translatableFrontmatter).length > 0,
2872
2879
  input.slugStrategy
2873
- )
2880
+ ),
2881
+ // Retry context is locale-specific suffix material: it MUST stay after the
2882
+ // EN body so the cacheable prefix is byte-identical with and without it.
2883
+ ...input.previousError ? buildRetryContextSection(input.previousError) : []
2874
2884
  ];
2875
2885
  return [...prefix, ...suffix].join("\n");
2876
2886
  }
@@ -3085,7 +3095,8 @@ function prepareTranslation(config, item, options, startedAt) {
3085
3095
  contextLabel: resolveContextLabel(enDoc, item.enSlug),
3086
3096
  translatableFrontmatter: payload.frontmatter,
3087
3097
  enBody: payload.body,
3088
- slugStrategy: type.slugStrategy
3098
+ slugStrategy: type.slugStrategy,
3099
+ previousError: item.previousError
3089
3100
  });
3090
3101
  const responseSchema = buildGeminiResponseSchema(type.schema, type.slugStrategy);
3091
3102
  return {
@@ -3497,6 +3508,82 @@ async function runPool(items, concurrency, worker) {
3497
3508
  function labelForItem(item) {
3498
3509
  return `${item.contentType}/${item.enSlug}@${item.locale}`;
3499
3510
  }
3511
+ function buildRetryWorklist(failed, originalByKey) {
3512
+ return failed.map((result) => {
3513
+ const key = translationItemKey(result);
3514
+ const original = originalByKey.get(key);
3515
+ return {
3516
+ contentType: result.contentType,
3517
+ enSlug: result.enSlug,
3518
+ locale: result.locale,
3519
+ reason: original?.reason ?? "missing",
3520
+ currentEnHash: original?.currentEnHash ?? "",
3521
+ storedEnHash: original?.storedEnHash,
3522
+ previousError: result.error ?? "Translation failed"
3523
+ };
3524
+ });
3525
+ }
3526
+ async function runRetryRound(config, retryItems, options) {
3527
+ const startedAt = Date.now();
3528
+ const translateOne = options.translateOne ?? ((item) => translatePage(config, item, { model: options.model, force: true }));
3529
+ if (options.mode === "direct") {
3530
+ const active = /* @__PURE__ */ new Set();
3531
+ await runPool(retryItems, options.concurrency, async (item) => {
3532
+ const label = labelForItem(item);
3533
+ active.add(label);
3534
+ options.onProgress?.({ type: "item-start", item, active: [...active] });
3535
+ const result = await translateOne(item);
3536
+ active.delete(label);
3537
+ options.onResult(result);
3538
+ });
3539
+ return;
3540
+ }
3541
+ const entries = [];
3542
+ for (const item of retryItems) {
3543
+ const itemStartedAt = Date.now();
3544
+ try {
3545
+ const outcome = prepareTranslation(config, item, { model: options.model, force: true }, itemStartedAt);
3546
+ if (outcome.status === "done") {
3547
+ options.onResult(outcome.result);
3548
+ } else {
3549
+ entries.push({
3550
+ apiModel: resolveGeminiModelId(displayModelFor(outcome.prepared)),
3551
+ prompt: outcome.prepared.prompt,
3552
+ prepared: outcome.prepared
3553
+ });
3554
+ }
3555
+ } catch (error) {
3556
+ options.onResult(failedResultForItem(item, error, itemStartedAt));
3557
+ }
3558
+ }
3559
+ const plans = planBatchJobs(entries);
3560
+ const submitted = await Promise.all(
3561
+ plans.map(async (plan, planIndex) => {
3562
+ try {
3563
+ return await submitBatchJobPlan(config, {
3564
+ plan,
3565
+ displayModel: normalizeGeminiDisplayName(plan.apiModel),
3566
+ jobIndex: planIndex,
3567
+ jobCount: plans.length,
3568
+ onProgress: options.onProgress
3569
+ });
3570
+ } catch (error) {
3571
+ for (const entry of plan.entries) {
3572
+ options.onResult(failedResultForItem(entry.prepared.item, error, startedAt));
3573
+ }
3574
+ return void 0;
3575
+ }
3576
+ })
3577
+ );
3578
+ const jobsToPoll = submitted.filter((row) => row !== void 0);
3579
+ if (jobsToPoll.length > 0) {
3580
+ await pollAndIngestBatchJobs(config, jobsToPoll, {
3581
+ jobCount: jobsToPoll.length,
3582
+ onProgress: options.onProgress,
3583
+ onResult: options.onResult
3584
+ });
3585
+ }
3586
+ }
3500
3587
  async function translatePage(config, item, options = {}) {
3501
3588
  const startedAt = Date.now();
3502
3589
  let outcome;
@@ -3669,9 +3756,40 @@ async function translateWorklist(config, items, options = {}) {
3669
3756
  });
3670
3757
  }
3671
3758
  }
3672
- const results = collector.assemble();
3759
+ const mainResults = collector.assemble();
3760
+ const failed = mainResults.filter((result) => result.failed);
3761
+ if (failed.length === 0) {
3762
+ const totals2 = summarizeResults(mainResults, Date.now() - startedAt);
3763
+ options.onProgress?.({ type: "done", results: mainResults, totals: totals2 });
3764
+ return mainResults;
3765
+ }
3766
+ options.onProgress?.({ type: "retry-start", failed });
3767
+ const originalByKey = new Map(
3768
+ items.map((item) => [translationItemKey(item), item])
3769
+ );
3770
+ const retryItems = buildRetryWorklist(failed, originalByKey);
3771
+ const retryByKey = /* @__PURE__ */ new Map();
3772
+ await runRetryRound(config, retryItems, {
3773
+ model: options.model,
3774
+ force: options.force,
3775
+ concurrency,
3776
+ mode,
3777
+ onProgress: options.onProgress,
3778
+ onResult: (result) => {
3779
+ retryByKey.set(translationItemKey(result), result);
3780
+ options.onProgress?.({ type: "item-done", result });
3781
+ }
3782
+ });
3783
+ const results = mainResults.map((result) => {
3784
+ if (!result.failed) return result;
3785
+ return retryByKey.get(translationItemKey(result)) ?? result;
3786
+ });
3787
+ const retriedTranslated = failed.filter((original) => {
3788
+ const retried = retryByKey.get(translationItemKey(original));
3789
+ return retried !== void 0 && !retried.failed && !retried.skipped;
3790
+ }).length;
3673
3791
  const totals = summarizeResults(results, Date.now() - startedAt);
3674
- options.onProgress?.({ type: "done", results, totals });
3792
+ options.onProgress?.({ type: "done", results, totals, retriedTranslated });
3675
3793
  return results;
3676
3794
  }
3677
3795
  function countPendingItems(config, jobs) {