param( [int]$Port = 9882, [ValidateSet('large-v3', 'large-v3-turbo', 'medium', 'small', 'base')] [string]$Model = 'small', [ValidateSet('auto', 'cuda', 'cpu')] [string]$Device = 'cpu', [int]$WaitReadySeconds = 180 ) # 노트북 상주 faster-whisper 스트리밍 STT 사이드카를 띄운다. # 오디오는 호스트를 벗어나지 않고 외부 STT 키도 필요 없다. $ErrorActionPreference = 'Stop' [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) $OutputEncoding = [System.Text.UTF8Encoding]::new($false) $repoRoot = Split-Path -Parent $PSScriptRoot $serverScript = Join-Path $PSScriptRoot 'local-whisper-stt-server.py' $runtimeLogDir = Join-Path $env:TEMP 'Vignette\local-whisper-stt' $stdoutLog = Join-Path $runtimeLogDir 'server.out.log' $stderrLog = Join-Path $runtimeLogDir 'server.err.log' if (!(Test-Path -LiteralPath $serverScript)) { throw "로컬 whisper 서버 스크립트를 찾지 못했습니다: $serverScript" } # faster-whisper 가 설치된 인터프리터를 고른다. $pyCandidates = @( (Join-Path $env:LOCALAPPDATA 'Programs\Python\Python311\python.exe'), (Join-Path $env:LOCALAPPDATA 'Programs\Python\Python312\python.exe'), 'python' ) $python = $null foreach ($candidate in $pyCandidates) { try { & $candidate -c 'import faster_whisper, websockets' 2>$null if ($LASTEXITCODE -eq 0) { $python = $candidate; break } } catch { } } if (-not $python) { throw 'faster_whisper 와 websockets 가 설치된 python 을 찾지 못했습니다.' } Write-Output "python: $python" $listener = Get-NetTCPConnection -State Listen -LocalPort $Port -ErrorAction SilentlyContinue if ($null -ne $listener) { Write-Output "포트 $Port 에 이미 리스너가 있습니다. 임의 종료하지 않았습니다." return } New-Item -ItemType Directory -Path $runtimeLogDir -Force | Out-Null $serverArgs = @( '-X', 'utf8', $serverScript, '--host', '127.0.0.1', '--port', "$Port", '--model', $Model, '--device', $Device, '--enable' ) $process = Start-Process -WindowStyle Hidden -FilePath $python ` -ArgumentList $serverArgs ` -WorkingDirectory $repoRoot ` -RedirectStandardOutput $stdoutLog ` -RedirectStandardError $stderrLog ` -PassThru Write-Output "로컬 whisper STT 로드를 시작했습니다. PID=$($process.Id) model=$Model device=$Device" Write-Output "로그: $stdoutLog" if ($WaitReadySeconds -le 0) { return } $deadline = (Get-Date).AddSeconds($WaitReadySeconds) while ((Get-Date) -lt $deadline) { if ($process.HasExited) { throw "로컬 whisper STT가 준비되기 전에 종료됐습니다. stderr=$stderrLog" } $ready = Get-NetTCPConnection -State Listen -LocalPort $Port -ErrorAction SilentlyContinue if ($null -ne $ready) { Write-Output "로컬 whisper STT 준비 완료: ws://127.0.0.1:$Port/v1/listen" if (Test-Path -LiteralPath $stdoutLog) { Get-Content -LiteralPath $stdoutLog -Tail 1 } return } Start-Sleep -Seconds 2 } throw "로컬 whisper STT 준비 시간이 ${WaitReadySeconds}초를 넘었습니다. 로그: $stdoutLog"