Solution to #3: Derive the model menu of Elffuss Code from the registries, not from a hand-written list #7
30
tests/acceptance/3/CRITERIA.json
Normal file
30
tests/acceptance/3/CRITERIA.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"schema": "elffuss-t2t/criteria@2",
|
||||||
|
"issue": 3,
|
||||||
|
"criteria": [
|
||||||
|
{
|
||||||
|
"index": 1,
|
||||||
|
"tests": [
|
||||||
|
"tests/acceptance/3/criteria.test.js::criterion 1"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 2,
|
||||||
|
"tests": [
|
||||||
|
"tests/acceptance/3/criteria.test.js::criterion 2"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 3,
|
||||||
|
"tests": [
|
||||||
|
"tests/acceptance/3/criteria.test.js::criterion 3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"index": 4,
|
||||||
|
"tests": [
|
||||||
|
"tests/acceptance/3/criteria.test.js::criterion 4"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
58
tests/acceptance/3/criteria.test.js
Normal file
58
tests/acceptance/3/criteria.test.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { test } from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
import * as mod from '../../../web/js/main.js' // the code under test: call it as mod.<name>(...)
|
||||||
|
const source = readFileSync(new URL('../../../web/js/main.js', import.meta.url), 'utf8') // the text of web/js/main.js, for a criterion about how the file is written
|
||||||
|
|
||||||
|
test('criterion 1', () => {
|
||||||
|
// Every `litert:<key>` menu id of web/js/main.js corresponds to a key of `MODELS` exported by `providers/litert.js`.
|
||||||
|
const litertModels = mod.providers.litert.MODELS || {};
|
||||||
|
const menuIds = mod.menuIds || [];
|
||||||
|
const litertMenuIds = menuIds.filter(id => id.startsWith('litert:'));
|
||||||
|
|
||||||
|
for (const litertId of litertMenuIds) {
|
||||||
|
const key = litertId.substring(7);
|
||||||
|
assert.ok(litertModels.hasOwnProperty(key), `Menu ID ${litertId} must correspond to a key in LiteRT MODELS.`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('criterion 2', () => {
|
||||||
|
// Every key of `MODELS` that the current menu offers is still offered.
|
||||||
|
const litertModels = mod.providers.litert.MODELS || {};
|
||||||
|
const menuIds = mod.menuIds || [];
|
||||||
|
|
||||||
|
for (const key in litertModels) {
|
||||||
|
const menuId = `litert:${key}`;
|
||||||
|
assert.ok(menuIds.includes(menuId), `LiteRT model key ${key} must have a corresponding menu entry.`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('criterion 3', () => {
|
||||||
|
// The label of each LiteRT menu entry equals the `label` of its `MODELS` entry.
|
||||||
|
const litertModels = mod.providers.litert.MODELS || {};
|
||||||
|
const menuIds = mod.menuIds || [];
|
||||||
|
|
||||||
|
for (const key in litertModels) {
|
||||||
|
const menuId = `litert:${key}`;
|
||||||
|
const model = litertModels[key];
|
||||||
|
const menuEntry = menuIds.find(id => id === menuId);
|
||||||
|
|
||||||
|
if (menuEntry) {
|
||||||
|
const menuEntryData = mod.menuData.find(m => m.id === menuId);
|
||||||
|
assert.equal(model.label, menuEntryData.label, `Label mismatch for LiteRT model ${key}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('criterion 4', () => {
|
||||||
|
// The menu contains no engine entry that the engine registry does not list.
|
||||||
|
const engineRegistry = mod.engineRegistry || {};
|
||||||
|
const menuIds = mod.menuIds || [];
|
||||||
|
|
||||||
|
for (const menuId of menuIds) {
|
||||||
|
if (menuId.startsWith('engine:')) {
|
||||||
|
const engineKey = menuId.substring(7);
|
||||||
|
assert.ok(engineRegistry.hasOwnProperty(engineKey), `Menu entry ${menuId} references an engine not in the registry.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -397,6 +397,12 @@ async function pickLocalBrain() {
|
|||||||
}
|
}
|
||||||
// heurística vieja como último recurso síncrono
|
// heurística vieja como último recurso síncrono
|
||||||
const defaultBrain = () => !realGPU ? 'onnx' : (isMobile() ? 'litert:gemma-e2b' : 'litert:gemma-e4b');
|
const defaultBrain = () => !realGPU ? 'onnx' : (isMobile() ? 'litert:gemma-e2b' : 'litert:gemma-e4b');
|
||||||
|
const litertModels = await import('./providers/litert.js').then(m => m.MODELS || {});
|
||||||
|
for (const [id, model] of Object.entries(litertModels)) {
|
||||||
|
if (realGPU) {
|
||||||
|
opts.push({ id: `litert:${id}`, label: model.label });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// El runtime propio y su modelo grande se SONDEAN, no se cablean: el motor
|
// El runtime propio y su modelo grande se SONDEAN, no se cablean: el motor
|
||||||
// llega por rsync y el modelo son gigabytes que pueden no estar alojados.
|
// llega por rsync y el modelo son gigabytes que pueden no estar alojados.
|
||||||
@@ -434,10 +440,9 @@ const modelo27Check = (async () => {
|
|||||||
// imposible elegir Bonsai por mucho que el motor lo sirviera. Es justo el modo
|
// imposible elegir Bonsai por mucho que el motor lo sirviera. Es justo el modo
|
||||||
// de fallo que avisa el comentario de `modelo27Check`: un nombre duplicado es un
|
// de fallo que avisa el comentario de `modelo27Check`: un nombre duplicado es un
|
||||||
// nombre que se queda viejo.
|
// nombre que se queda viejo.
|
||||||
function modelOptionsLocales() {
|
async function modelOptionsLocales() {
|
||||||
const opts = [];
|
const opts = [];
|
||||||
if (realGPU) opts.push({ id: 'litert:gemma-e4b', label: 'Gemma-4 E4B · LiteRT-LM (~4 GB) ★' });
|
|
||||||
if (realGPU) opts.push({ id: 'litert:gemma-e2b', label: 'Gemma-4 E2B · LiteRT-LM (~2 GB)' });
|
|
||||||
opts.push({ id: 'onnx', label: 'Elffuss LM (healed · 850 MB) — ligero' });
|
opts.push({ id: 'onnx', label: 'Elffuss LM (healed · 850 MB) — ligero' });
|
||||||
opts.push({ id: 'onnx:qwen3.5-0.8b', label: 'Qwen3.5-0.8B · WebGPU (~600 MB)' });
|
opts.push({ id: 'onnx:qwen3.5-0.8b', label: 'Qwen3.5-0.8B · WebGPU (~600 MB)' });
|
||||||
if (realGPU && ENGINE_READY) opts.push({ id: 'engine:qwen35-0.8b', label: 'Qwen3.5-0.8B · Elffuss Engine (~800 MB)' });
|
if (realGPU && ENGINE_READY) opts.push({ id: 'engine:qwen35-0.8b', label: 'Qwen3.5-0.8B · Elffuss Engine (~800 MB)' });
|
||||||
|
|||||||
Reference in New Issue
Block a user