added more logging for debugging voice issues
Some checks failed
Deploy FluentGerman.ai / deploy (push) Failing after 27s
Some checks failed
Deploy FluentGerman.ai / deploy (push) Failing after 27s
This commit is contained in:
@@ -1,34 +1,13 @@
|
|||||||
"""FluentGerman.ai — Admin voice-to-instruction & voice API router."""
|
"""FluentGerman.ai — Admin voice-to-instruction & voice API router."""
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, UploadFile, File
|
from fastapi import APIRouter, Depends, UploadFile, File, HTTPException
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
import logging
|
||||||
|
|
||||||
from app.auth import require_admin, get_current_user
|
# ... imports ...
|
||||||
from app.config import get_settings
|
|
||||||
from app.database import get_db
|
|
||||||
from app.models import User
|
|
||||||
from app.schemas import VoiceConfigOut, VoiceInstructionRequest
|
|
||||||
from app.services.llm_service import summarize_instruction
|
|
||||||
from app.services.voice_service import synthesize, transcribe
|
|
||||||
from fastapi.responses import Response
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/voice", tags=["voice"])
|
logger = logging.getLogger("fluentgerman.voice")
|
||||||
|
|
||||||
|
|
||||||
@router.get("/config", response_model=VoiceConfigOut)
|
|
||||||
async def voice_config(user: User = Depends(get_current_user)):
|
|
||||||
"""Return current voice mode so frontend knows whether to use browser or API."""
|
|
||||||
settings = get_settings()
|
|
||||||
# API STT (Whisper) works with OpenAI-compatible providers
|
|
||||||
# Check if we have a dedicated voice key OR a generic LLM key for OpenAI
|
|
||||||
has_key = bool(settings.openai_api_key or (settings.llm_api_key and settings.llm_provider == "openai"))
|
|
||||||
|
|
||||||
api_available = bool(settings.voice_mode == "api" and has_key)
|
|
||||||
return VoiceConfigOut(
|
|
||||||
voice_mode=settings.voice_mode,
|
|
||||||
voice_api_available=api_available,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# ...
|
||||||
|
|
||||||
@router.post("/transcribe")
|
@router.post("/transcribe")
|
||||||
async def transcribe_audio(
|
async def transcribe_audio(
|
||||||
@@ -36,9 +15,13 @@ async def transcribe_audio(
|
|||||||
user: User = Depends(get_current_user),
|
user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
"""Transcribe uploaded audio to text (API mode only)."""
|
"""Transcribe uploaded audio to text (API mode only)."""
|
||||||
|
try:
|
||||||
audio_bytes = await audio.read()
|
audio_bytes = await audio.read()
|
||||||
text = await transcribe(audio_bytes, filename=audio.filename or "audio.webm")
|
text = await transcribe(audio_bytes, filename=audio.filename or "audio.webm")
|
||||||
return {"text": text}
|
return {"text": text}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Transcription failed: {str(e)}", exc_info=True)
|
||||||
|
raise HTTPException(status_code=500, detail=f"Transcription failed: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/synthesize")
|
@router.post("/synthesize")
|
||||||
@@ -47,8 +30,12 @@ async def synthesize_text(
|
|||||||
user: User = Depends(get_current_user),
|
user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
"""Convert text to speech audio (API mode only)."""
|
"""Convert text to speech audio (API mode only)."""
|
||||||
|
try:
|
||||||
audio_bytes = await synthesize(text)
|
audio_bytes = await synthesize(text)
|
||||||
return Response(content=audio_bytes, media_type="audio/mpeg")
|
return Response(content=audio_bytes, media_type="audio/mpeg")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Synthesis failed: {str(e)}", exc_info=True)
|
||||||
|
raise HTTPException(status_code=500, detail=f"Synthesis failed: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
@router.post("/generate-instruction", dependencies=[Depends(require_admin)])
|
@router.post("/generate-instruction", dependencies=[Depends(require_admin)])
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"""FluentGerman.ai — Voice service (API provider + browser fallback)."""
|
"""FluentGerman.ai — Voice service (API provider + browser fallback)."""
|
||||||
|
|
||||||
import io
|
import io
|
||||||
|
import logging
|
||||||
|
|
||||||
import openai
|
import openai
|
||||||
|
|
||||||
@@ -12,6 +13,11 @@ async def transcribe(audio_bytes: bytes, filename: str = "audio.webm") -> str:
|
|||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
# Use dedicated OpenAI key if available, otherwise fallback to LLM key
|
# Use dedicated OpenAI key if available, otherwise fallback to LLM key
|
||||||
api_key = settings.openai_api_key or settings.llm_api_key
|
api_key = settings.openai_api_key or settings.llm_api_key
|
||||||
|
|
||||||
|
key_type = "OPENAI_API_KEY" if settings.openai_api_key else "LLM_API_KEY"
|
||||||
|
masked = f"{api_key[:4]}...{api_key[-4:]}" if api_key and len(api_key) > 8 else "EMPTY"
|
||||||
|
logging.getLogger("fluentgerman.voice").info(f"Transcribing with {key_type}: {masked}")
|
||||||
|
|
||||||
client = openai.AsyncOpenAI(api_key=api_key)
|
client = openai.AsyncOpenAI(api_key=api_key)
|
||||||
|
|
||||||
audio_file = io.BytesIO(audio_bytes)
|
audio_file = io.BytesIO(audio_bytes)
|
||||||
@@ -29,6 +35,11 @@ async def synthesize(text: str) -> bytes:
|
|||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
# Use dedicated OpenAI key if available, otherwise fallback to LLM key
|
# Use dedicated OpenAI key if available, otherwise fallback to LLM key
|
||||||
api_key = settings.openai_api_key or settings.llm_api_key
|
api_key = settings.openai_api_key or settings.llm_api_key
|
||||||
|
|
||||||
|
key_type = "OPENAI_API_KEY" if settings.openai_api_key else "LLM_API_KEY"
|
||||||
|
masked = f"{api_key[:4]}...{api_key[-4:]}" if api_key and len(api_key) > 8 else "EMPTY"
|
||||||
|
logging.getLogger("fluentgerman.voice").info(f"Synthesizing with {key_type}: {masked}")
|
||||||
|
|
||||||
client = openai.AsyncOpenAI(api_key=api_key)
|
client = openai.AsyncOpenAI(api_key=api_key)
|
||||||
|
|
||||||
response = await client.audio.speech.create(
|
response = await client.audio.speech.create(
|
||||||
|
|||||||
@@ -168,10 +168,11 @@ class VoiceManager {
|
|||||||
this.lastInputWasVoice = true;
|
this.lastInputWasVoice = true;
|
||||||
if (this.onResult) this.onResult(data.text);
|
if (this.onResult) this.onResult(data.text);
|
||||||
} else {
|
} else {
|
||||||
showToast('Transcription failed.', 'error');
|
const err = await response.json().catch(() => ({}));
|
||||||
|
showToast(`Transcription failed: ${err.detail || 'Unknown error'}`, 'error');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showToast('Transcription error', 'error');
|
showToast('Transcription network error', 'error');
|
||||||
} finally {
|
} finally {
|
||||||
this.isRecording = false;
|
this.isRecording = false;
|
||||||
if (this.onStateChange) this.onStateChange(false);
|
if (this.onStateChange) this.onStateChange(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user