"""
Best Engine AI Helper — single-page GUI.
This module holds the self-contained HTML document served by the FastAPI app at
``GET /gui`` (see :mod:`best_engine_ai_helper.api`). One file, Tailwind via CDN,
vanilla JavaScript — no build step, no framework, no npm, matching the rest of
the AI Helpers suite (see audio-helper's GUI.md).
The page is offered in French (default) and English. Both are rendered from a
single template plus a per-language strings table by :func:`render_gui`; a
header link switches between them (``/gui`` for French, ``/gui?lang=en`` for
English). The JSON API the page calls is language-neutral, so only the labels
differ. ``GUI_HTML`` is the French render, kept as a module constant for
callers and tests that want the default page directly.
Every user-facing string is authored once, in ``locales/i18n.yaml`` (package
root's ``gui:`` namespace, loaded through :mod:`best_engine_ai_helper.i18n`)
— this module never hardcodes wording. It maps that file's stable semantic
keys onto the template's ``{{TOKEN}}`` placeholders and the JavaScript ``T``
object, and falls back to the ``meta.default_locale`` declared in the YAML
(French) for any language the file does not cover.
Visual language matches the sprezzature-figures gallery
(https://harchaoui.org/warith/sprezzature/figures.html): Roboto / Roboto
Serif / Roboto Mono, the #007aff brand blue, a neutral gray scale, and a
manual light/dark toggle persisted under a ``data-color-scheme`` attribute
(not hotlinked — fonts are self-served via Google Fonts, not the reference
site's assets).
Author
------
Warith Harchaoui <warith.harchaoui@deraison.ai>
"""
from __future__ import annotations
import html
import json
from . import i18n
# ---------------------------------------------------------------------------
# Template-token <-> locale-key maps
# ---------------------------------------------------------------------------
# The markup and inline script below were written once against these short
# token names; renaming every {{TOKEN}} / T.name reference to match the YAML's
# semantic keys would only add churn, so these maps are the single place that
# bridges the two vocabularies. `_HTML_TOKEN_TO_KEY` fills {{TOKEN}} placeholders;
# `_JS_NAME_TO_KEY` builds the `const T = {...}` object the page's script reads.
_HTML_TOKEN_TO_KEY: dict[str, str] = {
"META_DESC": "meta_description",
"SKIP": "skip_to_content",
"NAV_ARIA": "nav_aria",
"GITHUB": "github_link_label",
"THEME_ARIA": "theme_toggle_aria",
"LANG_HREF": "lang_toggle_href",
"LANG_LABEL": "lang_toggle_flag",
"LANG_ARIA": "lang_toggle_aria",
"HERO_TITLE": "hero_title",
"HERO_P": "hero_subtitle",
"SYS_H2": "system_heading",
"REFRESH": "refresh_button",
"DETECTING": "detecting",
"TASK_H2": "task_heading",
"TASK_P": "task_subtitle",
"TASK_LABEL": "task_label",
"TASK_PLACEHOLDER": "task_placeholder",
"RUN_BTN": "run_button",
"HEADROOM": "headroom_label",
"LIVE_LABEL": "live_toggle_label",
"FOOTER_NOTE": "footer_note",
"ACTIVITY_H2": "activity_heading",
"ACTIVITY_P": "activity_subtitle",
"ACTIVITY_LOADING": "activity_loading",
}
_JS_NAME_TO_KEY: dict[str, str] = {
"detecting": "detecting",
"detectFail": "detect_failed",
"fPlatform": "field_platform",
"fVendor": "field_vendor",
"fChip": "field_chip",
"poolUnified": "pool_unified",
"poolVram": "pool_vram",
"poolRam": "pool_ram",
"fBandwidth": "field_bandwidth",
"unknown": "unknown",
"fBudget": "field_budget",
"gb": "unit_gb",
"gbs": "unit_gbs",
"analyzing": "analyzing",
"done": "done",
"recFail": "recommend_failed",
"error": "error_generic",
"yes": "yes",
"no": "no",
"best": "best_label",
"axis": "axis_label",
"overBudget": "over_budget_warning",
"notStructured": "not_structured_warning",
"lighterAlt": "lighter_alternative",
"score": "score_label",
"noCandidate": "no_candidate",
"allCandidates": "all_candidates",
"thStructured": "th_structured",
"thModel": "th_model",
"thRam": "th_ram",
"thScore": "th_score",
"thFits": "th_fits",
"thTps": "th_tps",
"task": "task_prefix",
"language": "language_prefix",
"serverLoad": "server_load_label",
"ramFree": "ram_free_label",
"diskFree": "disk_free_label",
"runningEngines": "running_engines_label",
"activityLoading": "activity_loading",
"activityFail": "activity_failed",
"noActivity": "no_activity",
"unknownCost": "unknown_cost",
"thCalls": "th_calls",
"thCost": "th_cost",
"calls": "calls_label",
"totalCost": "total_cost_label",
"errorRate": "error_rate_label",
"byUser": "by_user_label",
"byModel": "by_model_label",
"genericAssistant": "generic_assistant",
"matched": "matched_keywords",
"needs": "needs_label",
"tps": "unit_tps",
}
# Default language when the query string names none or an unknown one — read
# from the YAML's own declared default so the file stays the single source of
# truth for locale metadata (see references/CODING.md section 21.3.4).
_DEFAULT_LANG: str = str(i18n.meta()["default_locale"])
_SUPPORTED_LANGS: set[str] = set(i18n.meta()["supported_locales"])
def _strings_for(lang: str) -> dict[str, dict[str, str]]:
"""Build the ``{"html": ..., "js": ...}`` label tables for one language.
Parameters
----------
lang : str
Requested language code.
Returns
-------
dict
``html`` maps template tokens to text; ``js`` maps the names the
inline script reads off ``T``. A key missing for ``lang`` falls back
to ``_DEFAULT_LANG``'s value, then to the bare key itself — the file
should never omit an entry, but a page must never 500 over copy.
"""
gui = i18n.gui_strings()
def pick(key: str) -> str:
entry = gui.get(key, {})
return entry.get(lang) or entry.get(_DEFAULT_LANG) or key
html_strings = {"LANG": lang, **{tok: pick(k) for tok, k in _HTML_TOKEN_TO_KEY.items()}}
js_strings = {name: pick(k) for name, k in _JS_NAME_TO_KEY.items()}
return {"html": html_strings, "js": js_strings}
# The document template. Human-facing text is tokenised as {{TOKEN}} (HTML) or
# read from the injected `T` object (JavaScript), so this markup is shared by
# every language.
_GUI_TEMPLATE: str = r"""<!doctype html>
<html lang="{{LANG}}" data-color-scheme="light">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Best Engine AI Helper</title>
<meta name="description" content="{{META_DESC}}" />
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)" />
<meta name="theme-color" content="#0B0B0C" media="(prefers-color-scheme: dark)" />
<link rel="icon" href="/static/icons/favicon.ico" sizes="any" />
<link rel="icon" type="image/png" sizes="32x32" href="/static/icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/static/icons/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/static/icons/apple-touch-icon.png" />
<link rel="manifest" href="/static/site.webmanifest" />
<!-- Same type family as sprezzature-figures: Roboto / Roboto Serif / Roboto Mono. -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;600;700&family=Roboto+Serif:wght@600;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet" />
<!-- Resolve light/dark before first paint (no flash), same scheme as the
reference site but under this app's own storage key. -->
<script>(function(){var s;try{s=localStorage.getItem('best-engine-theme');}catch(e){}
if(!s)s=window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';
document.documentElement.setAttribute('data-color-scheme',s);})();</script>
<!-- Tailwind via CDN: keeps the page a single self-contained file, no build. -->
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: ["selector", '[data-color-scheme="dark"]'],
theme: {
extend: {
fontFamily: {
sans: ["Roboto", "system-ui", "sans-serif"],
serif: ["Roboto Serif", "serif"],
mono: ["Roboto Mono", "ui-monospace", "monospace"],
},
colors: {
brand: {
blue: "#007aff",
bluedark: "#0a84ff",
bluelight: "#cce4ff",
navy: "#0a4da0",
},
},
},
},
};
</script>
<style>
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }
</style>
</head>
<body class="bg-white text-neutral-900 antialiased dark:bg-[#0B0B0C] dark:text-neutral-100 font-sans">
<a href="#main" class="sr-only focus:not-sr-only focus:absolute focus:z-50 focus:top-3 focus:left-3 focus:rounded-lg focus:bg-brand-blue focus:px-4 focus:py-2 focus:text-white">{{SKIP}}</a>
<header class="sticky top-0 z-40 border-b border-neutral-200/70 bg-white/80 backdrop-blur dark:border-neutral-800 dark:bg-[#0B0B0C]/80">
<nav class="mx-auto flex max-w-4xl items-center justify-between px-5 py-3" aria-label="{{NAV_ARIA}}">
<span class="flex items-center gap-2 font-semibold tracking-tight">
<img src="/static/icons/android-chrome-192x192.png" alt="" width="28" height="28" class="rounded-lg" />
Best Engine AI Helper
</span>
<div class="flex items-center gap-3 text-sm">
<a href="https://github.com/warith-harchaoui/best-engine-ai-helper"
class="hidden rounded px-1 hover:text-brand-blue focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue sm:inline">
{{GITHUB}}
</a>
<a href="{{LANG_HREF}}" aria-label="{{LANG_ARIA}}"
class="rounded px-1.5 py-1 font-mono text-xs font-semibold hover:text-brand-blue focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue">
{{LANG_LABEL}}
</a>
<button id="theme-toggle" type="button" aria-label="{{THEME_ARIA}}"
class="rounded-full px-2 py-1 text-lg leading-none hover:bg-neutral-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue dark:hover:bg-neutral-800">
🌞
</button>
</div>
</nav>
</header>
<main id="main">
<section class="mx-auto max-w-4xl px-5 py-14">
<p class="font-mono text-sm text-brand-blue">best-engine-ai-helper</p>
<h1 class="mt-1 font-serif text-4xl font-bold tracking-tight sm:text-5xl">{{HERO_TITLE}}</h1>
<p class="mt-3 max-w-2xl text-base text-neutral-600 dark:text-neutral-300">
{{HERO_P}}
</p>
</section>
<!-- 1) Hardware snapshot -->
<section class="border-t border-neutral-200/70 dark:border-neutral-800">
<div class="mx-auto max-w-4xl px-5 py-10">
<div class="mb-4 flex items-center justify-between">
<h2 class="text-2xl font-bold tracking-tight">{{SYS_H2}}</h2>
<button id="refresh-system"
class="rounded-lg border border-neutral-300 px-3 py-1.5 text-sm font-medium
hover:border-brand-blue focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue
dark:border-neutral-600">
{{REFRESH}}
</button>
</div>
<div id="system-grid"
class="grid grid-cols-2 gap-4 rounded-xl border border-neutral-200 bg-white p-5 text-sm shadow-sm
sm:grid-cols-3 dark:border-neutral-800 dark:bg-neutral-950">
<p class="col-span-full text-neutral-500 dark:text-neutral-400">{{DETECTING}}</p>
</div>
</div>
</section>
<!-- 2) Task description -->
<section class="border-t border-neutral-200/70 dark:border-neutral-800">
<div class="mx-auto max-w-4xl px-5 py-10">
<h2 class="text-2xl font-bold tracking-tight">{{TASK_H2}}</h2>
<p class="mt-2 max-w-3xl text-neutral-600 dark:text-neutral-300">
{{TASK_P}}
</p>
<div class="mt-6 rounded-xl border border-neutral-200 bg-white p-5 shadow-sm dark:border-neutral-800 dark:bg-neutral-950">
<label for="task" class="sr-only">{{TASK_LABEL}}</label>
<textarea id="task" rows="3" placeholder="{{TASK_PLACEHOLDER}}"
class="w-full rounded-lg border border-neutral-300 bg-white px-3 py-2 text-sm text-neutral-900
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue
dark:border-neutral-600 dark:bg-neutral-900 dark:text-neutral-100"></textarea>
<div class="mt-4 flex flex-wrap items-center gap-3">
<button id="run-recommend"
class="rounded-lg bg-brand-blue px-4 py-2 text-sm font-semibold text-white
hover:bg-brand-bluedark focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue
disabled:opacity-50">
{{RUN_BTN}}
</button>
<label class="flex items-center gap-2 rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-1.5 text-xs text-neutral-600
dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-400">
{{HEADROOM}}
<input id="headroom" type="number" min="0.5" max="1" step="0.05" value="0.85"
class="w-16 rounded border border-neutral-300 bg-white px-2 py-1 text-xs text-neutral-900
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue
dark:border-neutral-600 dark:bg-neutral-950 dark:text-neutral-100" />
</label>
<label class="flex items-center gap-2 rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-1.5 text-xs text-neutral-600
dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-400">
<input id="live" type="checkbox"
class="rounded border-neutral-300 text-brand-blue focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue
dark:border-neutral-600" />
{{LIVE_LABEL}}
</label>
<span id="status" class="text-xs text-neutral-500 dark:text-neutral-400" role="status" aria-live="polite"></span>
</div>
</div>
</div>
</section>
<!-- 3) Results -->
<section id="results-section" class="border-t border-neutral-200/70 dark:border-neutral-800">
<div class="mx-auto max-w-4xl px-5 py-10">
<div id="results" class="space-y-4"></div>
</div>
</section>
<!-- 4) Activity ledger -->
<section class="border-t border-neutral-200/70 dark:border-neutral-800">
<div class="mx-auto max-w-4xl px-5 py-10">
<div class="mb-4 flex items-center justify-between">
<h2 class="text-2xl font-bold tracking-tight">{{ACTIVITY_H2}}</h2>
<button id="refresh-activity"
class="rounded-lg border border-neutral-300 px-3 py-1.5 text-sm font-medium
hover:border-brand-blue focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue
dark:border-neutral-600">
{{REFRESH}}
</button>
</div>
<p class="mb-4 max-w-2xl text-sm text-neutral-600 dark:text-neutral-300">{{ACTIVITY_P}}</p>
<div id="activity" class="rounded-xl border border-neutral-200 bg-white p-5 text-sm shadow-sm dark:border-neutral-800 dark:bg-neutral-950">
<p class="text-neutral-500 dark:text-neutral-400">{{ACTIVITY_LOADING}}</p>
</div>
</div>
</section>
</main>
<footer class="border-t border-neutral-200/70 py-10 dark:border-neutral-800">
<div class="mx-auto max-w-4xl px-5 text-sm text-neutral-500 dark:text-neutral-400">
<p>
<a class="underline hover:text-brand-blue" href="https://github.com/warith-harchaoui/best-engine-ai-helper">best-engine-ai-helper</a>
· {{FOOTER_NOTE}} ·
<a class="underline hover:text-brand-blue" href="https://www.linkedin.com/in/warith-harchaoui/">Warith Harchaoui</a>
</p>
</div>
</footer>
<script type="module">
// All user-facing strings for this render, injected from the server.
const T = {{JS_T}};
const $ = (id) => document.getElementById(id);
const status = (msg) => { $("status").textContent = msg; };
const fmt1 = (v) => (v === null || v === undefined ? "?" : Number(v).toFixed(1));
const fmtTps = (v) => (v ? Math.round(v) : "-");
// --- Theme toggle ------------------------------------------------------
const themeBtn = $("theme-toggle");
function currentTheme() {
return document.documentElement.getAttribute("data-color-scheme") === "dark" ? "dark" : "light";
}
function applyThemeIcon() {
themeBtn.textContent = currentTheme() === "dark" ? "🌙" : "🌞";
}
themeBtn.addEventListener("click", () => {
const next = currentTheme() === "dark" ? "light" : "dark";
document.documentElement.setAttribute("data-color-scheme", next);
try { localStorage.setItem("best-engine-theme", next); } catch (e) {}
applyThemeIcon();
});
applyThemeIcon();
// --- 1) Hardware snapshot -------------------------------------------
async function loadSystem() {
$("system-grid").innerHTML =
`<p class="col-span-full text-neutral-500 dark:text-neutral-400">${T.detecting}</p>`;
try {
const res = await fetch("/api/system");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const info = await res.json();
renderSystem(info);
} catch (err) {
$("system-grid").innerHTML =
`<p class="col-span-full text-red-600 dark:text-red-400">${T.detectFail}${err}</p>`;
}
}
function field(label, value) {
return `
<div>
<dt class="text-xs text-neutral-500 dark:text-neutral-400">${label}</dt>
<dd class="font-medium">${value}</dd>
</div>`;
}
function renderSystem(info) {
const mem = info.memory || {};
const comp = info.compute || {};
const pool = mem.unified_gb ?? mem.vram_gb ?? mem.ram_gb;
const poolLabel = mem.unified_gb != null ? T.poolUnified
: mem.vram_gb != null ? T.poolVram : T.poolRam;
const cells = [
field(T.fPlatform, info.platform ?? "?"),
field(T.fVendor, info.chip_vendor ?? "?"),
field(T.fChip, `${comp.chip ?? "?"} (${comp.accelerator ?? "?"})`),
field(poolLabel, `${fmt1(pool)} ${T.gb}`),
field(T.fBandwidth, comp.bandwidth_gbs ? `${Math.round(comp.bandwidth_gbs)} ${T.gbs}` : T.unknown),
field(T.fBudget, `${fmt1(info.memory_budget_gb)} ${T.gb}`),
];
$("system-grid").innerHTML = cells.join("");
}
$("refresh-system").addEventListener("click", loadSystem);
loadSystem();
// --- 4) Activity ledger ------------------------------------------------
async function loadActivity() {
$("activity").innerHTML =
`<p class="text-neutral-500 dark:text-neutral-400">${T.activityLoading}</p>`;
try {
const res = await fetch("/api/activity");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
renderActivity(await res.json());
} catch (err) {
$("activity").innerHTML =
`<p class="text-red-600 dark:text-red-400">${T.activityFail}${err}</p>`;
}
}
function activityRow(row, keyName) {
const cost = row.cost_usd != null ? `$${row.cost_usd.toFixed(4)}` : T.unknownCost;
return `
<tr class="border-t border-neutral-200 dark:border-neutral-800">
<td class="py-1.5 pr-3 font-mono">${row[keyName]}</td>
<td class="py-1.5 pr-3">${row.calls}</td>
<td class="py-1.5">${cost}</td>
</tr>`;
}
function activityTable(title, rows, keyName) {
if (!rows.length) return "";
return `
<div class="mt-4">
<h3 class="mb-1 text-xs font-semibold uppercase tracking-wide text-neutral-500 dark:text-neutral-400">${title}</h3>
<table class="w-full text-left text-xs">
<thead><tr class="text-neutral-500 dark:text-neutral-400">
<th class="pb-1 pr-3 font-medium">${keyName}</th>
<th class="pb-1 pr-3 font-medium">${T.thCalls}</th>
<th class="pb-1 font-medium">${T.thCost}</th>
</tr></thead>
<tbody>${rows.map((r) => activityRow(r, keyName)).join("")}</tbody>
</table>
</div>`;
}
function renderActivity(data) {
if (!data.total_calls) {
$("activity").innerHTML =
`<p class="text-neutral-500 dark:text-neutral-400">${T.noActivity}</p>`;
return;
}
const cost = data.total_cost_usd != null ? `$${data.total_cost_usd.toFixed(4)}` : T.unknownCost;
const parts = [`
<p>
<span class="font-semibold">${data.total_calls}</span> ${T.calls} ·
${T.totalCost} <span class="font-semibold">${cost}</span> ·
${T.errorRate} <span class="font-semibold">${(data.error_rate * 100).toFixed(1)}%</span>
</p>`];
parts.push(activityTable(T.byUser, data.by_user, "user"));
parts.push(activityTable(T.byModel, data.by_model, "model"));
$("activity").innerHTML = parts.join("");
}
$("refresh-activity").addEventListener("click", loadActivity);
loadActivity();
// --- 2 & 3) Recommendation -------------------------------------------
$("run-recommend").addEventListener("click", runRecommend);
$("task").addEventListener("keydown", (e) => {
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) runRecommend();
});
// Deep link: /gui?task=... pre-fills the box and runs it on load, so a
// recommendation is shareable by URL. ?lang is handled server-side.
const urlTask = new URLSearchParams(location.search).get("task");
if (urlTask) {
$("task").value = urlTask;
runRecommend();
}
async function runRecommend() {
const task = $("task").value.trim() || null;
const headroom = Number($("headroom").value) || 0.85;
const live = $("live").checked;
$("run-recommend").disabled = true;
status(T.analyzing);
$("results").innerHTML = "";
try {
const res = await fetch("/api/recommend", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ task, headroom, live }),
});
if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
const report = await res.json();
renderResults(report);
status(T.done);
} catch (err) {
$("results").innerHTML =
`<p class="text-sm text-red-600 dark:text-red-400">${T.recFail}${err}</p>`;
status(T.error);
} finally {
$("run-recommend").disabled = false;
}
}
function candidateRow(r) {
return `
<tr class="border-t border-neutral-200 dark:border-neutral-800">
<td class="py-1.5 pr-3 font-mono">${r.id}</td>
<td class="py-1.5 pr-3">${fmt1(r.ram_gb)}</td>
<td class="py-1.5 pr-3">${Math.round(r.score)}</td>
<td class="py-1.5 pr-3">${r.fits ? T.yes : T.no}</td>
<td class="py-1.5 pr-3">${r.structured_output === false ? T.no : T.yes}</td>
<td class="py-1.5">${fmtTps(r.est_tokens_per_s)}</td>
</tr>`;
}
function pill(text) {
return `<span class="inline-flex items-center rounded-full border border-brand-blue/30 bg-brand-bluelight/30
px-3 py-1 font-mono text-sm font-semibold text-brand-navy
dark:border-brand-blue/40 dark:bg-brand-navy/20 dark:text-blue-300">${text}</span>`;
}
function kindCard(kind, block) {
const chosen = block.chosen;
const alt = block.lighter_alternative;
const chosenHtml = chosen ? `
<p class="flex flex-wrap items-center gap-2 text-sm">
${pill(chosen.id)}
<span class="text-neutral-600 dark:text-neutral-300">
${fmt1(chosen.ram_gb)} ${T.gb}, ${T.score} ${Math.round(chosen.score)}, ~${fmtTps(chosen.est_tokens_per_s)} ${T.tps}
</span>
${chosen.fits ? "" : `<span class="text-red-600 dark:text-red-400">${T.overBudget}</span>`}
${chosen.structured_output === false ? `<span class="text-amber-600 dark:text-amber-400">${T.notStructured}</span>` : ""}
</p>
${alt ? `<p class="mt-2 text-xs text-neutral-500 dark:text-neutral-400">${T.lighterAlt} <span class="font-mono">${alt.id}</span> — ${fmt1(alt.ram_gb)} ${T.gb}, ${T.score} ${Math.round(alt.score)}, ~${fmtTps(alt.est_tokens_per_s)} ${T.tps}.</p>` : ""}
` : `<p class="text-sm text-neutral-500 dark:text-neutral-400">${T.noCandidate}</p>`;
return `
<div class="overflow-hidden rounded-xl border border-neutral-200 bg-white p-5 shadow-sm dark:border-neutral-800 dark:bg-neutral-950">
<h3 class="mb-3 text-lg font-bold tracking-tight">
${T.best} ${kind.toUpperCase()} <span class="font-mono text-sm font-normal text-neutral-500 dark:text-neutral-400">(${T.axis} ${block.axis})</span>
</h3>
${chosenHtml}
<details class="mt-4">
<summary class="cursor-pointer text-xs font-medium text-neutral-500 hover:text-brand-blue dark:text-neutral-400">
${T.allCandidates} (${block.candidates.length})
</summary>
<div class="mt-2 overflow-x-auto">
<table class="w-full text-left text-xs">
<thead>
<tr class="text-neutral-500 dark:text-neutral-400">
<th class="pb-1.5 pr-3 font-medium">${T.thModel}</th>
<th class="pb-1.5 pr-3 font-medium">${T.thRam}</th>
<th class="pb-1.5 pr-3 font-medium">${T.thScore}</th>
<th class="pb-1.5 pr-3 font-medium">${T.thFits}</th>
<th class="pb-1.5 pr-3 font-medium">${T.thStructured}</th>
<th class="pb-1.5 font-medium">${T.thTps}</th>
</tr>
</thead>
<tbody>${block.candidates.map(candidateRow).join("")}</tbody>
</table>
</div>
</details>
</div>`;
}
function renderResults(report) {
const task = report.task || {};
const parts = [];
parts.push(`
<div class="rounded-xl border border-neutral-200 bg-neutral-50 p-4 text-sm dark:border-neutral-800 dark:bg-neutral-900">
<p><span class="text-neutral-500 dark:text-neutral-400">${T.task}</span> ${task.input ? task.input : T.genericAssistant}</p>
${task.language ? `<p class="mt-1 text-xs text-neutral-500 dark:text-neutral-400">${T.language} ${task.language}</p>` : ""}
${task.matched && task.matched.length ? `<p class="mt-1 text-xs text-neutral-500 dark:text-neutral-400">${T.matched} ${task.matched.join(", ")}</p>` : ""}
<p class="mt-1 text-xs text-neutral-500 dark:text-neutral-400">${T.needs} ${task.kinds.map((k) => k.toUpperCase()).join(", ")}</p>
</div>`);
const load = report.server_load;
if (load) {
const gpuPart = load.gpu_percent != null ? `, GPU ${Math.round(load.gpu_percent)}%` : "";
parts.push(`
<div class="mt-3 rounded-xl border border-neutral-200 bg-neutral-50 p-4 text-xs text-neutral-600 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-400">
<span class="font-medium text-neutral-700 dark:text-neutral-300">${T.serverLoad}</span>
${fmt1(load.available_ram_gb)} ${T.gb} ${T.ramFree}, CPU ${Math.round(load.cpu_percent)}%${gpuPart},
${T.diskFree} ${fmt1(load.disk_free_gb)} ${T.gb} (${Math.round(load.disk_percent_used)}%),
${T.runningEngines} ${load.running_engines}
</div>`);
}
const kindBlocks = Object.entries(report.recommendations || {});
const gridClass = kindBlocks.length > 1 ? "mt-4 grid gap-4 sm:grid-cols-2" : "mt-4 grid gap-4";
parts.push(`<div class="${gridClass}">${kindBlocks.map(([kind, block]) => kindCard(kind, block)).join("")}</div>`);
$("results").innerHTML = parts.join("");
}
</script>
</body>
</html>
"""
[docs]
def render_gui(lang: str = _DEFAULT_LANG) -> str:
"""
Render the single-page GUI in the requested language.
Parameters
----------
lang : str
Two-letter language code. Any value outside ``locales/i18n.yaml``'s
``meta.supported_locales`` (``"fr"``, ``"en"``) falls back to
``meta.default_locale`` so an unknown ``?lang=`` never 500s.
Returns
-------
str
The complete HTML document for that language.
"""
strings = _strings_for(lang if lang in _SUPPORTED_LANGS else _DEFAULT_LANG)
page = _GUI_TEMPLATE
for token, value in strings["html"].items():
# Escape every value: these labels land in attributes (placeholder, aria,
# href) and text nodes alike, and some carry quotes (the EN placeholder)
# that would otherwise break out of the attribute they sit in.
page = page.replace("{{" + token + "}}", html.escape(value, quote=True))
# Emit the JS strings as a JSON literal so quotes/accents are escaped safely.
page = page.replace("{{JS_T}}", json.dumps(strings["js"], ensure_ascii=False))
return page
# French render, kept as a module constant for the default page and for callers
# and tests that want the markup without going through render_gui().
GUI_HTML: str = render_gui("fr")