Spaces:
Running
Running
File size: 4,084 Bytes
f8d0843 750ca83 f8d0843 8eac3eb fa27f81 8eac3eb 5264fdb f8d0843 8eac3eb f8d0843 8eac3eb f9dd2fe f8d0843 8eac3eb f8d0843 8eac3eb fa27f81 8eac3eb f8d0843 8eac3eb f8d0843 8eac3eb f8d0843 f9dd2fe f8d0843 8eac3eb fa27f81 f9dd2fe fa27f81 8eac3eb f8d0843 8eac3eb 750ca83 8eac3eb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | // Shared engine + model picker with cache controls for the in-browser panels.
// Lets you pick an ENGINE (wllama / Transformers.js / WebLLM) to benchmark which is
// fastest, then a model from that engine's catalog (showing size if known + whether
// it's already downloaded), and delete a downloaded model from the browser cache
// (wllama only) — like the wllama demo space.
import {
listEngines, getEngineId, setEngine,
listModels, currentModel, setModel,
cacheSupported, cachedSet, deleteCached, backendLabel, onModelChange,
} from '/web/runtime.js'
import { fmtBytes } from '/web/modelCatalog.js'
import { storageEstimate } from '/web/storage.js'
function el(tag, props = {}, kids = []) {
const n = document.createElement(tag)
for (const [k, v] of Object.entries(props)) {
if (k === 'class') n.className = v
else if (k.startsWith('on') && typeof v === 'function') n.addEventListener(k.slice(2), v)
else if (v != null) n.setAttribute(k, v)
}
for (const kid of [].concat(kids)) if (kid != null) n.append(kid)
return n
}
// Byte size only (params is already shown in the label/info) — avoids "0.6B · 0.6B".
const sizeOf = (m) => (m && m.bytes ? fmtBytes(m.bytes) : '')
export function mountModelBar(host, { onChange } = {}) {
const engSel = el('select', { class: 'model-select engine-select' })
const sel = el('select', { class: 'model-select' })
const del = el('button', { class: 'model-del', type: 'button', title: 'Delete this model from your browser cache' }, '🗑 delete')
const info = el('div', { class: 'model-info' })
host.append(el('div', { class: 'model-bar' }, [
el('label', { class: 'persona-label' }, 'Runtime'),
engSel,
el('label', { class: 'persona-label' }, 'Model'),
sel, el('div', { class: 'model-row' }, [info, del]),
]))
// Engine options are fixed; unavailable ones (e.g. WebLLM with no WebGPU) are disabled.
engSel.replaceChildren(...listEngines().map((e) =>
el('option', { value: e.id, ...(e.available ? {} : { disabled: 'disabled' }) },
`${e.label}${e.available ? '' : ' · needs WebGPU'}`)))
engSel.value = getEngineId()
let cached = new Set()
let storeNote = ''
function render() {
const models = listModels()
const cur = currentModel().id
sel.replaceChildren(...models.map((m) =>
el('option', { value: m.id }, `${m.label}${sizeOf(m) ? ` · ${sizeOf(m)}` : ''}${cached.has(m.id) ? ' · ✓ downloaded' : ''}`)))
sel.value = cur
const m = currentModel()
const size = sizeOf(m)
const cacheText = cacheSupported() ? (cached.has(m.id) ? 'cached' : 'downloads on first use') : (m.note || 'no browser download')
info.textContent = `${m.params || ''}${size ? ` · ${size}` : ''} · ${backendLabel()} · ${cacheText}${storeNote}`
del.style.display = (cacheSupported() && cached.has(m.id)) ? '' : 'none'
}
async function refresh() {
cached = cacheSupported() ? await cachedSet() : new Set()
const { usage, quota } = await storageEstimate()
storeNote = cacheSupported() && quota ? ` · cache ${fmtBytes(usage)}/${fmtBytes(quota)}` : ''
render()
}
engSel.addEventListener('change', async () => {
setEngine(engSel.value)
await refresh()
onChange && onChange(sel.value)
})
sel.addEventListener('change', async () => { setModel(sel.value); render(); onChange && onChange(sel.value) })
del.addEventListener('click', async () => {
del.disabled = true; const prev = info.textContent; info.textContent = 'deleting from cache…'
try { await deleteCached(sel.value) } catch (e) { info.textContent = 'delete failed: ' + (e.message || e) }
await refresh(); del.disabled = false
if (info.textContent.startsWith('delete failed')) setTimeout(() => { info.textContent = prev }, 2500)
})
// A preset (or any other surface) may change the engine/model — re-sync this bar.
// render() updates the selects immediately; refresh() then refreshes cache/size info.
onModelChange(() => { engSel.value = getEngineId(); render(); refresh() })
render()
refresh()
return { refresh }
}
|