feat: move voice enable/model config to admin UI
Replace VOICE_ENABLED env var gate with DB-backed admin setting. - services/voice_config.py: reads voice_enabled + voice_stt_model from admin user's settings row (falls back to env var defaults) - routes/admin.py: GET/PUT /api/admin/voice for admin configuration - routes/voice.py, services/stt.py, services/tts.py: read enabled/model from DB via voice_config instead of Config directly - app.py: always schedule model loaders at startup; they self-gate on the DB setting so no conditional needed at the call site - SettingsView.vue: Voice section in Admin → Config tab (enable toggle + STT model dropdown); user Voice tab now points to admin panel when disabled No env var required to test — enable via Settings → Admin → Config → Voice. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -445,6 +445,29 @@ const baseUrl = ref("");
|
||||
const savingBaseUrl = ref(false);
|
||||
const baseUrlSaved = ref(false);
|
||||
|
||||
// Voice config (admin only)
|
||||
const adminVoiceEnabled = ref(false);
|
||||
const adminVoiceSttModel = ref("base.en");
|
||||
const savingAdminVoice = ref(false);
|
||||
const adminVoiceSaved = ref(false);
|
||||
|
||||
async function saveAdminVoice() {
|
||||
savingAdminVoice.value = true;
|
||||
adminVoiceSaved.value = false;
|
||||
try {
|
||||
await apiPut("/api/admin/voice", {
|
||||
voice_enabled: adminVoiceEnabled.value,
|
||||
voice_stt_model: adminVoiceSttModel.value,
|
||||
});
|
||||
adminVoiceSaved.value = true;
|
||||
setTimeout(() => { adminVoiceSaved.value = false; }, 2000);
|
||||
// Refresh the user-facing voice status after toggling
|
||||
voiceTabLoaded.value = false;
|
||||
} finally {
|
||||
savingAdminVoice.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Search test (SearXNG)
|
||||
const searxngConfigured = ref(false);
|
||||
const searxngUrl = ref("");
|
||||
@@ -565,6 +588,13 @@ onMounted(async () => {
|
||||
} catch {
|
||||
// base URL not configured yet
|
||||
}
|
||||
try {
|
||||
const vc = await apiGet<Record<string, string>>("/api/admin/voice");
|
||||
adminVoiceEnabled.value = vc.voice_enabled === "true";
|
||||
adminVoiceSttModel.value = vc.voice_stt_model ?? "base.en";
|
||||
} catch {
|
||||
// voice not configured yet
|
||||
}
|
||||
}
|
||||
_loadTabContent(activeTab.value);
|
||||
});
|
||||
@@ -1888,9 +1918,8 @@ function formatUserDate(iso: string): string {
|
||||
</div>
|
||||
|
||||
<div v-if="!voiceStatus?.enabled" class="field-hint" style="margin-top:0.75rem;">
|
||||
Set <code>VOICE_ENABLED=true</code> in your server environment to activate voice features.
|
||||
Optionally set <code>STT_MODEL</code> (tiny.en / base.en / small.en / medium.en) and
|
||||
install <code>pip install fabledassistant[voice]</code>.
|
||||
Voice is disabled. An administrator can enable it under
|
||||
<strong>Admin → Config → Voice</strong>.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -2146,6 +2175,38 @@ FABLE_API_KEY=<your-api-key></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="settings-section full-width">
|
||||
<h2>Voice (Speech-to-Speech)</h2>
|
||||
<p class="section-desc">
|
||||
Enable self-hosted voice using faster-whisper (STT) and Kokoro (TTS).
|
||||
Models load at startup — a server restart is required after changing these settings.
|
||||
Install dependencies first: <code>pip install faster-whisper kokoro soundfile</code>
|
||||
</p>
|
||||
<div class="checkbox-field">
|
||||
<label>
|
||||
<input type="checkbox" v-model="adminVoiceEnabled" />
|
||||
Enable Voice
|
||||
</label>
|
||||
<p class="field-hint">Shows the PTT button and voice settings to all users.</p>
|
||||
</div>
|
||||
<div v-if="adminVoiceEnabled" class="field" style="max-width:280px; margin-top:0.75rem;">
|
||||
<label for="stt-model">STT Model</label>
|
||||
<select id="stt-model" v-model="adminVoiceSttModel" class="input">
|
||||
<option value="tiny.en">tiny.en — fastest, lowest accuracy (~75 MB)</option>
|
||||
<option value="base.en">base.en — balanced (recommended, ~145 MB)</option>
|
||||
<option value="small.en">small.en — better accuracy (~465 MB)</option>
|
||||
<option value="medium.en">medium.en — best accuracy (~1.5 GB)</option>
|
||||
</select>
|
||||
<p class="field-hint">Larger models are more accurate but use more RAM and take longer to load.</p>
|
||||
</div>
|
||||
<div class="actions" style="margin-top:0.85rem;">
|
||||
<button class="btn-save" @click="saveAdminVoice" :disabled="savingAdminVoice">
|
||||
{{ adminVoiceSaved ? 'Saved ✓' : savingAdminVoice ? 'Saving…' : 'Save' }}
|
||||
</button>
|
||||
<span v-if="adminVoiceSaved" class="field-hint">Restart the server to apply model changes.</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ── Users ── -->
|
||||
|
||||
Reference in New Issue
Block a user