Jamulix: Optimierungen übernehmen, lokale Voice-Modelle ignorieren

This commit is contained in:
Dieter Schlüter 2026-06-24 22:01:38 +02:00
commit 91b9ef3374
12 changed files with 555 additions and 94 deletions

View file

@ -1,3 +1,4 @@
import httpx
from fastapi import APIRouter
from app.config import settings, active_profile
@ -12,14 +13,60 @@ from app.dependencies import (
router = APIRouter()
async def _chatterbox_reachable() -> bool:
try:
async with httpx.AsyncClient(timeout=1.5) as client:
r = await client.get(f"{settings.chatterbox_base_url.rstrip('/')}/status")
return r.status_code < 500
except Exception:
return False
async def _cartesia_available() -> bool:
key = settings.cartesia_api_key.strip()
voice_id = settings.cartesia_voice_id.strip()
if not key or not voice_id:
return False
try:
async with httpx.AsyncClient(timeout=5.0) as client:
r = await client.get(
"https://api.cartesia.ai/voices",
headers={"X-API-Key": key, "Cartesia-Version": "2024-06-10"},
)
return r.status_code < 400
except Exception:
return False
async def _openrouter_tts_available() -> bool:
"""Prüft ob das konfigurierte TTS-Modell auf OpenRouter via /audio/speech erreichbar ist."""
key = settings.openrouter_api_key.strip()
model = settings.openrouter_tts_model.strip()
if not key or not model:
return False
try:
async with httpx.AsyncClient(timeout=5.0) as client:
r = await client.post(
"https://openrouter.ai/api/v1/audio/speech",
headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
json={"model": model, "input": "Test", "voice": settings.openrouter_tts_voice or "alloy",
"response_format": "mp3"},
)
# 200 = Audio OK, 4xx mit "does not exist" = Modell fehlt
if r.status_code == 200:
return True
body = r.text
return "does not exist" not in body and r.status_code < 500
except Exception:
return False
@router.get("/config")
async def get_config():
"""Zeigt aktives Profil, aufgeloeste Default-Route und verfuegbare Bausteine.
Bewusst OHNE Secrets - API-Keys werden nur als 'gesetzt/nicht gesetzt' gemeldet.
"""
"""Zeigt aktives Profil, aufgeloeste Default-Route und verfuegbare Bausteine."""
route = resolve_route()
audio_router = get_audio_router()
chatterbox_ok, openrouter_tts_ok, cartesia_ok = await _chatterbox_reachable(), await _openrouter_tts_available(), await _cartesia_available()
return {
"profile": active_profile(),
"app_env": settings.app_env,
@ -30,6 +77,9 @@ async def get_config():
"tts_providers": sorted(TTS_REGISTRY),
"input_endpoints": [c.model_dump() for c in await audio_router.list_inputs()],
"output_endpoints": [c.model_dump() for c in await audio_router.list_outputs()],
"chatterbox_reachable": chatterbox_ok,
"openrouter_tts_available": openrouter_tts_ok,
"cartesia_available": cartesia_ok,
},
"secrets": {
"openrouter_api_key_set": bool(settings.openrouter_api_key.strip()),