Skip to content

Commit 10732b3

Browse files
authored
Add a view for what models will show in Nightly (#1175)
* Add a view for what models will show in Nightly * WIP - Updates
1 parent 02042c1 commit 10732b3

File tree

2 files changed

+129
-38
lines changed

2 files changed

+129
-38
lines changed

site/firefox-models/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ <h1>Firefox Translations Models</h1>
3535

3636
<div class="settings">
3737
<input type='checkbox' id="releasedModels" />
38-
<label for="releasedModels">Released Models Only</label><br/>
38+
<label for="releasedModels">Released Models</label><br/>
39+
<input type='checkbox' id="nightlyModels" />
40+
<label for="nightlyModels">Nightly Models</label><br/>
3941
<input type='checkbox' id="showAdditionalDetails" />
4042
<label for="showAdditionalDetails">Show Additional Details</label><br/>
4143
<input type='checkbox' id="remoteSettingsPreview" />

site/firefox-models/models.mjs

Lines changed: 126 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,47 @@ function setupRemoteSettingsPreview() {
5454
return isPreview;
5555
}
5656

57-
function setupReleasedModels() {
57+
function setupReleaseChannelCheckbox() {
5858
const releasedModelsCheckbox = /** @type {HTMLInputElement} */ (
5959
getElement("releasedModels")
6060
);
61+
const nightlyModelsCheckbox = /** @type {HTMLInputElement} */ (
62+
getElement("nightlyModels")
63+
);
6164
const urlParams = new URLSearchParams(window.location.search);
62-
const urlValue = urlParams.get("releasedModels");
63-
const isReleasedModels = urlValue === "true" || !urlValue;
65+
const releasedModelsUrlValue = urlParams.get("releasedModels");
66+
const isReleasedModels =
67+
releasedModelsUrlValue === "true" || !releasedModelsUrlValue;
68+
6469
releasedModelsCheckbox.checked = isReleasedModels;
6570
releasedModelsCheckbox.addEventListener("change", () => {
6671
const urlParams = new URLSearchParams(window.location.search);
6772
if (releasedModelsCheckbox.checked) {
6873
urlParams.delete("releasedModels");
74+
nightlyModelsCheckbox.checked = false;
75+
urlParams.delete("nightlyModels");
6976
} else {
7077
urlParams.set("releasedModels", "false");
7178
}
7279
changeLocation(urlParams);
7380
});
7481

75-
return isReleasedModels;
82+
const nightlyModelsUrlValue = urlParams.get("nightlyModels");
83+
const isNightlyModels = !isReleasedModels && nightlyModelsUrlValue === "true";
84+
nightlyModelsCheckbox.checked = isNightlyModels;
85+
nightlyModelsCheckbox.addEventListener("change", () => {
86+
const urlParams = new URLSearchParams(window.location.search);
87+
if (nightlyModelsCheckbox.checked) {
88+
urlParams.set("nightlyModels", "true");
89+
urlParams.set("releasedModels", "false");
90+
releasedModelsCheckbox.checked = false;
91+
} else {
92+
urlParams.delete("nightlyModels");
93+
}
94+
changeLocation(urlParams);
95+
});
96+
97+
return [isReleasedModels, isNightlyModels];
7698
}
7799

78100
function setupShowAdditionalDetails() {
@@ -122,14 +144,33 @@ function getReleasedModels(models) {
122144
return (models = [...langPairs.values()]);
123145
}
124146

147+
/**
148+
* @param {ModelRecord[]} models
149+
*/
150+
function getNightlyModels(models) {
151+
/** @type {Map<string, ModelRecord>} */
152+
const langPairs = new Map();
153+
for (const model of models) {
154+
const langPair = model.fromLang + "-" + model.toLang;
155+
const existingModel = langPairs.get(langPair);
156+
if (
157+
!existingModel ||
158+
versionCompare(model.version, existingModel.version) > 0
159+
) {
160+
langPairs.set(langPair, model);
161+
}
162+
}
163+
return (models = [...langPairs.values()]);
164+
}
165+
125166
async function main() {
126167
getElement("counts").style.display = "table";
127168

128169
const isPreview = setupRemoteSettingsPreview();
129170
const bucket = isPreview ? "main-preview" : "main";
130171

131172
setupShowAdditionalDetails();
132-
const isReleasedModels = setupReleasedModels();
173+
const [isReleasedModels, isNightlyModels] = setupReleaseChannelCheckbox();
133174

134175
/** @type {{ data: ModelRecord[] }} */
135176
const records = await fetchJSON(
@@ -143,12 +184,9 @@ async function main() {
143184
const cometResults = await fetchJSON(
144185
"https://raw.githubusercontent.com/mozilla/firefox-translations-models/main/evaluation/comet-results.json"
145186
);
187+
exposeAsGlobal("cometResults", cometResults);
146188

147-
logCometResults(cometResults);
148-
149-
/** @type {Record<string, string>} */
150-
const byHash = await fetchJSON(REPO_URL + "models/by-hash.json");
151-
exposeAsGlobal("byHash", byHash);
189+
const modelMetadataFetcher = await ModelMetadataFetcher.create();
152190

153191
/**
154192
* @typedef {Object} ModelEntry
@@ -164,10 +202,13 @@ async function main() {
164202
const releasedModels = getReleasedModels(models);
165203

166204
countModels(models, releasedModels);
205+
logModelTableData(cometResults, models, modelMetadataFetcher);
167206

168207
if (isReleasedModels) {
169208
// Get the released model with the latest version.
170209
models = releasedModels;
210+
} else if (isNightlyModels) {
211+
models = getNightlyModels(models);
171212
}
172213
exposeAsGlobal("models", models);
173214

@@ -183,7 +224,7 @@ async function main() {
183224
* @param {string} version
184225
*/
185226
function getModelKey(lang, version) {
186-
if (isReleasedModels) {
227+
if (isReleasedModels || isNightlyModels) {
187228
return lang;
188229
}
189230
return lang + " " + version;
@@ -275,7 +316,7 @@ async function main() {
275316
`${lang}-en`,
276317
records.data,
277318
cometResults,
278-
byHash,
319+
modelMetadataFetcher,
279320
attachmentsByKey,
280321
toEn,
281322
langPairScoreAdded
@@ -285,7 +326,7 @@ async function main() {
285326
`en-${lang}`,
286327
records.data,
287328
cometResults,
288-
byHash,
329+
modelMetadataFetcher,
289330
attachmentsByKey,
290331
fromEn,
291332
langPairScoreAdded
@@ -301,7 +342,7 @@ async function main() {
301342
* @param {string} pair
302343
* @param {ModelRecord[]} records
303344
* @param {EvalResults} cometResults
304-
* @param {Record<string, string>} byHash
345+
* @param {ModelMetadataFetcher} modelMetadataFetcher
305346
* @param {Map<string, Array<[string, string]>>} attachmentsByKey
306347
* @param {ModelRecord | null} model
307348
* @param {Set<string>} langPairScoreAdded
@@ -311,7 +352,7 @@ function addToRow(
311352
pair,
312353
records,
313354
cometResults,
314-
byHash,
355+
modelMetadataFetcher,
315356
attachmentsByKey,
316357
model,
317358
langPairScoreAdded
@@ -401,7 +442,7 @@ function addToRow(
401442
const parametersEl = td();
402443
parametersEl.className = "parametersColumn";
403444

404-
getModelMetadata(byHash, model).then((modelMetadata) => {
445+
modelMetadataFetcher.get(model).then((modelMetadata) => {
405446
if (!modelMetadata) {
406447
return;
407448
}
@@ -641,21 +682,48 @@ assertComparison("1.0a", "1.1", aLessThanB);
641682

642683
/**
643684
* @param {EvalResults} cometResults
685+
* @param {ModelRecord[]} models,
686+
* @param {ModelMetadataFetcher} modelMetadataFetcher
644687
*/
645-
function logCometResults(cometResults) {
688+
async function logModelTableData(cometResults, models, modelMetadataFetcher) {
646689
/** @type {Array<unknown[]>} */
647690
const xx_en = [];
648691
const en_xx = [];
649692

650-
for (const [langPair, evaluation] of Object.entries(cometResults)) {
651-
const flores = evaluation["flores-dev"];
693+
/**
694+
* Combine the cometResults and model records into a single list of langpairs.
695+
* It's not guaranteed that both lists overlap, so we need to combine both.
696+
* @type {Map<string, ModelRecord | null>}
697+
*/
698+
const modelsByLangPair = new Map();
699+
for (const langPair of Object.keys(cometResults)) {
700+
modelsByLangPair.set(langPair, null);
701+
}
702+
for (const model of getNightlyModels(models)) {
703+
// Get the Nightly models as this will be a flattened list of all models without
704+
// duplicates of various versions.
705+
let { fromLang, toLang } = model;
706+
if (fromLang === "zh-Hans") {
707+
fromLang = "zh";
708+
}
709+
if (toLang === "zh-Hans") {
710+
toLang = "zh";
711+
}
712+
modelsByLangPair.set(`${fromLang}-${toLang}`, model);
713+
}
714+
715+
for (const [langPair, model] of modelsByLangPair) {
716+
const modelMetadata = await modelMetadataFetcher.get(model);
717+
const evaluation = cometResults[langPair];
652718
const [fromLang, toLang] = langPair.split("-");
653719
const row = [
654720
langPair,
655721
fromLang,
656722
toLang,
657-
flores.google || "",
658-
flores.bergamot || "",
723+
evaluation?.["flores-test"]?.google ?? "",
724+
modelMetadata?.flores["comet"] ?? "",
725+
getReleaseChannels(model)?.label ?? "",
726+
modelMetadata?.architecture ?? "",
659727
];
660728
if (fromLang === "en") {
661729
en_xx.push(row);
@@ -675,7 +743,7 @@ function logCometResults(cometResults) {
675743
en_xx.sort(sortRow);
676744

677745
const rows = [
678-
["Lang Pair", "From", "To", "Google", "Bergamot"],
746+
["Lang Pair", "From", "To", "Google", "Mozilla", "Release", "Architecture"],
679747
...en_xx,
680748
...xx_en,
681749
];
@@ -751,25 +819,46 @@ function countModels(allModels, releasedModels) {
751819

752820
getElement("fromProd").innerText = String(fromProd.size);
753821
getElement("toProd").innerText = String(toProd.size);
754-
getElement("fromNightly").innerText = String(toNightly.size);
755-
getElement("toNightly").innerText = String(fromNightly.size);
822+
getElement("fromNightly").innerText = String(fromNightly.size);
823+
getElement("toNightly").innerText = String(toNightly.size);
756824
getElement("uniqueLanguages").innerText = String(unique.size);
757825
}
758826

759-
/**
760-
* @param {Record<string, string>} byHash
761-
* @param {ModelRecord | null} model
762-
* @return {Promise<ModelMetadata | null>}
763-
*/
764-
async function getModelMetadata(byHash, model) {
765-
if (!model) {
766-
return null;
827+
class ModelMetadataFetcher {
828+
/** @type {Record<string, Promise<ModelMetadataFetcher | null>>} */
829+
metadataCache = {};
830+
831+
static async create() {
832+
/** @type {Record<string, string>} */
833+
const byHash = await fetchJSON(REPO_URL + "models/by-hash.json");
834+
exposeAsGlobal("byHash", byHash);
835+
return new ModelMetadataFetcher(byHash);
767836
}
768-
const metadataUrl = byHash[model.attachment.hash];
769-
if (!metadataUrl) {
770-
return null;
837+
/**
838+
* @param {Record<string, string>} byHash
839+
*/
840+
constructor(byHash) {
841+
this.byHash = byHash;
771842
}
772843

773-
const response = await fetch(REPO_URL + metadataUrl);
774-
return response.json();
844+
/**
845+
* @param {ModelRecord | null} model
846+
* @return {Promise<ModelMetadata | null>}
847+
*/
848+
async get(model) {
849+
if (!model) {
850+
return null;
851+
}
852+
const metadataUrl = this.byHash[model.attachment.hash];
853+
if (!metadataUrl) {
854+
return null;
855+
}
856+
857+
const promise = fetch(REPO_URL + metadataUrl).then((response) =>
858+
response.json()
859+
);
860+
this.metadataCache[metadataUrl] = promise;
861+
862+
return promise;
863+
}
775864
}

0 commit comments

Comments
 (0)