Spaces:
Running
Running
Add Build Small recommended preset
Browse files- web/imagen.js +2 -2
- web/qualityBar.js +2 -2
- web/qualityPreset.js +24 -21
- web/runtime.js +1 -1
- web/tts.js +2 -2
web/imagen.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
// Image facade — mirrors tts.js. Picks the active portrait engine (local Z-Image on your
|
| 2 |
// GPU, or cloud FLUX; in-browser SD-Turbo / Janus get added here later) and exposes one
|
| 3 |
// generatePortrait(). The persona panel + the Settings image bar import only from here.
|
| 4 |
-
import { engineLocal as zimagelocal, engineKleinZeroGpu as klein, engineCloud as flux, engineCloudDev as fluxdev
|
| 5 |
import { engine as bonsai } from '/web/imagenBonsai.js'
|
| 6 |
|
| 7 |
const ENGINES = [zimagelocal, klein, bonsai, flux, fluxdev]
|
|
@@ -12,7 +12,7 @@ let activeId = (() => {
|
|
| 12 |
let saved = ''
|
| 13 |
try { saved = localStorage.getItem(KEY) || '' } catch { /* ignore */ }
|
| 14 |
const e = ENGINES.find((x) => x.id === saved)
|
| 15 |
-
return e && e.available() ? saved :
|
| 16 |
})()
|
| 17 |
|
| 18 |
const eng = () => ENGINES.find((e) => e.id === activeId) || ENGINES.find((e) => e.available()) || ENGINES[0]
|
|
|
|
| 1 |
// Image facade — mirrors tts.js. Picks the active portrait engine (local Z-Image on your
|
| 2 |
// GPU, or cloud FLUX; in-browser SD-Turbo / Janus get added here later) and exposes one
|
| 3 |
// generatePortrait(). The persona panel + the Settings image bar import only from here.
|
| 4 |
+
import { engineLocal as zimagelocal, engineKleinZeroGpu as klein, engineCloud as flux, engineCloudDev as fluxdev } from '/web/imagenServer.js'
|
| 5 |
import { engine as bonsai } from '/web/imagenBonsai.js'
|
| 6 |
|
| 7 |
const ENGINES = [zimagelocal, klein, bonsai, flux, fluxdev]
|
|
|
|
| 12 |
let saved = ''
|
| 13 |
try { saved = localStorage.getItem(KEY) || '' } catch { /* ignore */ }
|
| 14 |
const e = ENGINES.find((x) => x.id === saved)
|
| 15 |
+
return e && e.available() ? saved : 'klein-zerogpu'
|
| 16 |
})()
|
| 17 |
|
| 18 |
const eng = () => ENGINES.find((e) => e.id === activeId) || ENGINES.find((e) => e.available()) || ENGINES[0]
|
web/qualityBar.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
// "Recommended settings" — a High/Medium/Low/Custom preset picker (sets the LLM model +
|
| 2 |
// voice provider together) plus an expandable "Your device" readout for debugging and
|
| 3 |
// benchmarking. Mounted at the TOP of the Settings page, above Local AI Model.
|
| 4 |
-
import { PRESETS, applyPreset, getActivePreset, onPresetChange } from '/web/qualityPreset.js'
|
| 5 |
import { gatherDeviceInfo, recommendPreset } from '/web/deviceInfo.js'
|
| 6 |
|
| 7 |
const presetLabel = (id) => (PRESETS.find((p) => p.id === id) || {}).label || id
|
|
@@ -19,7 +19,7 @@ function el(tag, props = {}, kids = []) {
|
|
| 19 |
|
| 20 |
export function mountQualityBar(host) {
|
| 21 |
// Preset buttons (+ a derived "Custom" that only highlights, never applies).
|
| 22 |
-
const OPTS = [...PRESETS, { id: 'custom', label: 'Custom', sub: 'Your own model + voice' }]
|
| 23 |
const btns = {}
|
| 24 |
const row = el('div', { class: 'tac-preset-row' }, OPTS.map((p) => {
|
| 25 |
const b = el('button', { class: 'tac-preset-btn', type: 'button', 'data-id': p.id },
|
|
|
|
| 1 |
// "Recommended settings" — a High/Medium/Low/Custom preset picker (sets the LLM model +
|
| 2 |
// voice provider together) plus an expandable "Your device" readout for debugging and
|
| 3 |
// benchmarking. Mounted at the TOP of the Settings page, above Local AI Model.
|
| 4 |
+
import { PRESETS, applyPreset, getActivePreset, onPresetChange, detectPreset } from '/web/qualityPreset.js'
|
| 5 |
import { gatherDeviceInfo, recommendPreset } from '/web/deviceInfo.js'
|
| 6 |
|
| 7 |
const presetLabel = (id) => (PRESETS.find((p) => p.id === id) || {}).label || id
|
|
|
|
| 19 |
|
| 20 |
export function mountQualityBar(host) {
|
| 21 |
// Preset buttons (+ a derived "Custom" that only highlights, never applies).
|
| 22 |
+
const OPTS = [...PRESETS, { id: 'custom', label: 'Custom', sub: 'Your own model + voice + portrait' }]
|
| 23 |
const btns = {}
|
| 24 |
const row = el('div', { class: 'tac-preset-row' }, OPTS.map((p) => {
|
| 25 |
const b = el('button', { class: 'tac-preset-btn', type: 'button', 'data-id': p.id },
|
web/qualityPreset.js
CHANGED
|
@@ -1,40 +1,40 @@
|
|
| 1 |
-
//
|
| 2 |
-
//
|
| 3 |
-
|
| 4 |
-
// happens to equal another preset — like a game's graphics dropdown). The choice is
|
| 5 |
-
// persisted (and the model/voice themselves are persisted by runtime.js / tts.js), so it
|
| 6 |
-
// survives a refresh.
|
| 7 |
-
import { setModel, currentModelId, listModels, onModelChange } from '/web/runtime.js'
|
| 8 |
import { setTtsEngine, getTtsEngineId, onTtsEngineChange } from '/web/tts.js'
|
|
|
|
| 9 |
|
| 10 |
-
// model = preferred exact id; family = prefix fallback if the active engine lacks it
|
| 11 |
-
// (e.g. Transformers.js has no Qwen3 — then High can't be honored and reads as Custom).
|
| 12 |
export const PRESETS = [
|
| 13 |
-
{ id: '
|
| 14 |
-
{ id: '
|
| 15 |
-
{ id: '
|
|
|
|
| 16 |
]
|
| 17 |
const KEY = 'tinyarmy.qualityPreset'
|
| 18 |
const VALID = new Set([...PRESETS.map((p) => p.id), 'custom'])
|
| 19 |
|
| 20 |
const byId = (id) => PRESETS.find((p) => p.id === id)
|
| 21 |
-
|
| 22 |
function resolveModel(p) {
|
| 23 |
const ms = listModels()
|
| 24 |
const m = ms.find((x) => x.id === p.model) || ms.find((x) => x.id.startsWith(p.family))
|
| 25 |
return m ? m.id : ''
|
| 26 |
}
|
| 27 |
|
| 28 |
-
// Which preset does the current (model, voice) match? 'custom' if none.
|
| 29 |
export function detectPreset() {
|
| 30 |
-
const
|
| 31 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
return p ? p.id : 'custom'
|
| 33 |
}
|
| 34 |
|
| 35 |
let _applying = false
|
| 36 |
-
// The active preset id. Load the saved one, but only trust a saved preset id if the
|
| 37 |
-
// persisted model+voice still match it; honor a saved 'custom' as-is.
|
| 38 |
let _current = (() => {
|
| 39 |
let saved = ''
|
| 40 |
try { saved = localStorage.getItem(KEY) || '' } catch { /* ignore */ }
|
|
@@ -55,16 +55,19 @@ export function applyPreset(id) {
|
|
| 55 |
const p = byId(id)
|
| 56 |
if (!p) return
|
| 57 |
_applying = true
|
|
|
|
| 58 |
const mid = resolveModel(p)
|
| 59 |
-
if (mid) setModel(mid)
|
| 60 |
-
setTtsEngine(p.voice)
|
|
|
|
| 61 |
_applying = false
|
| 62 |
commit(id)
|
| 63 |
}
|
| 64 |
|
| 65 |
const _listeners = new Set()
|
| 66 |
export function onPresetChange(fn) { _listeners.add(fn); return () => _listeners.delete(fn) }
|
| 67 |
-
|
| 68 |
const _onManual = () => { if (!_applying) commit('custom') }
|
| 69 |
onModelChange(_onManual)
|
| 70 |
onTtsEngineChange(_onManual)
|
|
|
|
|
|
| 1 |
+
// Recommended settings presets. Each preset can set the text engine/model, voice
|
| 2 |
+
// provider, and portrait model together. Manual changes move the picker to Custom.
|
| 3 |
+
import { setEngine, getEngineId, setModel, currentModelId, listModels, onModelChange } from '/web/runtime.js'
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
import { setTtsEngine, getTtsEngineId, onTtsEngineChange } from '/web/tts.js'
|
| 5 |
+
import { setImageEngine, getImageEngineId, onImageEngineChange } from '/web/imagen.js'
|
| 6 |
|
|
|
|
|
|
|
| 7 |
export const PRESETS = [
|
| 8 |
+
{ id: 'build-small', label: 'BUILD SMALL', engine: 'server', model: 'tiny-aya-global-zerogpu', family: 'tiny-aya', voice: 'voxcpm', image: 'klein-zerogpu', sub: 'Tiny Aya - VoxCPM2 - FLUX.2 klein' },
|
| 9 |
+
{ id: 'high', label: 'High', engine: 'webllm', model: 'qwen3-0.6b', family: 'qwen3', voice: 'kokoro', image: '', sub: 'Qwen3 - Kokoro 82M' },
|
| 10 |
+
{ id: 'medium', label: 'Medium', engine: 'transformers', model: 'qwen2.5-0.5b', family: 'qwen2.5', voice: 'kitten', image: '', sub: 'Qwen2.5 - Kitten TTS' },
|
| 11 |
+
{ id: 'low', label: 'Low', engine: 'transformers', model: 'qwen2.5-0.5b', family: 'qwen2.5', voice: 'webspeech', image: '', sub: 'Qwen2.5 - Web Speech' },
|
| 12 |
]
|
| 13 |
const KEY = 'tinyarmy.qualityPreset'
|
| 14 |
const VALID = new Set([...PRESETS.map((p) => p.id), 'custom'])
|
| 15 |
|
| 16 |
const byId = (id) => PRESETS.find((p) => p.id === id)
|
| 17 |
+
|
| 18 |
function resolveModel(p) {
|
| 19 |
const ms = listModels()
|
| 20 |
const m = ms.find((x) => x.id === p.model) || ms.find((x) => x.id.startsWith(p.family))
|
| 21 |
return m ? m.id : ''
|
| 22 |
}
|
| 23 |
|
|
|
|
| 24 |
export function detectPreset() {
|
| 25 |
+
const eid = getEngineId()
|
| 26 |
+
const mid = currentModelId()
|
| 27 |
+
const voice = getTtsEngineId()
|
| 28 |
+
const image = getImageEngineId()
|
| 29 |
+
const p = PRESETS.find((x) =>
|
| 30 |
+
(!x.engine || eid === x.engine) &&
|
| 31 |
+
(mid === x.model || mid.startsWith(x.family)) &&
|
| 32 |
+
voice === x.voice &&
|
| 33 |
+
(!x.image || image === x.image))
|
| 34 |
return p ? p.id : 'custom'
|
| 35 |
}
|
| 36 |
|
| 37 |
let _applying = false
|
|
|
|
|
|
|
| 38 |
let _current = (() => {
|
| 39 |
let saved = ''
|
| 40 |
try { saved = localStorage.getItem(KEY) || '' } catch { /* ignore */ }
|
|
|
|
| 55 |
const p = byId(id)
|
| 56 |
if (!p) return
|
| 57 |
_applying = true
|
| 58 |
+
if (p.engine) setEngine(p.engine)
|
| 59 |
const mid = resolveModel(p)
|
| 60 |
+
if (mid) setModel(mid)
|
| 61 |
+
setTtsEngine(p.voice)
|
| 62 |
+
if (p.image) setImageEngine(p.image)
|
| 63 |
_applying = false
|
| 64 |
commit(id)
|
| 65 |
}
|
| 66 |
|
| 67 |
const _listeners = new Set()
|
| 68 |
export function onPresetChange(fn) { _listeners.add(fn); return () => _listeners.delete(fn) }
|
| 69 |
+
|
| 70 |
const _onManual = () => { if (!_applying) commit('custom') }
|
| 71 |
onModelChange(_onManual)
|
| 72 |
onTtsEngineChange(_onManual)
|
| 73 |
+
onImageEngineChange(_onManual)
|
web/runtime.js
CHANGED
|
@@ -18,7 +18,7 @@ const loadStr = (k) => { try { return localStorage.getItem(k) || '' } catch { re
|
|
| 18 |
let activeId = (() => {
|
| 19 |
const saved = loadStr(ENGINE_KEY)
|
| 20 |
const e = ENGINES.find((x) => x.id === saved)
|
| 21 |
-
return e && e.available() ? saved :
|
| 22 |
})()
|
| 23 |
const modelSel = loadJSON(MODELS_KEY, {}) // engineId -> chosen model id (remembered per engine)
|
| 24 |
|
|
|
|
| 18 |
let activeId = (() => {
|
| 19 |
const saved = loadStr(ENGINE_KEY)
|
| 20 |
const e = ENGINES.find((x) => x.id === saved)
|
| 21 |
+
return e && e.available() ? saved : 'server'
|
| 22 |
})()
|
| 23 |
const modelSel = loadJSON(MODELS_KEY, {}) // engineId -> chosen model id (remembered per engine)
|
| 24 |
|
web/tts.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
// reader that speaks sentence-by-sentence so a war diary can narrate itself while the
|
| 4 |
// LLM is still writing. Panels + the TTS bar import only from here.
|
| 5 |
import { engine as kokoro } from '/web/ttsKokoro.js'
|
| 6 |
-
import { engine as qwen3, engineLocal as qwen3local
|
| 7 |
import { engine as voxcpm } from '/web/ttsVoxcpm.js'
|
| 8 |
import { engine as kitten } from '/web/ttsKitten.js'
|
| 9 |
import { engine as webspeech } from '/web/ttsWebSpeech.js'
|
|
@@ -19,7 +19,7 @@ let activeId = (() => {
|
|
| 19 |
let saved = ''
|
| 20 |
try { saved = localStorage.getItem(TTS_ENGINE_KEY) || '' } catch { /* ignore */ }
|
| 21 |
const e = ENGINES.find((x) => x.id === saved)
|
| 22 |
-
return e && e.available() ? saved :
|
| 23 |
})()
|
| 24 |
|
| 25 |
// Qwen3-TTS designs a voice from a free-form description (the persona's `voice`).
|
|
|
|
| 3 |
// reader that speaks sentence-by-sentence so a war diary can narrate itself while the
|
| 4 |
// LLM is still writing. Panels + the TTS bar import only from here.
|
| 5 |
import { engine as kokoro } from '/web/ttsKokoro.js'
|
| 6 |
+
import { engine as qwen3, engineLocal as qwen3local } from '/web/ttsQwen3.js'
|
| 7 |
import { engine as voxcpm } from '/web/ttsVoxcpm.js'
|
| 8 |
import { engine as kitten } from '/web/ttsKitten.js'
|
| 9 |
import { engine as webspeech } from '/web/ttsWebSpeech.js'
|
|
|
|
| 19 |
let saved = ''
|
| 20 |
try { saved = localStorage.getItem(TTS_ENGINE_KEY) || '' } catch { /* ignore */ }
|
| 21 |
const e = ENGINES.find((x) => x.id === saved)
|
| 22 |
+
return e && e.available() ? saved : 'voxcpm'
|
| 23 |
})()
|
| 24 |
|
| 25 |
// Qwen3-TTS designs a voice from a free-form description (the persona's `voice`).
|