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.
- package/dist/cli/index.cjs +150 -8
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +150 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/translate-progress.d.ts.map +1 -1
- package/dist/index.cjs +122 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +122 -4
- package/dist/index.js.map +1 -1
- package/dist/src/translate/page-translator.d.ts +28 -0
- package/dist/src/translate/page-translator.d.ts.map +1 -1
- package/dist/src/translate/prompts/translation-prompt.d.ts +2 -6
- package/dist/src/translate/prompts/translation-prompt.d.ts.map +1 -1
- package/dist/src/translate/translate-core.d.ts +9 -0
- package/dist/src/translate/translate-core.d.ts.map +1 -1
- package/dist/src/translate/worklist.d.ts +6 -0
- package/dist/src/translate/worklist.d.ts.map +1 -1
- package/dist/studio/server.cjs.map +1 -1
- package/dist/studio/server.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2827,6 +2827,13 @@ function buildOutputFormatLine(hasFrontmatter, slugStrategy) {
|
|
|
2827
2827
|
}
|
|
2828
2828
|
return slugStrategy === "localized" ? "`frontmatter` (object with translated frontmatter fields), `body` (string, full MDX body), `slug` (string)." : "`frontmatter` (object), `body` (string).";
|
|
2829
2829
|
}
|
|
2830
|
+
function buildRetryContextSection(previousError) {
|
|
2831
|
+
return [
|
|
2832
|
+
"",
|
|
2833
|
+
"## Previous attempt",
|
|
2834
|
+
`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).`
|
|
2835
|
+
];
|
|
2836
|
+
}
|
|
2830
2837
|
function buildPageTranslationPrompt(input) {
|
|
2831
2838
|
const localeName = LOCALE_NAMES[input.targetLocale] ?? input.targetLocale;
|
|
2832
2839
|
const localizationPrompt = input.resolved.promptOverride ?? defaultLocalizationPrompt(localeName, input.targetLocale);
|
|
@@ -2857,7 +2864,10 @@ function buildPageTranslationPrompt(input) {
|
|
|
2857
2864
|
buildOutputFormatLine(
|
|
2858
2865
|
Object.keys(input.translatableFrontmatter).length > 0,
|
|
2859
2866
|
input.slugStrategy
|
|
2860
|
-
)
|
|
2867
|
+
),
|
|
2868
|
+
// Retry context is locale-specific suffix material: it MUST stay after the
|
|
2869
|
+
// EN body so the cacheable prefix is byte-identical with and without it.
|
|
2870
|
+
...input.previousError ? buildRetryContextSection(input.previousError) : []
|
|
2861
2871
|
];
|
|
2862
2872
|
return [...prefix, ...suffix].join("\n");
|
|
2863
2873
|
}
|
|
@@ -3072,7 +3082,8 @@ function prepareTranslation(config, item, options, startedAt) {
|
|
|
3072
3082
|
contextLabel: resolveContextLabel(enDoc, item.enSlug),
|
|
3073
3083
|
translatableFrontmatter: payload.frontmatter,
|
|
3074
3084
|
enBody: payload.body,
|
|
3075
|
-
slugStrategy: type.slugStrategy
|
|
3085
|
+
slugStrategy: type.slugStrategy,
|
|
3086
|
+
previousError: item.previousError
|
|
3076
3087
|
});
|
|
3077
3088
|
const responseSchema = buildGeminiResponseSchema(type.schema, type.slugStrategy);
|
|
3078
3089
|
return {
|
|
@@ -3484,6 +3495,82 @@ async function runPool(items, concurrency, worker) {
|
|
|
3484
3495
|
function labelForItem(item) {
|
|
3485
3496
|
return `${item.contentType}/${item.enSlug}@${item.locale}`;
|
|
3486
3497
|
}
|
|
3498
|
+
function buildRetryWorklist(failed, originalByKey) {
|
|
3499
|
+
return failed.map((result) => {
|
|
3500
|
+
const key = translationItemKey(result);
|
|
3501
|
+
const original = originalByKey.get(key);
|
|
3502
|
+
return {
|
|
3503
|
+
contentType: result.contentType,
|
|
3504
|
+
enSlug: result.enSlug,
|
|
3505
|
+
locale: result.locale,
|
|
3506
|
+
reason: original?.reason ?? "missing",
|
|
3507
|
+
currentEnHash: original?.currentEnHash ?? "",
|
|
3508
|
+
storedEnHash: original?.storedEnHash,
|
|
3509
|
+
previousError: result.error ?? "Translation failed"
|
|
3510
|
+
};
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
async function runRetryRound(config, retryItems, options) {
|
|
3514
|
+
const startedAt = Date.now();
|
|
3515
|
+
const translateOne = options.translateOne ?? ((item) => translatePage(config, item, { model: options.model, force: true }));
|
|
3516
|
+
if (options.mode === "direct") {
|
|
3517
|
+
const active = /* @__PURE__ */ new Set();
|
|
3518
|
+
await runPool(retryItems, options.concurrency, async (item) => {
|
|
3519
|
+
const label = labelForItem(item);
|
|
3520
|
+
active.add(label);
|
|
3521
|
+
options.onProgress?.({ type: "item-start", item, active: [...active] });
|
|
3522
|
+
const result = await translateOne(item);
|
|
3523
|
+
active.delete(label);
|
|
3524
|
+
options.onResult(result);
|
|
3525
|
+
});
|
|
3526
|
+
return;
|
|
3527
|
+
}
|
|
3528
|
+
const entries = [];
|
|
3529
|
+
for (const item of retryItems) {
|
|
3530
|
+
const itemStartedAt = Date.now();
|
|
3531
|
+
try {
|
|
3532
|
+
const outcome = prepareTranslation(config, item, { model: options.model, force: true }, itemStartedAt);
|
|
3533
|
+
if (outcome.status === "done") {
|
|
3534
|
+
options.onResult(outcome.result);
|
|
3535
|
+
} else {
|
|
3536
|
+
entries.push({
|
|
3537
|
+
apiModel: resolveGeminiModelId(displayModelFor(outcome.prepared)),
|
|
3538
|
+
prompt: outcome.prepared.prompt,
|
|
3539
|
+
prepared: outcome.prepared
|
|
3540
|
+
});
|
|
3541
|
+
}
|
|
3542
|
+
} catch (error) {
|
|
3543
|
+
options.onResult(failedResultForItem(item, error, itemStartedAt));
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
const plans = planBatchJobs(entries);
|
|
3547
|
+
const submitted = await Promise.all(
|
|
3548
|
+
plans.map(async (plan, planIndex) => {
|
|
3549
|
+
try {
|
|
3550
|
+
return await submitBatchJobPlan(config, {
|
|
3551
|
+
plan,
|
|
3552
|
+
displayModel: normalizeGeminiDisplayName(plan.apiModel),
|
|
3553
|
+
jobIndex: planIndex,
|
|
3554
|
+
jobCount: plans.length,
|
|
3555
|
+
onProgress: options.onProgress
|
|
3556
|
+
});
|
|
3557
|
+
} catch (error) {
|
|
3558
|
+
for (const entry of plan.entries) {
|
|
3559
|
+
options.onResult(failedResultForItem(entry.prepared.item, error, startedAt));
|
|
3560
|
+
}
|
|
3561
|
+
return void 0;
|
|
3562
|
+
}
|
|
3563
|
+
})
|
|
3564
|
+
);
|
|
3565
|
+
const jobsToPoll = submitted.filter((row) => row !== void 0);
|
|
3566
|
+
if (jobsToPoll.length > 0) {
|
|
3567
|
+
await pollAndIngestBatchJobs(config, jobsToPoll, {
|
|
3568
|
+
jobCount: jobsToPoll.length,
|
|
3569
|
+
onProgress: options.onProgress,
|
|
3570
|
+
onResult: options.onResult
|
|
3571
|
+
});
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3487
3574
|
async function translatePage(config, item, options = {}) {
|
|
3488
3575
|
const startedAt = Date.now();
|
|
3489
3576
|
let outcome;
|
|
@@ -3656,9 +3743,40 @@ async function translateWorklist(config, items, options = {}) {
|
|
|
3656
3743
|
});
|
|
3657
3744
|
}
|
|
3658
3745
|
}
|
|
3659
|
-
const
|
|
3746
|
+
const mainResults = collector.assemble();
|
|
3747
|
+
const failed = mainResults.filter((result) => result.failed);
|
|
3748
|
+
if (failed.length === 0) {
|
|
3749
|
+
const totals2 = summarizeResults(mainResults, Date.now() - startedAt);
|
|
3750
|
+
options.onProgress?.({ type: "done", results: mainResults, totals: totals2 });
|
|
3751
|
+
return mainResults;
|
|
3752
|
+
}
|
|
3753
|
+
options.onProgress?.({ type: "retry-start", failed });
|
|
3754
|
+
const originalByKey = new Map(
|
|
3755
|
+
items.map((item) => [translationItemKey(item), item])
|
|
3756
|
+
);
|
|
3757
|
+
const retryItems = buildRetryWorklist(failed, originalByKey);
|
|
3758
|
+
const retryByKey = /* @__PURE__ */ new Map();
|
|
3759
|
+
await runRetryRound(config, retryItems, {
|
|
3760
|
+
model: options.model,
|
|
3761
|
+
force: options.force,
|
|
3762
|
+
concurrency,
|
|
3763
|
+
mode,
|
|
3764
|
+
onProgress: options.onProgress,
|
|
3765
|
+
onResult: (result) => {
|
|
3766
|
+
retryByKey.set(translationItemKey(result), result);
|
|
3767
|
+
options.onProgress?.({ type: "item-done", result });
|
|
3768
|
+
}
|
|
3769
|
+
});
|
|
3770
|
+
const results = mainResults.map((result) => {
|
|
3771
|
+
if (!result.failed) return result;
|
|
3772
|
+
return retryByKey.get(translationItemKey(result)) ?? result;
|
|
3773
|
+
});
|
|
3774
|
+
const retriedTranslated = failed.filter((original) => {
|
|
3775
|
+
const retried = retryByKey.get(translationItemKey(original));
|
|
3776
|
+
return retried !== void 0 && !retried.failed && !retried.skipped;
|
|
3777
|
+
}).length;
|
|
3660
3778
|
const totals = summarizeResults(results, Date.now() - startedAt);
|
|
3661
|
-
options.onProgress?.({ type: "done", results, totals });
|
|
3779
|
+
options.onProgress?.({ type: "done", results, totals, retriedTranslated });
|
|
3662
3780
|
return results;
|
|
3663
3781
|
}
|
|
3664
3782
|
function countPendingItems(config, jobs) {
|