@braze/sanity-plugin-ai-translation 0.2.69 → 0.2.72
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/index.d.ts +227 -0
- package/dist/index.js +255 -55
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1039,6 +1039,57 @@ const fetchTranslationPrompts = async ({
|
|
|
1039
1039
|
payload
|
|
1040
1040
|
}), mainSourceDocumentId && updateActiveTranslationJob(mainSourceDocumentId, payload);
|
|
1041
1041
|
}
|
|
1042
|
+
}, emitTelemetryEvent = (config, event) => {
|
|
1043
|
+
if (config?.onEvent)
|
|
1044
|
+
try {
|
|
1045
|
+
config.onEvent(event), devLog.debug("Telemetry event emitted", { eventName: event.name });
|
|
1046
|
+
} catch (error) {
|
|
1047
|
+
devLog.warn("Failed to emit telemetry event", {
|
|
1048
|
+
eventName: event.name,
|
|
1049
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
}, emitTelemetryError = (config, bundle) => {
|
|
1053
|
+
if (config?.onError)
|
|
1054
|
+
try {
|
|
1055
|
+
config.onError(bundle), devLog.debug("Telemetry error emitted", {
|
|
1056
|
+
documentId: bundle.documentId,
|
|
1057
|
+
errorType: bundle.error.type
|
|
1058
|
+
});
|
|
1059
|
+
} catch (error) {
|
|
1060
|
+
devLog.warn("Failed to emit telemetry error", {
|
|
1061
|
+
documentId: bundle.documentId,
|
|
1062
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
}, buildErrorBundle = (params) => {
|
|
1066
|
+
const durationMs = Date.now() - params.startTime;
|
|
1067
|
+
return {
|
|
1068
|
+
documentId: params.documentId,
|
|
1069
|
+
documentType: params.documentType,
|
|
1070
|
+
targetLanguage: params.targetLanguage,
|
|
1071
|
+
sourceLanguage: params.sourceLanguage,
|
|
1072
|
+
translationMode: params.translationMode,
|
|
1073
|
+
error: {
|
|
1074
|
+
message: params.error.message,
|
|
1075
|
+
stack: params.error.stack,
|
|
1076
|
+
type: params.errorType
|
|
1077
|
+
},
|
|
1078
|
+
debug: {
|
|
1079
|
+
originalDocument: params.originalDocument,
|
|
1080
|
+
serializedDocument: params.serializedDocument,
|
|
1081
|
+
openaiResponse: params.openaiResponse,
|
|
1082
|
+
translationPrompts: params.translationPrompts,
|
|
1083
|
+
validationErrors: params.validationErrors
|
|
1084
|
+
},
|
|
1085
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1086
|
+
durationMs
|
|
1087
|
+
};
|
|
1088
|
+
}, createTimestamp = () => (/* @__PURE__ */ new Date()).toISOString(), classifyError = (error, context) => {
|
|
1089
|
+
if (context)
|
|
1090
|
+
return context;
|
|
1091
|
+
const message = error.message.toLowerCase();
|
|
1092
|
+
return message.includes("network") || message.includes("fetch") || message.includes("timeout") || message.includes("econnrefused") || message.includes("enotfound") ? "network" : message.includes("openai") || message.includes("gpt") || message.includes("api error") ? "openai_api" : message.includes("validation") || message.includes("structure") || message.includes("invalid") ? "validation" : message.includes("deserialize") || message.includes("parse") ? "deserialization" : message.includes("serialize") || message.includes("xml") ? "serialization" : message.includes("agent") || message.includes("sanity") ? "sanity_agent" : "unknown";
|
|
1042
1093
|
}, updateTranslationMetadata = async ({
|
|
1043
1094
|
client,
|
|
1044
1095
|
targetLanguage,
|
|
@@ -2231,16 +2282,18 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2231
2282
|
translationPrompts,
|
|
2232
2283
|
targetLanguage,
|
|
2233
2284
|
openaiConfig,
|
|
2234
|
-
previousError,
|
|
2235
|
-
errorContext
|
|
2285
|
+
previousError: _previousError,
|
|
2286
|
+
errorContext,
|
|
2287
|
+
telemetryConfig
|
|
2236
2288
|
}) => {
|
|
2237
|
-
const sourceLanguage = sourceDocument[languageFieldName], logContext = {
|
|
2289
|
+
const startTime = Date.now(), sourceLanguage = sourceDocument[languageFieldName], logContext = {
|
|
2238
2290
|
sourceId: sourceDocument._id,
|
|
2239
2291
|
sourceLanguage
|
|
2240
2292
|
};
|
|
2293
|
+
let serializedDocument, openaiResponseContent;
|
|
2241
2294
|
try {
|
|
2242
2295
|
const sourceText = serializeDocument(sourceDocument);
|
|
2243
|
-
devLog.info("Serialized document for OpenAI", {
|
|
2296
|
+
serializedDocument = sourceText, devLog.info("Serialized document for OpenAI", {
|
|
2244
2297
|
...logContext,
|
|
2245
2298
|
characterCount: sourceText.length,
|
|
2246
2299
|
estimatedTokens: Math.ceil(sourceText.length / 4),
|
|
@@ -2263,7 +2316,7 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2263
2316
|
model: openaiConfig.model,
|
|
2264
2317
|
reasoningEffort: openaiConfig.reasoningEffort
|
|
2265
2318
|
});
|
|
2266
|
-
devLog.info("OpenAI translation response received", {
|
|
2319
|
+
openaiResponseContent = response.content, devLog.info("OpenAI translation response received", {
|
|
2267
2320
|
...logContext,
|
|
2268
2321
|
tokensUsed: response.tokensUsed,
|
|
2269
2322
|
cost: response.cost,
|
|
@@ -2301,9 +2354,35 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2301
2354
|
})), setJobStatus(dispatch, sourceDocument._id, targetLanguage, "translating_with_openai", {
|
|
2302
2355
|
tokensUsed: response.tokensUsed.total,
|
|
2303
2356
|
estimatedCost: response.cost
|
|
2304
|
-
}),
|
|
2357
|
+
}), {
|
|
2358
|
+
document: result,
|
|
2359
|
+
tokensUsed: response.tokensUsed.total,
|
|
2360
|
+
cost: response.cost
|
|
2361
|
+
};
|
|
2305
2362
|
} catch (error) {
|
|
2306
|
-
|
|
2363
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2364
|
+
throw devLog.error("OpenAI translation failed", err, logContext), emitTelemetryError(
|
|
2365
|
+
telemetryConfig,
|
|
2366
|
+
buildErrorBundle({
|
|
2367
|
+
documentId: sourceDocument._id,
|
|
2368
|
+
documentType: sourceDocument._type,
|
|
2369
|
+
targetLanguage,
|
|
2370
|
+
sourceLanguage,
|
|
2371
|
+
translationMode: "openai-only",
|
|
2372
|
+
error: err,
|
|
2373
|
+
errorType: classifyError(err, "openai_api"),
|
|
2374
|
+
startTime,
|
|
2375
|
+
originalDocument: sourceDocument,
|
|
2376
|
+
serializedDocument,
|
|
2377
|
+
openaiResponse: openaiResponseContent,
|
|
2378
|
+
translationPrompts: {
|
|
2379
|
+
globalPrompt: translationPrompts.globalPrompt,
|
|
2380
|
+
localePrompt: translationPrompts.localeInstructions[targetLanguage]?.prompt,
|
|
2381
|
+
protectedPhrases: translationPrompts.protectedPhrases,
|
|
2382
|
+
glossaryTerms: translationPrompts.localeInstructions[targetLanguage]?.glossaryTerms ? [translationPrompts.localeInstructions[targetLanguage].glossaryTerms] : void 0
|
|
2383
|
+
}
|
|
2384
|
+
})
|
|
2385
|
+
), error;
|
|
2307
2386
|
}
|
|
2308
2387
|
}, buildTranslationPrompt$1 = ({
|
|
2309
2388
|
sourceText,
|
|
@@ -2539,9 +2618,10 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2539
2618
|
releaseId,
|
|
2540
2619
|
excludeFields = [],
|
|
2541
2620
|
replaceExisting = !1,
|
|
2542
|
-
mainSourceDocumentId
|
|
2621
|
+
mainSourceDocumentId,
|
|
2622
|
+
telemetryConfig
|
|
2543
2623
|
}) => {
|
|
2544
|
-
const safeExcludeFields = excludeFields.filter((field) => field !== languageFieldName);
|
|
2624
|
+
const startTime = Date.now(), sourceLanguage = sourceDocument[languageFieldName], safeExcludeFields = excludeFields.filter((field) => field !== languageFieldName);
|
|
2545
2625
|
excludeFields.length !== safeExcludeFields.length && devLog.warn("Language field was in excludeFields - removing it to prevent translation errors", {
|
|
2546
2626
|
documentId: sourceDocument._id,
|
|
2547
2627
|
languageFieldName,
|
|
@@ -2580,29 +2660,55 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2580
2660
|
documentId: sourceDocument._id,
|
|
2581
2661
|
documentType: sourceDocument._type
|
|
2582
2662
|
}), documentToTranslate = filteredDocument;
|
|
2583
|
-
let translatedDoc, previousError = null;
|
|
2663
|
+
let translatedDoc, translationMetrics = {}, previousError = null;
|
|
2584
2664
|
const performTranslation = async (errorContext) => {
|
|
2585
2665
|
switch (translationMode) {
|
|
2586
|
-
case "sanity-only":
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2666
|
+
case "sanity-only": {
|
|
2667
|
+
setJobStatus(dispatch, sourceDocument._id, targetLanguage, "translating", void 0, mainSourceDocumentId);
|
|
2668
|
+
try {
|
|
2669
|
+
return { document: await getSanityAgentTranslation({
|
|
2670
|
+
sourceDocument: documentToTranslate,
|
|
2671
|
+
targetLanguage,
|
|
2672
|
+
languageFieldName,
|
|
2673
|
+
schemaId,
|
|
2674
|
+
client,
|
|
2675
|
+
translationPrompts
|
|
2676
|
+
}) };
|
|
2677
|
+
} catch (error) {
|
|
2678
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2679
|
+
throw emitTelemetryError(
|
|
2680
|
+
telemetryConfig,
|
|
2681
|
+
buildErrorBundle({
|
|
2682
|
+
documentId: sourceDocument._id,
|
|
2683
|
+
documentType: sourceDocument._type,
|
|
2684
|
+
targetLanguage,
|
|
2685
|
+
sourceLanguage,
|
|
2686
|
+
translationMode: "sanity-only",
|
|
2687
|
+
error: err,
|
|
2688
|
+
errorType: classifyError(err, "sanity_agent"),
|
|
2689
|
+
startTime,
|
|
2690
|
+
originalDocument: sourceDocument,
|
|
2691
|
+
translationPrompts: {
|
|
2692
|
+
globalPrompt: translationPrompts.globalPrompt,
|
|
2693
|
+
localePrompt: translationPrompts.localeInstructions[targetLanguage]?.prompt,
|
|
2694
|
+
protectedPhrases: translationPrompts.protectedPhrases
|
|
2695
|
+
}
|
|
2696
|
+
})
|
|
2697
|
+
), error;
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2595
2700
|
case "openai-only": {
|
|
2596
2701
|
if (!openaiConfig?.enabled || !openaiConfig?.apiKey)
|
|
2597
2702
|
throw new Error("OpenAI is not configured but openai-only mode was selected");
|
|
2598
|
-
|
|
2703
|
+
setJobStatus(
|
|
2599
2704
|
dispatch,
|
|
2600
2705
|
sourceDocument._id,
|
|
2601
2706
|
targetLanguage,
|
|
2602
2707
|
"translating_with_openai",
|
|
2603
2708
|
void 0,
|
|
2604
2709
|
mainSourceDocumentId
|
|
2605
|
-
)
|
|
2710
|
+
);
|
|
2711
|
+
const result = await getOpenAITranslation({
|
|
2606
2712
|
dispatch,
|
|
2607
2713
|
sourceDocument: documentToTranslate,
|
|
2608
2714
|
targetLanguage,
|
|
@@ -2610,14 +2716,23 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2610
2716
|
translationPrompts,
|
|
2611
2717
|
openaiConfig,
|
|
2612
2718
|
previousError: errorContext && previousError ? previousError : void 0,
|
|
2613
|
-
errorContext
|
|
2719
|
+
errorContext,
|
|
2720
|
+
telemetryConfig
|
|
2614
2721
|
});
|
|
2722
|
+
return result ? {
|
|
2723
|
+
document: result.document,
|
|
2724
|
+
tokensUsed: result.tokensUsed,
|
|
2725
|
+
cost: result.cost
|
|
2726
|
+
} : { document: null };
|
|
2615
2727
|
}
|
|
2616
2728
|
default:
|
|
2617
2729
|
throw new Error(`Invalid translation mode: ${translationMode}`);
|
|
2618
2730
|
}
|
|
2619
|
-
};
|
|
2620
|
-
if (translatedDoc =
|
|
2731
|
+
}, translationResult = await performTranslation();
|
|
2732
|
+
if (translatedDoc = translationResult.document, translationMetrics = {
|
|
2733
|
+
tokensUsed: translationResult.tokensUsed,
|
|
2734
|
+
cost: translationResult.cost
|
|
2735
|
+
}, !translatedDoc)
|
|
2621
2736
|
return null;
|
|
2622
2737
|
let transformedResult;
|
|
2623
2738
|
Object.keys(originalStructure).length > 0 ? (devLog.debug("Restoring field transforms after translation", {
|
|
@@ -2680,9 +2795,13 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2680
2795
|
targetLanguage,
|
|
2681
2796
|
error: error instanceof Error ? error.message : String(error)
|
|
2682
2797
|
}), previousError = error instanceof Error ? error : new Error(String(error));
|
|
2683
|
-
const errorContext = `Previous translation attempt failed with Sanity validation error: ${previousError.message}. Please ensure the translated document structure exactly matches the source document structure
|
|
2684
|
-
if (translatedDoc =
|
|
2798
|
+
const errorContext = `Previous translation attempt failed with Sanity validation error: ${previousError.message}. Please ensure the translated document structure exactly matches the source document structure.`, retryResult = await performTranslation(errorContext);
|
|
2799
|
+
if (translatedDoc = retryResult.document, !translatedDoc)
|
|
2685
2800
|
throw new Error("Translation retry failed: no document returned");
|
|
2801
|
+
translationMetrics = {
|
|
2802
|
+
tokensUsed: retryResult.tokensUsed,
|
|
2803
|
+
cost: retryResult.cost
|
|
2804
|
+
};
|
|
2686
2805
|
let retryTransformedResult;
|
|
2687
2806
|
Object.keys(originalStructure).length > 0 ? retryTransformedResult = restoreTransforms({
|
|
2688
2807
|
translatedDocument: translatedDoc,
|
|
@@ -2713,11 +2832,19 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2713
2832
|
isRetry: !0
|
|
2714
2833
|
}
|
|
2715
2834
|
}
|
|
2716
|
-
),
|
|
2835
|
+
), {
|
|
2836
|
+
document: retryNewDoc,
|
|
2837
|
+
tokensUsed: translationMetrics.tokensUsed,
|
|
2838
|
+
cost: translationMetrics.cost
|
|
2839
|
+
};
|
|
2717
2840
|
}
|
|
2718
2841
|
throw error;
|
|
2719
2842
|
}
|
|
2720
|
-
return
|
|
2843
|
+
return {
|
|
2844
|
+
document: newDoc,
|
|
2845
|
+
tokensUsed: translationMetrics.tokensUsed,
|
|
2846
|
+
cost: translationMetrics.cost
|
|
2847
|
+
};
|
|
2721
2848
|
}, translateDocument = async ({
|
|
2722
2849
|
dispatch,
|
|
2723
2850
|
client,
|
|
@@ -2732,9 +2859,10 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2732
2859
|
releaseId,
|
|
2733
2860
|
excludeFields,
|
|
2734
2861
|
replaceExisting,
|
|
2735
|
-
mainSourceDocumentId
|
|
2862
|
+
mainSourceDocumentId,
|
|
2863
|
+
telemetryConfig
|
|
2736
2864
|
}) => {
|
|
2737
|
-
const logContext = {
|
|
2865
|
+
const jobStartTime = Date.now(), logContext = {
|
|
2738
2866
|
documentId: sourceDocument._id,
|
|
2739
2867
|
targetLanguage,
|
|
2740
2868
|
schemaId,
|
|
@@ -2746,7 +2874,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2746
2874
|
client,
|
|
2747
2875
|
sourceDocument,
|
|
2748
2876
|
sourceLanguage
|
|
2749
|
-
}), existingTranslationId = getExistingTranslationId(translationMetadata, targetLanguage),
|
|
2877
|
+
}), existingTranslationId = getExistingTranslationId(translationMetadata, targetLanguage), translationResult = await handleTranslation({
|
|
2750
2878
|
dispatch,
|
|
2751
2879
|
client,
|
|
2752
2880
|
schemaId,
|
|
@@ -2760,10 +2888,12 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2760
2888
|
releaseId,
|
|
2761
2889
|
excludeFields,
|
|
2762
2890
|
replaceExisting,
|
|
2763
|
-
mainSourceDocumentId
|
|
2764
|
-
|
|
2891
|
+
mainSourceDocumentId,
|
|
2892
|
+
telemetryConfig
|
|
2893
|
+
}), newDocument = translationResult?.document;
|
|
2765
2894
|
if (!newDocument) {
|
|
2766
|
-
|
|
2895
|
+
const failureError = new Error("Translation returned null");
|
|
2896
|
+
devLog.error("Translation failed: no document returned", failureError, logContext), setJobStatus(
|
|
2767
2897
|
dispatch,
|
|
2768
2898
|
sourceDocument._id,
|
|
2769
2899
|
targetLanguage,
|
|
@@ -2772,7 +2902,20 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2772
2902
|
errorMessage: "Translation failed"
|
|
2773
2903
|
},
|
|
2774
2904
|
mainSourceDocumentId
|
|
2775
|
-
),
|
|
2905
|
+
), emitTelemetryEvent(telemetryConfig, {
|
|
2906
|
+
name: "translation.job_failed",
|
|
2907
|
+
timestamp: createTimestamp(),
|
|
2908
|
+
data: {
|
|
2909
|
+
documentId: sourceDocument._id,
|
|
2910
|
+
documentType: sourceDocument._type,
|
|
2911
|
+
sourceLanguage,
|
|
2912
|
+
targetLanguage,
|
|
2913
|
+
translationMode,
|
|
2914
|
+
durationMs: Date.now() - jobStartTime,
|
|
2915
|
+
errorType: classifyError(failureError),
|
|
2916
|
+
errorMessage: "Translation failed"
|
|
2917
|
+
}
|
|
2918
|
+
}), devLog.timeEnd("translateDocument"), devLog.groupEnd();
|
|
2776
2919
|
return;
|
|
2777
2920
|
}
|
|
2778
2921
|
return setJobStatus(
|
|
@@ -2798,16 +2941,31 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2798
2941
|
targetLanguage,
|
|
2799
2942
|
"completed",
|
|
2800
2943
|
{
|
|
2801
|
-
targetId: newDocument._id
|
|
2944
|
+
targetId: newDocument._id,
|
|
2945
|
+
tokensUsed: translationResult?.tokensUsed,
|
|
2946
|
+
estimatedCost: translationResult?.cost
|
|
2802
2947
|
},
|
|
2803
2948
|
mainSourceDocumentId
|
|
2804
2949
|
), devLog.info("Translation completed successfully", {
|
|
2805
2950
|
...logContext,
|
|
2806
2951
|
newDocumentId: newDocument._id
|
|
2952
|
+
}), emitTelemetryEvent(telemetryConfig, {
|
|
2953
|
+
name: "translation.job_completed",
|
|
2954
|
+
timestamp: createTimestamp(),
|
|
2955
|
+
data: {
|
|
2956
|
+
documentId: sourceDocument._id,
|
|
2957
|
+
documentType: sourceDocument._type,
|
|
2958
|
+
sourceLanguage,
|
|
2959
|
+
targetLanguage,
|
|
2960
|
+
translationMode,
|
|
2961
|
+
durationMs: Date.now() - jobStartTime,
|
|
2962
|
+
tokensUsed: translationResult?.tokensUsed,
|
|
2963
|
+
estimatedCost: translationResult?.cost
|
|
2964
|
+
}
|
|
2807
2965
|
}), devLog.timeEnd("translateDocument"), devLog.groupEnd(), newDocument._id;
|
|
2808
2966
|
} catch (error) {
|
|
2809
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2810
|
-
devLog.error("Translation failed",
|
|
2967
|
+
const errorMessage = error instanceof Error ? error.message : String(error), errorObj = error instanceof Error ? error : new Error(String(error));
|
|
2968
|
+
devLog.error("Translation failed", errorObj, logContext), setJobStatus(
|
|
2811
2969
|
dispatch,
|
|
2812
2970
|
sourceDocument._id,
|
|
2813
2971
|
targetLanguage,
|
|
@@ -2816,7 +2974,20 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2816
2974
|
errorMessage
|
|
2817
2975
|
},
|
|
2818
2976
|
mainSourceDocumentId
|
|
2819
|
-
),
|
|
2977
|
+
), emitTelemetryEvent(telemetryConfig, {
|
|
2978
|
+
name: "translation.job_failed",
|
|
2979
|
+
timestamp: createTimestamp(),
|
|
2980
|
+
data: {
|
|
2981
|
+
documentId: sourceDocument._id,
|
|
2982
|
+
documentType: sourceDocument._type,
|
|
2983
|
+
sourceLanguage,
|
|
2984
|
+
targetLanguage,
|
|
2985
|
+
translationMode,
|
|
2986
|
+
durationMs: Date.now() - jobStartTime,
|
|
2987
|
+
errorType: classifyError(errorObj),
|
|
2988
|
+
errorMessage
|
|
2989
|
+
}
|
|
2990
|
+
}), devLog.timeEnd("translateDocument"), devLog.groupEnd();
|
|
2820
2991
|
return;
|
|
2821
2992
|
}
|
|
2822
2993
|
}, translationSemaphore = new Semaphore(2), translateDocumentSet = async ({
|
|
@@ -2831,9 +3002,10 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2831
3002
|
translationMode,
|
|
2832
3003
|
openaiConfig,
|
|
2833
3004
|
excludeFields,
|
|
2834
|
-
releaseConfig
|
|
3005
|
+
releaseConfig,
|
|
3006
|
+
telemetryConfig
|
|
2835
3007
|
}) => {
|
|
2836
|
-
const logContext = {
|
|
3008
|
+
const batchStartTime = Date.now(), logContext = {
|
|
2837
3009
|
documentId: sourceDocument._id,
|
|
2838
3010
|
documentRev: sourceDocument._rev,
|
|
2839
3011
|
targetLanguages,
|
|
@@ -2847,7 +3019,19 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2847
3019
|
const sourceLanguage = sourceDocument[languageFieldName];
|
|
2848
3020
|
if (!sourceLanguage)
|
|
2849
3021
|
throw devLog.error("Source language not found"), new Error("Source language not found");
|
|
2850
|
-
if (
|
|
3022
|
+
if (emitTelemetryEvent(telemetryConfig, {
|
|
3023
|
+
name: "translation.batch_started",
|
|
3024
|
+
timestamp: createTimestamp(),
|
|
3025
|
+
data: {
|
|
3026
|
+
documentId: sourceDocument._id,
|
|
3027
|
+
documentType: sourceDocument._type,
|
|
3028
|
+
sourceLanguage,
|
|
3029
|
+
targetLanguages,
|
|
3030
|
+
translationMode,
|
|
3031
|
+
referenceCount: references.length,
|
|
3032
|
+
releaseMode: releaseConfig.mode
|
|
3033
|
+
}
|
|
3034
|
+
}), maxConcurrentTranslationJobs && translationSemaphore.updatePermits(maxConcurrentTranslationJobs), dispatch({
|
|
2851
3035
|
type: "SET_OVERALL_STATUS",
|
|
2852
3036
|
payload: "translating"
|
|
2853
3037
|
}), releaseConfig.mode === "existing" && releaseConfig.existingReleaseId)
|
|
@@ -2940,7 +3124,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2940
3124
|
excludeFields,
|
|
2941
3125
|
replaceExisting: releaseConfig.replaceExisting,
|
|
2942
3126
|
// Pass main source document ID for active translations tracking
|
|
2943
|
-
mainSourceDocumentId: sourceDocument._id
|
|
3127
|
+
mainSourceDocumentId: sourceDocument._id,
|
|
3128
|
+
telemetryConfig
|
|
2944
3129
|
});
|
|
2945
3130
|
versionedTargetDocumentId && versionedTargetDocumentIds.push(versionedTargetDocumentId);
|
|
2946
3131
|
}
|
|
@@ -2965,6 +3150,20 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
2965
3150
|
failed,
|
|
2966
3151
|
skipped: skippedCount,
|
|
2967
3152
|
targetDocuments: versionedTargetDocumentIds.length
|
|
3153
|
+
}), emitTelemetryEvent(telemetryConfig, {
|
|
3154
|
+
name: "translation.batch_completed",
|
|
3155
|
+
timestamp: createTimestamp(),
|
|
3156
|
+
data: {
|
|
3157
|
+
documentId: sourceDocument._id,
|
|
3158
|
+
documentType: sourceDocument._type,
|
|
3159
|
+
sourceLanguage,
|
|
3160
|
+
targetLanguages,
|
|
3161
|
+
translationMode,
|
|
3162
|
+
succeeded,
|
|
3163
|
+
failed,
|
|
3164
|
+
skipped: skippedCount,
|
|
3165
|
+
durationMs: Date.now() - batchStartTime
|
|
3166
|
+
}
|
|
2968
3167
|
}), dispatch({
|
|
2969
3168
|
type: "SET_OVERALL_STATUS",
|
|
2970
3169
|
payload: "linking_references"
|
|
@@ -3110,7 +3309,8 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3110
3309
|
// Use the override release ID if provided (from conflict context),
|
|
3111
3310
|
// otherwise use the current releaseConfig value
|
|
3112
3311
|
existingReleaseId: overrideReleaseId ?? releaseConfig.existingReleaseId
|
|
3113
|
-
}
|
|
3312
|
+
},
|
|
3313
|
+
telemetryConfig: config.telemetry
|
|
3114
3314
|
});
|
|
3115
3315
|
},
|
|
3116
3316
|
[
|
|
@@ -3124,6 +3324,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3124
3324
|
config.maxReferenceDepth,
|
|
3125
3325
|
config.openai,
|
|
3126
3326
|
config.excludeFields,
|
|
3327
|
+
config.telemetry,
|
|
3127
3328
|
filteredReferences,
|
|
3128
3329
|
translationMode,
|
|
3129
3330
|
releaseConfig
|
|
@@ -3343,7 +3544,9 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3343
3544
|
}), error;
|
|
3344
3545
|
}
|
|
3345
3546
|
}, ReconcileReferencesButton = ({ sourceLanguage }) => {
|
|
3346
|
-
const {
|
|
3547
|
+
const { releaseConfig, overallStatus, config } = useTranslationContext(), client = useClient({ apiVersion: "vX" }), [documentCount, setDocumentCount] = useState(null), [isLoadingCount, setIsLoadingCount] = useState(!1), [reconcilingStatus, setReconcilingStatus] = useState("idle"), [reconciliationProgress, setReconciliationProgress] = useState(
|
|
3548
|
+
void 0
|
|
3549
|
+
), releaseId = releaseConfig.existingReleaseId, languageFieldName = config?.languageFieldName ?? "language";
|
|
3347
3550
|
useEffect(() => {
|
|
3348
3551
|
if (!releaseId || releaseConfig.mode !== "existing") {
|
|
3349
3552
|
setDocumentCount(null);
|
|
@@ -3378,7 +3581,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3378
3581
|
releaseId,
|
|
3379
3582
|
sourceLanguage,
|
|
3380
3583
|
component: "ReconcileReferencesButton"
|
|
3381
|
-
}),
|
|
3584
|
+
}), setReconcilingStatus("reconciling");
|
|
3382
3585
|
try {
|
|
3383
3586
|
const result = await reconcileReleaseReferences({
|
|
3384
3587
|
client,
|
|
@@ -3386,27 +3589,24 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3386
3589
|
sourceLanguage,
|
|
3387
3590
|
languageFieldName,
|
|
3388
3591
|
onProgress: (current, total) => {
|
|
3389
|
-
|
|
3390
|
-
type: "SET_RECONCILIATION_PROGRESS",
|
|
3391
|
-
payload: { current, total }
|
|
3392
|
-
});
|
|
3592
|
+
setReconciliationProgress({ current, total });
|
|
3393
3593
|
}
|
|
3394
3594
|
});
|
|
3395
3595
|
devLog.info("Reference reconciliation completed", {
|
|
3396
3596
|
releaseId,
|
|
3397
3597
|
...result,
|
|
3398
3598
|
component: "ReconcileReferencesButton"
|
|
3399
|
-
}),
|
|
3400
|
-
|
|
3599
|
+
}), setReconcilingStatus("completed"), setTimeout(() => {
|
|
3600
|
+
setReconcilingStatus("idle"), setReconciliationProgress(void 0);
|
|
3401
3601
|
}, 2e3);
|
|
3402
3602
|
} catch (error) {
|
|
3403
3603
|
devLog.error("Reference reconciliation failed", error instanceof Error ? error : new Error(String(error)), {
|
|
3404
3604
|
releaseId,
|
|
3405
3605
|
component: "ReconcileReferencesButton"
|
|
3406
|
-
}),
|
|
3606
|
+
}), setReconcilingStatus("error"), setReconciliationProgress(void 0);
|
|
3407
3607
|
}
|
|
3408
3608
|
}
|
|
3409
|
-
}, [releaseId, sourceLanguage, languageFieldName, client
|
|
3609
|
+
}, [releaseId, sourceLanguage, languageFieldName, client]);
|
|
3410
3610
|
if (releaseConfig.mode !== "existing" || !releaseId)
|
|
3411
3611
|
return null;
|
|
3412
3612
|
const isTranslating = overallStatus === "translating" || overallStatus === "linking_references", isReconciling = reconcilingStatus === "reconciling";
|
|
@@ -3583,7 +3783,7 @@ ${protectedPhrases.join(", ")}`, buildStyleGuideSection = (globalPrompt, localeP
|
|
|
3583
3783
|
}
|
|
3584
3784
|
)
|
|
3585
3785
|
] }),
|
|
3586
|
-
|
|
3786
|
+
isSelected && sourceLanguage && showReconcileUI && /* @__PURE__ */ jsx(ReconcileReferencesButton, { sourceLanguage })
|
|
3587
3787
|
] })
|
|
3588
3788
|
},
|
|
3589
3789
|
release._id
|