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
|
@@ -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;
|
|
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
|
|
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) {
|