Skip to content

Commit d655b59

Browse files
committed
ci: wiring in to core.ai translation APIs
1 parent 3e68def commit d655b59

File tree

2 files changed

+124
-12
lines changed

2 files changed

+124
-12
lines changed

.github/workflows/tranlate-languages.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ jobs:
1111
- uses: actions/checkout@v3
1212
- name: Building phoenix
1313
env:
14-
GCP_API_KEY: ${{ secrets.GCP_API_KEY }}
15-
GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
14+
CORE_AI_TRANSLATE_API_KEY: ${{ secrets.CORE_AI_TRANSLATE_API_KEY }}
1615
run: |
1716
npm ci
1817
npm run _translateStrings

gulpfile.js/translateStrings.js

Lines changed: 123 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,109 @@
2020

2121
/* eslint-env node */
2222
const fs = require('fs');
23-
const projectId = process.env.GCP_PROJECT_ID;
24-
const API_KEY = process.env.GCP_API_KEY;
23+
const CORE_AI_TRANSLATE_API_KEY = process.env.CORE_AI_TRANSLATE_API_KEY;
24+
25+
// A global accumulator object initialized to zero
26+
const globalUtilizationMetrics = {
27+
tokens: {
28+
prompt: 0,
29+
candidates: 0,
30+
cachedContent: 0,
31+
total: 0
32+
},
33+
characters: {
34+
input: 0,
35+
output: 0
36+
},
37+
costs: {
38+
input: 0,
39+
output: 0,
40+
total: 0,
41+
currency: "USD"// or set once, if you always expect the same currency
42+
}
43+
};
44+
45+
/**
46+
* Aggregate the utilization metrics from a single object into a global accumulator.
47+
* @param {object} obj - An object with `utilizationMetrics` (tokens, characters, costs).
48+
* @returns {object} The updated global utilization metrics.
49+
*/
50+
function aggregateUtilizationMetrics(obj) {
51+
if (!obj || !obj.utilizationMetrics) {
52+
console.warn("Object missing 'utilizationMetrics' field, nothing to aggregate.");
53+
return globalUtilizationMetrics;
54+
}
55+
56+
const { tokens, characters, costs } = obj.utilizationMetrics;
57+
58+
// Safely add tokens
59+
if (tokens) {
60+
globalUtilizationMetrics.tokens.prompt += tokens.prompt || 0;
61+
globalUtilizationMetrics.tokens.candidates += tokens.candidates || 0;
62+
globalUtilizationMetrics.tokens.cachedContent += tokens.cachedContent || 0;
63+
globalUtilizationMetrics.tokens.total += tokens.total || 0;
64+
}
65+
66+
// Safely add characters
67+
if (characters) {
68+
globalUtilizationMetrics.characters.input += characters.input || 0;
69+
globalUtilizationMetrics.characters.output += characters.output || 0;
70+
}
71+
72+
// Safely add costs
73+
if (costs) {
74+
globalUtilizationMetrics.costs.input += costs.input || 0;
75+
globalUtilizationMetrics.costs.output += costs.output || 0;
76+
globalUtilizationMetrics.costs.total += costs.total || 0;
77+
// currency is assumed to remain consistent; you could also check or update it if needed
78+
}
79+
80+
return globalUtilizationMetrics;
81+
}
82+
83+
function getTranslationrequest(stringsToTranslate, lang) {
84+
return {
85+
translationContext: "This is a bunch of strings extracted from a JavaScript file used to develop our product with is a text editor. Some strings may have HTML or templates(mustache library used). Please translate these strings accurately.",
86+
"source": stringsToTranslate,
87+
"provider": "vertex",
88+
"sourceContext": {
89+
// this is currently unused. you can provide context specific to the key in the source to give the AI
90+
// additional context about the key for translation.
91+
},
92+
translationTargets: [lang] // multiple langs can be given here to translate at a time, for now using only one
93+
};
94+
}
95+
96+
/**
97+
* Sends translation payload to the specified API and returns the result.
98+
*
99+
* @param {object} apiInput - The translation payload object.
100+
* @returns {Promise<any>} The JSON-parsed response from the API.
101+
*/
102+
async function getTranslation(apiInput) {
103+
const url = "https://translate.core.ai/translate";
104+
try {
105+
const response = await fetch(url, {
106+
method: "POST",
107+
headers: {
108+
"Content-Type": "application/json",
109+
"authorization": `Basic ${CORE_AI_TRANSLATE_API_KEY}`
110+
},
111+
body: JSON.stringify(apiInput)
112+
});
113+
114+
if (!response.ok) {
115+
throw new Error(`Request failed with status ${response.status}`);
116+
}
117+
118+
// Parse and return the JSON response
119+
const data = await response.json();
120+
return data;
121+
} catch (error) {
122+
console.error("Error translating:", error);
123+
throw error;
124+
}
125+
}
25126

