From 5be0fb91abf02718d72cc3cddef5ff95702369ca Mon Sep 17 00:00:00 2001 From: josedario87 Date: Sat, 14 Feb 2026 01:02:54 -0600 Subject: [PATCH] fix: Improve Whisper server startup with async polling and reduce logs - Make server startup async to avoid Bun's 10s timeout - Add frontend polling to detect when server is ready - Use PowerShell Get-NetTCPConnection for reliable port detection - Add starting state to prevent multiple simultaneous starts - Reduce verbose logging, keep only essential info - Add dev-dist and nul to gitignore --- .claude/settings.local.json | 4 +- .gitignore | 2 + frontend/src/components/FloatingVoice.vue | 96 +++++++++++++++-- server/services/whisper.ts | 119 ++++++++++++++-------- server/whisper_server.py | 32 ++---- 5 files changed, 180 insertions(+), 73 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 213cb73..4eeb78e 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -50,7 +50,9 @@ "mcp__agent-ui__localhost_4100-notificar", "mcp__agent-ui__localhost_4100-enviar_al_panel", "mcp__agent-ui__localhost_4100-render_html", - "mcp__agent-ui__localhost_4100-load_vue_component" + "mcp__agent-ui__localhost_4100-load_vue_component", + "mcp__agent-ui__localhost_4100-page_refresh", + "WebFetch(domain:docs.anthropic.com)" ] }, "enableAllProjectMcpServers": true, diff --git a/.gitignore b/.gitignore index c070f87..2e5c9d1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ frontend/node_modules/ .env *.log dist/ +frontend/dev-dist/ +nul diff --git a/frontend/src/components/FloatingVoice.vue b/frontend/src/components/FloatingVoice.vue index e9cdd00..232ae1d 100644 --- a/frontend/src/components/FloatingVoice.vue +++ b/frontend/src/components/FloatingVoice.vue @@ -135,35 +135,59 @@ function initRecognition() { // ============ WHISPER FUNCTIONS ============ -async function checkWhisperStatus() { +async function checkWhisperStatus(updateLoading = true) { try { const res = await fetch(`http://${window.location.hostname}:4100/api/whisper/status`) const data = await res.json() useWhisper.value = data.enabled whisperReady.value = data.running + if (updateLoading) { + whisperLoading.value = data.starting || false + } return data } catch { useWhisper.value = false whisperReady.value = false + if (updateLoading) { + whisperLoading.value = false + } return null } } async function toggleWhisperMode() { + // Prevent multiple clicks + if (whisperLoading.value) { + console.log('[Voice] Toggle already in progress, ignoring') + return + } + whisperLoading.value = true error.value = '' + // Show immediate feedback + if (!useWhisper.value) { + canvasStore.showNotification('Starting Whisper GPU server...', 'info', 10000) + } + try { const res = await fetch(`http://${window.location.hostname}:4100/api/whisper/toggle`, { method: 'POST' }) const data = await res.json() + // Server is starting - poll until ready + if (data.starting) { + console.log('[Voice] Server starting, polling for status...') + await pollWhisperStatus() + return + } + useWhisper.value = data.enabled whisperReady.value = data.running if (data.enabled) { - canvasStore.showNotification('Whisper GPU enabled', 'success') + canvasStore.showNotification('Whisper GPU ready!', 'success') connectWhisperSocket() } else { canvasStore.showNotification('Using Web Speech API', 'info') @@ -171,12 +195,61 @@ async function toggleWhisperMode() { } } catch (e: any) { error.value = 'Failed to toggle Whisper' + canvasStore.showNotification('Error starting Whisper server', 'error') console.error('[Voice] Whisper toggle error:', e) } finally { whisperLoading.value = false } } +// Poll server status until ready or failed +async function pollWhisperStatus() { + const maxAttempts = 60 // 2 minutes max + let attempts = 0 + + while (attempts < maxAttempts) { + await new Promise(resolve => setTimeout(resolve, 2000)) + attempts++ + + try { + const status = await checkWhisperStatus(false) // Don't update loading state + + if (!status) { + console.log('[Voice] Failed to get status') + continue + } + + // Still starting + if (status.starting) { + console.log(`[Voice] Still starting... (${attempts * 2}s)`) + continue + } + + // Started successfully + if (status.running && status.enabled) { + console.log('[Voice] Server ready!') + canvasStore.showNotification('Whisper GPU ready!', 'success') + connectWhisperSocket() + whisperLoading.value = false + return + } + + // Failed to start + console.log('[Voice] Server failed to start') + canvasStore.showNotification('Whisper server failed to start', 'error') + whisperLoading.value = false + return + + } catch (e) { + console.error('[Voice] Polling error:', e) + } + } + + // Timeout + canvasStore.showNotification('Whisper server timeout', 'error') + whisperLoading.value = false +} + function connectWhisperSocket() { if (whisperSocket?.readyState === WebSocket.OPEN) return @@ -671,8 +744,13 @@ onMounted(async () => { document.addEventListener('keyup', handleKeyUp, { capture: true }) // Check Whisper status on mount - await checkWhisperStatus() - if (useWhisper.value) { + const status = await checkWhisperStatus() + + // If server is starting (page was reloaded during startup), continue polling + if (status?.starting) { + console.log('[Voice] Server is starting, resuming polling...') + pollWhisperStatus() + } else if (useWhisper.value) { connectWhisperSocket() } }) @@ -743,8 +821,9 @@ defineExpose({