release: v0.2.37 — zero-argument startup, first-run wizard, real download progress

Startup no longer needs arguments: the config is created on first launch, a busy
port is swapped for a free one, the console is open by default, and the browser
lands on a setup wizard that downloads a ready-made .cmf model and wires it into
routing (managed server + routing.default) when you press Finish.

Download progress was reported from curl's own meter, which prints a full
0→100% bar for the -L redirect hop before the transfer starts — so every job
showed 100% and then hung. Progress now comes from bytes on disk against the
size from the HuggingFace tree (or a HEAD request).

The UI and the gateway also disagreed about the output file name, so a finished
download never marked its card as downloaded; the gateway now returns the exact
stem and the UI sends it back verbatim for download, progress and delete.

Featured models are read live from the infosave HF account in both the wizard
and Import, and all 432 UI strings now exist in every one of the 7 languages.
This commit is contained in:
infosave2007
2026-08-06 17:56:42 +03:00
parent ad438a5dcb
commit 9ec8861464
25 changed files with 3154 additions and 234 deletions
+191 -3
View File
@@ -30,7 +30,7 @@ export default {
"nav.routing": "Enrutamiento",
"nav.protocols": "Protocolos",
"nav.keys": "Claves API",
"nav.playground": "Playground",
"nav.playground": "Zona de pruebas",
"nav.settings": "Configuración",
"nav.foot": "Gateway · v{v}",
"nav.billing": "Clave del router y pagos",
@@ -76,7 +76,7 @@ export default {
"dash.groupby.label": "por tarea",
"dash.groupby.account": "por cuenta",
"dash.recent.title": "Solicitudes recientes",
"dash.recent.empty": "Sin solicitudes registradas. Prueba el Playground.",
"dash.recent.empty": "Sin solicitudes registradas. Prueba la Zona de pruebas.",
"dash.col.time": "Hora",
"dash.col.model": "Modelo",
"dash.col.tier": "Nivel",
@@ -189,7 +189,7 @@ export default {
"keys.allowAll": "todos",
// playground
"pg.title": "Playground",
"pg.title": "Zona de pruebas",
"pg.subtitle": "Envía un prompt por el pipeline en vivo e inspecciona la decisión de enrutamiento.",
"pg.system": "Prompt del sistema",
"pg.systemPlaceholder": "mensaje de sistema opcional",
@@ -280,4 +280,192 @@ export default {
"settings.cache.ttl": "TTL de entrada",
"settings.cache.embed": "Modelo de embeddings",
"settings.cache.note": "Los ajustes del caché se aplican tras reiniciar.",
// ── translated from en.js (previously falling back to English) ──
// nav
"nav.import": "Importar modelo",
// dash
"dash.clear": "Borrar registros",
"dash.clearConfirm": "¿Borrar todas las estadísticas y registros guardados? Esta acción no se puede deshacer.",
"dash.cleared": "Registros borrados",
// models
"models.form.idOrModel": "Introduce un id o un nombre de modelo",
"models.form.apiKey": "Clave API",
"models.form.apiKeyPlaceholder": "pega tu clave API",
"models.form.apiKeyNotNeeded": "no necesaria (servidor local)",
"models.form.apiKeyHint": "Se guarda de forma segura en la pasarela; no se vuelve a mostrar.",
"models.form.keyStored": "Hay una clave guardada — déjalo vacío para conservarla o pega una nueva.",
"models.form.keyFromEnv": "Se está usando una clave de una variable de entorno.",
"models.form.paramsTitle": "Parámetros de generación y razonamiento (O(1), MTP, temperatura)",
"models.form.temperature": "Temperatura",
"models.form.temperatureHint": "Temperatura de generación (0.0 = determinista, 1.0+ = creativa).",
"models.form.topP": "Top-P (muestreo nucleus)",
"models.form.topPHint": "Umbral de muestreo (0.0 .. 1.0).",
"models.form.maxTokens": "Tokens máx.",
"models.form.maxTokensHint": "Número máximo de tokens generados.",
"models.form.thinkBudget": "Modo razonamiento / presupuesto de pensamiento",
"models.form.thinkBudgetHint": "Presupuesto de tokens de razonamiento (0 = desactivado, N > 0 = máximo de tokens de razonamiento).",
"models.form.systemPrompt": "Prompt de sistema por defecto",
"models.form.systemPromptHint": "Se antepone automáticamente si la petición no trae prompt de sistema.",
"models.form.systemPromptPlaceholder": "Eres un asistente útil…",
"models.form.testKey": "Probar y cargar modelos",
"models.form.testing": "comprobando…",
"models.form.foundModels": "{n} modelos cargados",
"models.form.modelPlaceholder": "escribe para buscar…",
"models.form.modelHint": "Pulsa «Probar y cargar modelos» para traer la lista con tu clave y luego elige uno.",
"models.managed": "gestionado",
"models.managedHint": "Lo proporciona el servidor CMF gestionado (Ajustes → Modelos locales). Configúralo allí.",
// settings
"settings.router.enabled": "Usar el router (desactivado = solo modelos locales)",
"settings.cmf": "Modelos locales (CMF)",
"settings.cmf.localOnly": "Usar solo modelos locales",
"settings.cmf.manage": "Ejecutar un servidor de modelo local",
"settings.cmf.autoInstall": "Instalar cortiq automáticamente (crates.io)",
"settings.cmf.autoUpdate": "Actualizar cortiq automáticamente",
"settings.cmf.model": "Ruta del modelo .cmf local",
"settings.cmf.port": "Puerto del servidor local",
"settings.cmf.modelId": "Id del modelo en el pool",
"settings.cmf.note": "Sirve un .cmf local mediante cortiq (github.com/infosave2007/cmf). Funciona incluso cuando el router no está disponible.",
"settings.cmf.threads": "Hilos de trabajo",
"settings.cmf.gpu": "Usar GPU (Metal / wgpu)",
"settings.cmf.speedNote": "Los hilos paralelizan los matvec de decodificación (~3× más rápido; 0 = valor por defecto del runtime). Descargar en GPU ayuda sobre todo al prefill de prompts largos — la decodificación está limitada por el ancho de banda de memoria, así que en Apple Silicon es prácticamente neutro.",
"settings.cmf.pick": "selecciona un modelo",
"settings.cmf.models": "Modelos locales",
"settings.cmf.modelsHelp": "Cada fila es un modelo que la pasarela ejecuta (su propio cortiq serve en su propio puerto) y registra en el pool. Añade tantos como archivos .cmf y puertos libres tengas.",
"settings.cmf.rowHint": "id · archivo .cmf · puerto · hilos · GPU. Los cambios se aplican al reiniciar; borrar una fila detiene ese servidor.",
"settings.cmf.addServer": "+ Añadir modelo local",
"settings.cmf.checkPort": "Comprobar puerto",
"settings.cmf.portFree": "puerto libre",
"settings.cmf.portBusy": "puerto ocupado",
"settings.cmf.useSuggested": "usar {port}",
"settings.cmf.serving": "sirviendo {model} en :{port}",
"settings.cmf.intro": "Ejecuta modelos en local con el motor cortiq — funciona incluso sin conexión o cuando el router está caído.",
"settings.cmf.runtime": "Runtime cortiq",
"settings.cmf.runtimeHelp": "La herramienta de línea de comandos `cortiq` que carga y ejecuta modelos .cmf. Instálala una vez antes de arrancar un servidor local.",
"settings.cmf.install": "Instalar cortiq",
"settings.cmf.reinstall": "Reinstalar / actualizar",
"settings.cmf.installing": "instalando… (compila, unos minutos)",
"settings.cmf.installedOk": "cortiq instalado",
"settings.cmf.notInstalled": "no instalado",
"settings.cmf.addModel": "Añadir un modelo",
"settings.cmf.addModelHelp": "Los modelos se descargan y convierten en la pestaña Importar (HuggingFace → .cmf). Después apunta el servidor local al archivo .cmf de abajo.",
"settings.cmf.importNote": "¿Aún no tienes modelo? Importa uno:",
"settings.cmf.importLink": "Abrir Importar",
"settings.cmf.modelHelp": "Ruta del archivo .cmf que se va a servir (p. ej. models/qwen.cmf), generado en la pestaña Importar. ¿Varios modelos para distintas tareas? Impórtalos y cambia esta ruta.",
"settings.cmf.portHelp": "Puerto TCP en el que escucha el servidor de modelo local (8081 por defecto). Cámbialo solo si choca con otro servicio.",
"settings.cmf.threadsHelp": "Cuántos núcleos de CPU usar para la generación. Más = más rápido (~3× frente a un núcleo); 0 deja que decida el runtime.",
"settings.cmf.gpuHelp": "Descargar el cálculo matricial en la GPU (Metal/wgpu). En Apple Silicon acelera sobre todo los prompts largos — la decodificación está limitada por el ancho de banda de memoria.",
"settings.cmf.localOnlyHelp": "Enviar cada petición al modelo local, ignorando el router y cualquier proveedor en la nube.",
"settings.cmf.manageHelp": "La pasarela arranca `cortiq serve` para el .cmf de abajo al iniciarse y enruta hacia él. Requiere cortiq instalado (paso 1). Se aplica al reiniciar.",
"settings.cmf.autoInstallHelp": "Si cortiq falta al arrancar, instalarlo automáticamente desde crates.io.",
"settings.cmf.autoUpdateHelp": "Al arrancar, actualizar cortiq a la versión más reciente si existe una más nueva.",
"settings.router.enabledHelp": "Dejar que el router de cortiq clasifique cada petición (tipo de tarea + complejidad) y elija un modelo. Desactivado = usar solo la tabla de enrutado local.",
"settings.router.verifyTlsHelp": "Verificar el certificado TLS del router. Desactívalo solo con un certificado autofirmado o ligado a una IP.",
"settings.telemetry.metricsHelp": "Exponer métricas Prometheus en GET /metrics.",
"settings.cortiq.echoHelp": "Añadir un campo `cortiq` (tarea, modelo, coste) al cuerpo de cada respuesta. Las cabeceras de respuesta se envían siempre.",
"settings.stats.enabledHelp": "Registrar las peticiones recientes para alimentar el panel y las gráficas.",
"settings.cache.enabledHelp": "Caché semántica: devolver una respuesta guardada cuando un prompt nuevo se parece mucho a uno anterior, evitando la llamada al modelo. Requiere un modelo de embeddings en el pool.",
"settings.log.bodiesHelp": "Registrar el cuerpo completo de los mensajes de petición y respuesta. Desactivado por defecto por privacidad.",
"settings.router.saveKey": "Guardar clave",
"settings.router.keySaved": "Clave del router guardada",
"settings.router.keyEmpty": "Pega antes la clave",
"settings.router.secretHelp": "Pega la clave del correo de api.allaigate.com y pulsa Guardar clave. NO es un valor de «Claves API» — esas son para los clientes que llaman a tu pasarela.",
"settings.router.keyStoredMask": "•••••••••••• — clave guardada (pega otra para reemplazarla)",
"settings.route.strategyHelp": "Qué texto se envía al router para clasificar: last_user = solo el último mensaje del usuario; last_user_plus_system = último mensaje del usuario + prompt de sistema; concat_all = toda la conversación.",
"settings.route.maxCharsHelp": "Máximo de caracteres de ese texto enviados al router (lo que exceda se recorta). Más = clasificación más precisa, pero llamadas al router más lentas y caras.",
"settings.route.cacheTtlHelp": "Cuánto tiempo se cachea una decisión de enrutado para una petición idéntica antes de volver a preguntar al router (p. ej. 60s, 5m).",
"settings.route.profileHelp": "Perfil de enrutado por defecto cuando la petición no indica ninguno: cost-saver (el más barato), balanced o quality-first (el mejor modelo). Influye en qué modelo elige el router.",
// import
"import.subtitle": "Busca en HuggingFace, convierte a un .cmf local y sírvelo.",
"import.searchPlaceholder": "Buscar modelos en HuggingFace (p. ej. qwen3, llama)…",
"import.searching": "Buscando…",
"import.noResults": "No se encontraron modelos.",
"import.configure": "Configurar la conversión",
"import.outName": "Nombre de salida",
"import.outNameHint": "Nombre del archivo .cmf (letras, dígitos, - y _).",
"import.quant": "Cuantización",
"import.convert": "Convertir y cargar",
"import.converting": "Convirtiendo",
"import.started": "Conversión iniciada",
"import.failed": "La conversión falló — revisa el registro.",
"import.register": "Registrar como modelo local",
"import.registered": "Registrado como «{id}». Ejecuta cortiq-server para servirlo.",
"import.jobs": "Descargas y conversiones",
"import.cancel": "Cancelar",
"import.cancelling": "Cancelando — limpiando el archivo incompleto…",
"import.delete": "Eliminar",
"import.deleteConfirm": "¿Eliminar este modelo y su archivo .cmf convertido del disco?",
"import.deleted": "Eliminado junto con sus archivos",
"import.state.running": "en curso",
"import.state.done": "listo",
"import.state.error": "error",
"import.state.cancelled": "cancelado",
"import.featuredTitle": "Modelos .cmf listos para usar",
"import.featuredSub": "Modelos .cmf ya convertidos de la cuenta infosave en HuggingFace — la lista se lee en vivo, los modelos nuevos aparecen solos.",
"import.readyCmfBadge": ".cmf listo",
"import.directDownload": "Descargar",
"import.q.q8_2f": "8 bits de dos campos (𝒲×θ) — recomendado: mejor relación calidad/tamaño.",
"import.q.q8_row": "8 bits por fila — simple y robusto.",
"import.q.q4": "4 bits por bloque — el más pequeño, menor calidad.",
"import.q.q4_tiled": "4 bits en mosaico (Q4_TILED) — 1,66× más rápido en GPU Metal.",
"import.q.vbit": "Variable 3–8 bits — presupuesto de bits por experto.",
"import.q.q1t": "Ternario de 1,58 bits (Q1T) — BitNet b1.58 con valores atípicos dispersos.",
"import.q.q1s": "1 bit disperso (Q1S) — base de 1 bit con valores atípicos holográficos.",
"import.q.q1p": "1 bit PTQ (Q1P) — 1 bit con difusión de error.",
"import.q.q1": "1 bit (Q1) — formato puramente binario.",
"import.q.f16": "16 bits — sin cuantización.",
"import.q.f32": "32 bits — precisión completa (archivo grande).",
"import.adv.title": "Ajustes avanzados",
"import.adv.auto": "auto",
"import.adv.linearCore": "Núcleo lineal",
"import.adv.linearHint": "Cómo se trasladan o pliegan las capas de atención lineal.",
"import.adv.nphase": "vmf_phase fases/cabeza",
"import.adv.vbitShape": "Forma del presupuesto VBIT",
"import.adv.meanBits": "Bits medios VBIT",
"import.adv.shard": "Tamaño máx. de shard (GB)",
"import.adv.noShard": "sin fragmentar",
"import.adv.skipMtp": "omitir la cabeza MTP",
"import.adv.o1": "Atención O(1)",
"import.adv.o1Hint": "Atención en streaming con memoria constante: la caché KV se libera tras el prefill, así que los contextos largos no hacen crecer la memoria. Los pesos quedan idénticos byte a byte — solo se guarda una indicación para el momento de carga; puedes volver atrás cuando quieras (servir con --o1 off). Validado en Qwen3-0.6B: todas las capas ≈ ×1,18 de perplejidad.",
"import.adv.o1Off": "desactivada — atención exacta (por defecto)",
"import.adv.o1All": "all — convertir todas las capas softmax",
"import.adv.o1Deep": "deep N — solo las N capas más profundas",
"import.adv.o1Custom": "personalizado — lista explícita de capas",
"import.adv.o1DeepN": "N capas profundas",
"import.adv.o1Layers": "Capas (separadas por comas)",
"import.adv.o1mHint": "presupuesto de landmarks (32 por defecto)",
"import.adv.o1windowHint": "ancho de la ventana exacta (128 por defecto)",
"import.adv.o1sinkHint": "claves sink exactas permanentes (4 por defecto)",
// onboarding
"onboarding.title": "Configuración inicial",
"onboarding.welcome": "Bienvenido a Cortiq Gateway",
"onboarding.welcomeSub": "Elige tu idioma — puedes cambiarlo cuando quieras en la cabecera.",
"onboarding.next": "Siguiente",
"onboarding.back": "Atrás",
"onboarding.skip": "Omitir por ahora",
"onboarding.skipSetup": "Omitir",
"onboarding.finish": "Finalizar configuración",
"onboarding.featuredTitle": "Modelos listos para usar (un clic)",
"onboarding.featuredSub": "Son modelos .cmf ya convertidos de infosave en HuggingFace — basta con descargarlos, sin conversión. Elige uno o varios para empezar al instante.",
"onboarding.download": "Descargar",
"onboarding.hfTitle": "Cualquier modelo de HuggingFace — conversión automática",
"onboarding.hfSub": "Busca cualquier modelo, elige una cuantización y lo convertimos a .cmf automáticamente. Sin editar la configuración a mano.",
"onboarding.noJobs": "Todavía no hay descargas.",
"onboarding.doneTitle": "Todo listo",
"onboarding.doneSub": "Los modelos descargados se registrarán automáticamente. Puedes añadir más en Importar o Modelos.",
"onboarding.doneHint": "«Finalizar configuración» conecta cada descarga completada como modelo local, arranca su servidor .cmf y apunta el enrutado hacia él. Todo esto se puede cambiar luego en Modelos.",
"onboarding.willRegister": "Se conectarán como modelos locales",
"onboarding.nothingToRegister": "Aún no hay descargas terminadas — no hay nada que conectar. Vuelve un paso atrás y elige un modelo.",
"onboarding.registeredN": "{n} modelo(s) conectado(s): {ids}",
"onboarding.runtimeMissing": "El runtime cortiq no está instalado — instálalo en Ajustes → Modelos locales para poder servirlos.",
"onboarding.downloaded": "descargado",
"onboarding.update": "Actualizar",
"onboarding.deleteFile": "Eliminar el archivo .cmf del disco",
"onboarding.confirmDelete": "¿Eliminar «{id}»? El archivo .cmf se borrará del disco.",
"onboarding.unregistered": "También se dio de baja: {ids}",
};