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:
2026-03-29 20:22:18 -04:00
parent 6f84d90dff
commit eaf70500b8
7 changed files with 163 additions and 28 deletions
+8 -7
View File
@@ -16,11 +16,11 @@ _load_error: str | None = None
async def load_stt_model() -> None:
"""Load the Whisper model. Called once at startup when VOICE_ENABLED=true."""
"""Load the Whisper model. Called once at startup when voice is enabled."""
global _model, _load_error
from fabledassistant.config import Config
from fabledassistant.services.voice_config import get_stt_model, is_voice_enabled
if not Config.VOICE_ENABLED:
if not await is_voice_enabled():
return
async with _model_lock:
@@ -29,15 +29,16 @@ async def load_stt_model() -> None:
try:
from faster_whisper import WhisperModel
logger.info("Loading Whisper STT model '%s'...", Config.STT_MODEL)
model_name = await get_stt_model()
logger.info("Loading Whisper STT model '%s'...", model_name)
loop = asyncio.get_running_loop()
_model = await loop.run_in_executor(
None,
lambda: WhisperModel(Config.STT_MODEL, device="cpu", compute_type="int8"),
lambda: WhisperModel(model_name, device="cpu", compute_type="int8"),
)
logger.info("Whisper STT model '%s' loaded", Config.STT_MODEL)
logger.info("Whisper STT model '%s' loaded", model_name)
except Exception:
_load_error = f"Failed to load Whisper model '{Config.STT_MODEL}'"
_load_error = "Failed to load Whisper STT model"
logger.exception(_load_error)