26127
function _getAllNLSFolders() {
27128
let names = fs.readdirSync('src/nls');
@@ -84,8 +185,11 @@ function _isTranslatableKey(key) {
84185
}
85186

86187
async function coreAiTranslate(stringsToTranslate, lang) {
87-
const translations = _getJson("/home/home/Downloads/full_transalation_phoenix.json", 'utf8');
88-
// console.log("Translation output: ", JSON.stringify(translations, null, 4)); todo uncomment
188+
const translationRequest = getTranslationrequest(stringsToTranslate, lang);
189+
const translations = await getTranslation(translationRequest);
190+
aggregateUtilizationMetrics(translations);
191+
console.log("Translation output: ", JSON.stringify(translations, null, 4));
192+
console.log("Aggregate utilization metrics: ", JSON.stringify(globalUtilizationMetrics, null, 4));
89193
if(translations.failedLanguages.length){
90194
const errorStr = `Error translating ${lang}. it has failures `;
91195
console.error(errorStr);
@@ -147,7 +251,14 @@ async function _processLang(lang) {
147251
// we have already translated this in the last pass.
148252
// Load expert translation if there is one else we don't need to translate, use existing translation as is.
149253
translations[rootKey] = expertTranslations[englishStringToTranslate] || localeStringsJS[rootKey];
150-
updatedLastTranslatedJSON[rootKey] = englishStringToTranslate;
254+
if(translations[rootKey]){
255+
updatedLastTranslatedJSON[rootKey] = englishStringToTranslate;
256+
} else {
257+
// we dont have a last local translation in locale strings.js file to use. this cannot happen
258+
// except in a translation reset pass where we delete all translations and restart like when we moved
259+
// to core.ai auto translate.
260+
pendingTranslate[rootKey] = englishStringToTranslate;
261+
}
151262
} else {
152263
// this is a new english string or there is a string change.
153264
if(expertTranslations[englishStringToTranslate]){
@@ -173,11 +284,13 @@ async function _processLang(lang) {
173284
}
174285
let englishStringToTranslate = rootStrings[rootKey];
175286
const translatedText = aiTranslations[rootKey];
176-
translations[rootKey] = translatedText;
177-
updatedLastTranslatedJSON[rootKey] = englishStringToTranslate;
287+
if(translatedText){
288+
translations[rootKey] = translatedText;
289+
updatedLastTranslatedJSON[rootKey] = englishStringToTranslate;
290+
}
178291
}
179292
// now detect any keys that has not yet been translated
180-
const allKeys = Object.keys(rootStrings);
293+
const allKeys = Object.keys(rootStrings).filter(_isTranslatableKey);
181294
const translatedKeys = Object.keys(translations);
182295
const notTranslated = allKeys.filter(key => !translatedKeys.includes(key));
183296
if(notTranslated.length){
@@ -193,12 +306,12 @@ async function _processLang(lang) {
193306
}
194307

195308
async function translate() {
196-
console.log("please make sure that AWS/Google credentials are available as env vars.");
309+
console.log("please make sure that core.ai lang translation service credentials are available as env vars.");
197310
return new Promise(async (resolve)=>{
198311
let langs = _getAllNLSFolders();
199312
console.log(langs);
200313
for(let lang of langs){
201-
_processLang(lang);
314+
await _processLang(lang);
202315
}
203316
resolve();
204317
});

0 commit comments

Comments
 (0)