From 6e317a9c6702a814ea7a162dd7e55d5ff9a7d98a Mon Sep 17 00:00:00 2001 From: "thomas.kopp" Date: Wed, 1 Apr 2026 12:41:45 +0200 Subject: [PATCH] fix: error state resettable via mic click, debug logging, pipeline traceback --- api/pipeline.py | 6 ++++++ api/router.py | 3 +++ frontend/app.js | 12 ++++++++++-- main.py | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/api/pipeline.py b/api/pipeline.py index b2b5cd0..3dadf5c 100644 --- a/api/pipeline.py +++ b/api/pipeline.py @@ -1,7 +1,11 @@ +import logging import os import tempfile +import traceback from api.state import state, Status + +logger = logging.getLogger(__name__) from config import load as load_config from transcription import engine as transcription_engine from llm import OllamaClient @@ -59,6 +63,8 @@ async def run_pipeline(): await state.set_status(Status.IDLE) except Exception as e: + tb = traceback.format_exc() + logger.error("Pipeline error:\n%s", tb) state.last_error = str(e) await state.set_status(Status.ERROR) await broadcast({"event": "error", "message": str(e)}) diff --git a/api/router.py b/api/router.py index 8af125a..c8b7f2e 100644 --- a/api/router.py +++ b/api/router.py @@ -97,6 +97,9 @@ async def toggle_recording(user: dict = Depends(current_user)): if state.status == Status.RECORDING: asyncio.create_task(run_pipeline()) return {"action": "stopped"} + if state.status == Status.ERROR: + await state.set_status(Status.IDLE) + return {"action": "reset"} if state.status == Status.IDLE: from audio import AudioRecorder state._recorder = AudioRecorder() diff --git a/frontend/app.js b/frontend/app.js index 4cbb505..c89e9d8 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -47,12 +47,20 @@ function setStatus(status) { btn.className = status; headerStatus.className = `status-badge ${status}`; const label = STATUS_LABELS[status] || status; - statusText.textContent = label; + statusText.textContent = status === 'error' ? label + ' — klicken zum Zurücksetzen' : label; headerStatus.textContent = label; btn.disabled = status === 'processing'; } -btn.addEventListener('click', () => apiFetch('/toggle', { method: 'POST' })); +btn.addEventListener('click', async () => { + const r = await apiFetch('/toggle', { method: 'POST' }); + const data = await r.json(); + if (data.action === 'reset') { + preview.textContent = 'Noch keine Aufnahme verarbeitet.'; + preview.classList.remove('has-content'); + setStatus('idle'); + } +}); function connectWs() { const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'; diff --git a/main.py b/main.py index 554903f..f10a636 100644 --- a/main.py +++ b/main.py @@ -137,7 +137,7 @@ if __name__ == "__main__": write_pid(pid_path) signal.signal(signal.SIGUSR1, _sigusr1_handler) - uvicorn_cfg = uvicorn.Config(app, host=host, port=port, log_level="warning") + uvicorn_cfg = uvicorn.Config(app, host=host, port=port, log_level="debug") server_thread = threading.Thread(target=run_server, args=(uvicorn_cfg,), daemon=True) server_thread.start()