feat(gui): footer backend chips + Settings 'AI Backends' tab + i18n
Surfaces the LLM/TTS hot-swap UX in the GUI so the user doesn't need env vars
or voice commands. Tauri commands for this landed in b243e67; this commit
wires them to Svelte.
Footer (frontend/src/components/Footer.svelte)
- Poll get_active_backends() every 5 s, show TTS / LLM / Profile chips.
- Chips have border accents by kind (cyan = TTS, lime = LLM, orange = Profile).
- Friendly title tooltips with full backend + model names.
- Silent if jarvis-gui can't reach the command (e.g. cold start) — no UI flicker.
Settings — new "AI Backends" tab (frontend/src/routes/settings/index.svelte)
- Status banner showing active LLM (with model name) + TTS + Profile.
- LLM selector: Auto / Groq / Ollama → calls set_llm_backend on change
(hot-swap, persisted to DB).
- TTS selector: Auto / SAPI / Piper / Silero → calls set_tts_backend
(persisted, effective on next jarvis-app restart).
- "Reset context" button → llm_reset_context Tauri command.
- Error toast for swap failures (e.g. Groq selected but no GROQ_TOKEN).
- Tips alert: how to install Ollama / Piper / voice commands.
- Loads prefs via db_read('llm_backend' / 'tts_backend') on mount.
i18n strings (ru/en/ua, .ftl files)
- 14 new keys: settings-ai-backends, settings-ai-active, settings-ai-auto,
settings-ai-applying, settings-ai-error, settings-ai-tips,
settings-llm-backend{,-desc}, settings-tts-backend{,-desc},
settings-llm-context{,-desc}, settings-llm-reset, settings-profile.
Build glue (crates/jarvis-gui/Cargo.toml)
- jarvis-core dep now includes the `llm` feature so backends.rs can resolve
`jarvis_core::llm::*` (was failing to compile after the global LLM module).
.gitignore
- tools/piper/*.dll, piper.exe, voices/, espeak-ng-data/ — locally
installed binaries from tools/piper/install.ps1, not committed.
Build: cargo build --release -p jarvis-gui green (4m, includes npm/Vite).
Test path: rebuild + launch jarvis-gui, /settings → "AI Backends" tab.
This commit is contained in:
parent
b243e67870
commit
d63399f4c9
6 changed files with 313 additions and 4 deletions
|
|
@ -1,4 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
|
||||
import { appInfo, translations, translate } from "@/stores"
|
||||
|
||||
$: t = (key: string) => translate($translations, key)
|
||||
|
|
@ -14,9 +17,72 @@
|
|||
upstreamLink = info.upstreamLink
|
||||
issuesLink = info.issuesLink
|
||||
})
|
||||
|
||||
// Active backends — refreshed every 5 seconds. Silently fails if the
|
||||
// Tauri command isn't registered (e.g. running against an old jarvis-app).
|
||||
interface ActiveBackends {
|
||||
tts: string
|
||||
llm: string
|
||||
llm_model: string | null
|
||||
profile: string
|
||||
}
|
||||
|
||||
let backends: ActiveBackends | null = null
|
||||
let pollId: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
async function refreshBackends() {
|
||||
try {
|
||||
backends = await invoke<ActiveBackends>("get_active_backends")
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
refreshBackends()
|
||||
pollId = setInterval(refreshBackends, 5000)
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
if (pollId !== null) clearInterval(pollId)
|
||||
})
|
||||
|
||||
function chipTitle(key: "tts" | "llm"): string {
|
||||
if (!backends) return ""
|
||||
if (key === "tts") return `TTS backend: ${backends.tts}`
|
||||
const model = backends.llm_model ? ` (${backends.llm_model})` : ""
|
||||
return `LLM backend: ${backends.llm}${model}`
|
||||
}
|
||||
|
||||
function chipLabel(value: string): string {
|
||||
const map: Record<string, string> = {
|
||||
sapi: "SAPI",
|
||||
piper: "Piper",
|
||||
silero: "Silero",
|
||||
groq: "Groq",
|
||||
ollama: "Ollama",
|
||||
none: "—",
|
||||
}
|
||||
return map[value] ?? value
|
||||
}
|
||||
</script>
|
||||
|
||||
<footer id="footer">
|
||||
{#if backends}
|
||||
<p class="backends" title="Активные движки. Меняются в /settings и голосом.">
|
||||
<span class="chip chip-tts" title={chipTitle("tts")}>
|
||||
<small>TTS</small> {chipLabel(backends.tts)}
|
||||
</span>
|
||||
<span class="chip chip-llm" title={chipTitle("llm")}>
|
||||
<small>LLM</small> {chipLabel(backends.llm)}
|
||||
</span>
|
||||
{#if backends.profile && backends.profile !== "default"}
|
||||
<span class="chip chip-profile" title="Active profile">
|
||||
<small>Profile</small> {backends.profile}
|
||||
</span>
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
<p>
|
||||
© {currentYear} J.A.R.V.I.S.
|
||||
<span class="edition" title="Rust + Tauri build">Rust</span>
|
||||
|
|
@ -62,6 +128,35 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.backends {
|
||||
margin-bottom: 8px;
|
||||
|
||||
.chip {
|
||||
display: inline-block;
|
||||
margin: 0 3px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.2px;
|
||||
color: #cfd2d5;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
|
||||
small {
|
||||
color: #6c6e71;
|
||||
margin-right: 3px;
|
||||
font-size: 9px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
&.chip-tts { border-color: rgba(82, 254, 254, 0.25); }
|
||||
&.chip-llm { border-color: rgba(138, 200, 50, 0.25); }
|
||||
&.chip-profile { border-color: rgba(255, 168, 60, 0.25); }
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue