@braze/sanity-plugin-ai-translation 0.2.32 → 0.2.33
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 +9 -1
- package/dist/index.js +99 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -233,7 +233,15 @@ export declare interface PluginConfig {
|
|
|
233
233
|
maxReferenceDepth?: number;
|
|
234
234
|
openai?: {
|
|
235
235
|
apiKey: string;
|
|
236
|
-
model?:
|
|
236
|
+
model?:
|
|
237
|
+
| "gpt-5.1"
|
|
238
|
+
| "gpt-5"
|
|
239
|
+
| "gpt-5-mini"
|
|
240
|
+
| "gpt-5-nano"
|
|
241
|
+
| "gpt-5-pro"
|
|
242
|
+
| "gpt-4o"
|
|
243
|
+
| "gpt-4-turbo"
|
|
244
|
+
| "gpt-4";
|
|
237
245
|
reasoningEffort?: "low" | "medium" | "high";
|
|
238
246
|
enabled?: boolean;
|
|
239
247
|
defaultMode?: TranslationMode;
|
package/dist/index.js
CHANGED
|
@@ -1961,9 +1961,10 @@ class TransformRegistry {
|
|
|
1961
1961
|
}), matches[0];
|
|
1962
1962
|
}
|
|
1963
1963
|
}
|
|
1964
|
-
const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKey, request) => (request.model || "gpt-5")
|
|
1964
|
+
const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKey, request) => (request.model || "gpt-5").startsWith("gpt-5") ? await callGPT5ResponsesAPI(apiKey, request) : await callChatCompletionsAPI(apiKey, request), callGPT5ResponsesAPI = async (apiKey, request) => {
|
|
1965
1965
|
const model = request.model || "gpt-5", requestBody = {
|
|
1966
|
-
model: "gpt-5.1",
|
|
1966
|
+
model: model === "gpt-5" ? "gpt-5.1" : model,
|
|
1967
|
+
// Map gpt-5 to gpt-5.1, use others as-is
|
|
1967
1968
|
input: request.messages
|
|
1968
1969
|
// Uses 'input' instead of 'messages'
|
|
1969
1970
|
};
|
|
@@ -2053,9 +2054,29 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2053
2054
|
}, calculateCost = (tokens, model) => {
|
|
2054
2055
|
const pricing = {
|
|
2055
2056
|
"gpt-5": {
|
|
2056
|
-
input:
|
|
2057
|
-
output:
|
|
2058
|
-
reasoning:
|
|
2057
|
+
input: 125e-5,
|
|
2058
|
+
output: 0.01,
|
|
2059
|
+
reasoning: 0
|
|
2060
|
+
},
|
|
2061
|
+
"gpt-5.1": {
|
|
2062
|
+
input: 125e-5,
|
|
2063
|
+
output: 0.01,
|
|
2064
|
+
reasoning: 0
|
|
2065
|
+
},
|
|
2066
|
+
"gpt-5-mini": {
|
|
2067
|
+
input: 25e-5,
|
|
2068
|
+
output: 2e-3,
|
|
2069
|
+
reasoning: 0
|
|
2070
|
+
},
|
|
2071
|
+
"gpt-5-nano": {
|
|
2072
|
+
input: 5e-5,
|
|
2073
|
+
output: 4e-4,
|
|
2074
|
+
reasoning: 0
|
|
2075
|
+
},
|
|
2076
|
+
"gpt-5-pro": {
|
|
2077
|
+
input: 0.015,
|
|
2078
|
+
output: 0.12,
|
|
2079
|
+
reasoning: 0
|
|
2059
2080
|
},
|
|
2060
2081
|
"gpt-4o": {
|
|
2061
2082
|
input: 5e-6,
|
|
@@ -2089,7 +2110,7 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2089
2110
|
type: "field",
|
|
2090
2111
|
name: fieldName,
|
|
2091
2112
|
text: unescapeXml(text2),
|
|
2092
|
-
attributes: {},
|
|
2113
|
+
attributes: { type: "string" },
|
|
2093
2114
|
children: []
|
|
2094
2115
|
};
|
|
2095
2116
|
if (stack.length > 0) {
|
|
@@ -2162,7 +2183,13 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2162
2183
|
parent && parent.children.push(node);
|
|
2163
2184
|
} else
|
|
2164
2185
|
root.push(node);
|
|
2165
|
-
} else
|
|
2186
|
+
} else if (attributes.type === "string" && content && content.includes(closingTag))
|
|
2187
|
+
if (node.text = unescapeXml(content.replace(closingTag, "")), stack.length > 0) {
|
|
2188
|
+
const parent = stack[stack.length - 1];
|
|
2189
|
+
parent && parent.children.push(node);
|
|
2190
|
+
} else
|
|
2191
|
+
root.push(node);
|
|
2192
|
+
else
|
|
2166
2193
|
stack.push(node);
|
|
2167
2194
|
continue;
|
|
2168
2195
|
}
|
|
@@ -2294,7 +2321,7 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2294
2321
|
return;
|
|
2295
2322
|
}
|
|
2296
2323
|
if (typeof value == "string") {
|
|
2297
|
-
value.length > 0 && lines.push(`${
|
|
2324
|
+
value.length > 0 && lines.push(`${indentStr}<field name="${fieldName}" type="string">${escapeXml(value)}</field>`);
|
|
2298
2325
|
return;
|
|
2299
2326
|
}
|
|
2300
2327
|
(typeof value == "number" || typeof value == "boolean") && lines.push(`${indentStr}<field name="${fieldName}" type="${typeof value}" readonly="true">${value}</field>`);
|
|
@@ -2331,7 +2358,7 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2331
2358
|
return;
|
|
2332
2359
|
}
|
|
2333
2360
|
if (typeof value == "string" && value.length > 0) {
|
|
2334
|
-
lines.push(`${indentStr}<field name="${fieldName}">${escapeXml(value)}</field>`);
|
|
2361
|
+
lines.push(`${indentStr}<field name="${fieldName}" type="string">${escapeXml(value)}</field>`);
|
|
2335
2362
|
return;
|
|
2336
2363
|
}
|
|
2337
2364
|
(typeof value == "number" || typeof value == "boolean") && lines.push(`${indentStr}<field name="${fieldName}" type="${typeof value}" readonly="true">${value}</field>`);
|
|
@@ -2473,7 +2500,19 @@ const globalTransformRegistry = new TransformRegistry(), callGPT5 = async (apiKe
|
|
|
2473
2500
|
`Structure validation failed: ${unexpectedErrors.length} errors. First: ${unexpectedErrors[0].type} at ${unexpectedErrors[0].path.join(".")}`
|
|
2474
2501
|
);
|
|
2475
2502
|
}
|
|
2476
|
-
|
|
2503
|
+
const translationValidation = validateTranslationCompleteness(sourceDocument, result, languageFieldName);
|
|
2504
|
+
return translationValidation.valid || (devLog.warn("Translation completeness warning", {
|
|
2505
|
+
...logContext,
|
|
2506
|
+
skippedFields: translationValidation.skippedFields,
|
|
2507
|
+
warning: "GPT-5 may have skipped some important fields"
|
|
2508
|
+
}), devLog.info("Skipped field details", {
|
|
2509
|
+
...logContext,
|
|
2510
|
+
skippedFields: translationValidation.skippedFields.map((field) => ({
|
|
2511
|
+
path: field.path,
|
|
2512
|
+
originalValue: field.originalValue,
|
|
2513
|
+
translatedValue: field.translatedValue
|
|
2514
|
+
}))
|
|
2515
|
+
})), setJobStatus(dispatch, sourceDocument._id, targetLanguage, "translating_with_openai", {
|
|
2477
2516
|
tokensUsed: response.tokensUsed.total,
|
|
2478
2517
|
estimatedCost: response.cost
|
|
2479
2518
|
}), result;
|
|
@@ -2514,12 +2553,19 @@ ${sourceText}
|
|
|
2514
2553
|
|
|
2515
2554
|
## Instructions
|
|
2516
2555
|
|
|
2517
|
-
1.
|
|
2518
|
-
2.
|
|
2519
|
-
3.
|
|
2520
|
-
4.
|
|
2521
|
-
5.
|
|
2522
|
-
6.
|
|
2556
|
+
1. **TRANSLATE ALL TEXT FIELDS**: Every field marked with type="string" or containing text must be translated
|
|
2557
|
+
2. **DO NOT SKIP SIMPLE FIELDS**: Pay special attention to simple fields like "title", "name", "description" - these are often the most important
|
|
2558
|
+
3. Follow the guidelines and terminology requirements above
|
|
2559
|
+
4. Preserve protected phrases exactly as they appear
|
|
2560
|
+
5. Return the document in THE EXACT SAME FORMAT
|
|
2561
|
+
6. **CRITICAL**: Only modify text content between tags
|
|
2562
|
+
7. Never add, remove, or change any tags, attributes, or structure
|
|
2563
|
+
|
|
2564
|
+
**Common fields that must always be translated:**
|
|
2565
|
+
- title, name, heading, label
|
|
2566
|
+
- description, caption, alt text
|
|
2567
|
+
- All text within <field> tags
|
|
2568
|
+
- All text within <span> tags
|
|
2523
2569
|
|
|
2524
2570
|
If you used significant reasoning to make translation decisions, you may include a brief summary:
|
|
2525
2571
|
<reasoning>
|
|
@@ -2527,6 +2573,43 @@ Brief summary of key translation decisions and reasoning
|
|
|
2527
2573
|
</reasoning>
|
|
2528
2574
|
|
|
2529
2575
|
Then return the translated document:`;
|
|
2576
|
+
}, validateTranslationCompleteness = (sourceDoc, translatedDoc, languageFieldName) => {
|
|
2577
|
+
const skippedFields = [], checkField = (source, translated, path = []) => {
|
|
2578
|
+
if (path.length > 0) {
|
|
2579
|
+
const fieldName = path[path.length - 1];
|
|
2580
|
+
if (fieldName.startsWith("_") || fieldName === languageFieldName)
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
if (typeof source == "string" && typeof translated == "string") {
|
|
2584
|
+
if (source.length > 0 && source === translated) {
|
|
2585
|
+
const fieldName = path[path.length - 1] || "unknown";
|
|
2586
|
+
(["title", "name", "heading", "label", "description", "caption", "alt"].includes(
|
|
2587
|
+
fieldName
|
|
2588
|
+
) || source.length > 10) && skippedFields.push({
|
|
2589
|
+
path: path.join("."),
|
|
2590
|
+
originalValue: source,
|
|
2591
|
+
translatedValue: translated
|
|
2592
|
+
});
|
|
2593
|
+
}
|
|
2594
|
+
return;
|
|
2595
|
+
}
|
|
2596
|
+
if (source && translated && typeof source == "object" && typeof translated == "object") {
|
|
2597
|
+
if (Array.isArray(source) && Array.isArray(translated)) {
|
|
2598
|
+
const maxLength = Math.min(source.length, translated.length);
|
|
2599
|
+
for (let i = 0; i < maxLength; i++)
|
|
2600
|
+
checkField(source[i], translated[i], [...path, String(i)]);
|
|
2601
|
+
} else if (!Array.isArray(source) && !Array.isArray(translated))
|
|
2602
|
+
for (const key of Object.keys(source))
|
|
2603
|
+
!key.startsWith("_") && key !== languageFieldName && checkField(source[key], translated[key], [
|
|
2604
|
+
...path,
|
|
2605
|
+
key
|
|
2606
|
+
]);
|
|
2607
|
+
}
|
|
2608
|
+
};
|
|
2609
|
+
return checkField(sourceDoc, translatedDoc), {
|
|
2610
|
+
valid: skippedFields.length === 0,
|
|
2611
|
+
skippedFields
|
|
2612
|
+
};
|
|
2530
2613
|
}, assembleLanguageSpecs = ({
|
|
2531
2614
|
sourceDocument,
|
|
2532
2615
|
languageFieldName,
|