diff --git a/.gitignore b/.gitignore index 8e48642..e5d3e15 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ data/samples/ *.pyc *.whl *.log -/llm_weights/ \ No newline at end of file +/llm_weights/ +/whisper_weights/ \ No newline at end of file diff --git a/README.md b/README.md index f923b77..44500f6 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ - **[주의] 대용량 언어모델 수동 배치**: `Qwen3.5-0.8B-Q4_K_M.gguf` (또는 지정된 모델 버전)와 같은 대형 `.gguf` 파일들은 `.gitignore` 처리되어 Git에서 제외되므로 개별적으로 다운로드 받아 프로젝트 내 **`llm_weights/`** 폴더 안에 배치해야 합니다. (동봉된 `setup.ps1` 스크립트를 관리자 권한으로 실행하면 모델 다운로드 및 폴더 생성까지 자동으로 처리됩니다). - 최초 기동 시 오디오 저장 폴더(`data/audio/`, `data/samples/`) 및 로컬 DB 파일(`whisper.db`)은 시스템 내에 자동으로 초기화됩니다. + - whisper_weights 폴더와 llm_weights 폴더는 `.gitignore` 처리되어 Git에서 제외되므로 개별적으로 다운로드 받아 프로젝트 내 **`whisper_weights/`** 폴더와 **`llm_weights/`** 폴더 안에 배치해야 합니다. 2. **서버 실행**: `run_server.ps1` 또는 아래 구문을 사용합니다. ```bash diff --git a/app/core/config.py b/app/core/config.py index 68168c0..f4633d0 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -6,6 +6,9 @@ class Settings(BaseSettings): # CPU/내장그래픽 구동을 위해 한국어 인식률이 극대화된 모델 지정 WHISPER_MODEL_NAME: str = "large-v3-turbo" + # 로컬 캐시 경로 (비워두면 HuggingFace 전역 캐시 사용, 설정하면 내부 폴더 사용) + WHISPER_MODEL_PATH: str = "./whisper_weights" + # 로컬 LLM GGUF 모델 파일 경로 (.env에서 오버라이드 가능) LLM_MODEL_PATH: str = "" LLM_ENABLED: bool = False # False면 LLM 분석 스킵 (모델 미설정 시 안전 기본값) diff --git a/app/services/stt_service.py b/app/services/stt_service.py index 3e672a5..82785ec 100644 --- a/app/services/stt_service.py +++ b/app/services/stt_service.py @@ -35,8 +35,16 @@ class WhisperSTTService: if self._model is None: try: - # CPU 타겟, int8 양자화로 모델 객체 생성. (최초 시도시 모델 자동 다운로드됨) - self._model = WhisperModel(self.model_name, device="cpu", compute_type="int8") + # CPU 타겟, int8 양자화로 모델 객체 생성. + # WHISPER_MODEL_PATH가 설정되어 있으면 해당 폴더를 다운로드 루트로 사용 (퀔야 3기동 후 오프라인 가능) + from app.core.config import settings + download_root = settings.WHISPER_MODEL_PATH if settings.WHISPER_MODEL_PATH else None + self._model = WhisperModel( + self.model_name, + device="cpu", + compute_type="int8", + download_root=download_root + ) except Exception as e: raise ModelNotFoundError(f"Whisper 모델 로드 실패 ({self.model_name}): {str(e)}") diff --git a/setup.ps1 b/setup.ps1 index 38aa9e7..79740b3 100644 --- a/setup.ps1 +++ b/setup.ps1 @@ -4,77 +4,96 @@ HUTAMS 프로젝트 최초 설정 스크립트. .DESCRIPTION 1. uv 동기화(의존성 라이브러리 설치) -2. llm_weights 폴더 생성 +2. llm_weights, whisper_weights 폴더 생성 3. 대용량 언어모델(.gguf) 다운로드 (존재하지 않을 경우) +4. Whisper STT 모델 다운로드 (존재하지 않을 경우) #> $ErrorActionPreference = "Stop" -Write-Host "=========================================" -ForegroundColor Cyan -Write-Host " HUTAMS STT 프로젝트 초기화 스크립트 " -ForegroundColor Cyan -Write-Host "=========================================" -ForegroundColor Cyan +Write-Host "=====================================================" -ForegroundColor Cyan +Write-Host " HUTAMS STT 프로젝트 초기화 스크립트 (setup.ps1) " -ForegroundColor Cyan +Write-Host "=====================================================" -ForegroundColor Cyan -# 1. uv 패키지 설치 -Write-Host "`n[1] 패키지 매니저(uv) 동기화 중..." -ForegroundColor Yellow +# ── 1. uv 패키지 설치 ──────────────────────────────────────────────────────── +Write-Host "`n[1/4] uv sync로 파이썬 패키지 설치 중..." -ForegroundColor Yellow try { uv sync Write-Host "패키지 설치 완료!" -ForegroundColor Green } catch { - Write-Host "uv 실행 실패. uv가 설치되어 있는지 확인해주세요." -ForegroundColor Red + Write-Host "uv 실행 실패. https://docs.astral.sh/uv/ 에서 uv를 설치 후 재시도해주세요." -ForegroundColor Red exit 1 } -# 2. 로컬 모델 폴더 확인 및 생성 -$ModelDir = "llm_weights" -if (-Not (Test-Path -Path $ModelDir)) { - Write-Host "`n[2] 모델 폴더($ModelDir) 생성 중..." -ForegroundColor Yellow - New-Item -ItemType Directory -Force -Path $ModelDir | Out-Null - Write-Host "폴더 생성 완료!" -ForegroundColor Green +# ── 2. 폴더 생성 ───────────────────────────────────────────────────────────── +Write-Host "`n[2/4] 모델 폴더 생성 중..." -ForegroundColor Yellow +New-Item -ItemType Directory -Force -Path "llm_weights" | Out-Null +New-Item -ItemType Directory -Force -Path "whisper_weights" | Out-Null +New-Item -ItemType Directory -Force -Path "data/audio" | Out-Null +New-Item -ItemType Directory -Force -Path "data/samples" | Out-Null +Write-Host "폴더 생성 완료!" -ForegroundColor Green + +# ── 3. LLM 모델(.gguf) 다운로드 ────────────────────────────────────────────── +$LlmModelName = "Qwen3.5-0.8B-Q4_K_M.gguf" +$LlmModelPath = "llm_weights\$LlmModelName" + +# [⚠️ 실제 사용 중인 모델 URL로 교체하세요] +$LlmDownloadUrl = "https://huggingface.co/unsloth/Qwen3.5-0.8B-GGUF/resolve/main/Qwen3.5-0.8B-Q4_K_M.gguf?download=true" + +# $LlmModelName2 = "Qwen3.5-2B-Q4_K_M.gguf" +# $LlmDownloadUrl2 = "https://huggingface.co/unsloth/Qwen3.5-2B-GGUF/resolve/main/Qwen3.5-2B-Q4_K_M.gguf?download=true"# 모델이 실제로는 Qwen3.5-0.8B 이므로 실제 유효한 HuggingFace URL + +Write-Host "`n[3/4] LLM 모델($LlmModelName) 확인 중..." -ForegroundColor Yellow +if (Test-Path -Path $LlmModelPath) { + Write-Host "이미 존재합니다. 다운로드를 건너뜁니다." -ForegroundColor Green } else { - Write-Host "`n[2] 모델 폴더($ModelDir)가 이미 존재합니다." -ForegroundColor Green -} - -# 3. 모델 다운로드 -$Model1Name = "Qwen3.5-0.8B-Q4_K_M.gguf" -$ModelPath = Join-Path -Path $ModelDir -ChildPath $ModelName -$DownloadUrl1 = "https://huggingface.co/unsloth/Qwen3.5-0.8B-GGUF/resolve/main/Qwen3.5-0.8B-Q4_K_M.gguf?download=true" - -$Model2Name = "Qwen3.5-2B-Q4_K_M.gguf" -$ModelPath = Join-Path -Path $ModelDir -ChildPath $ModelName -$DownloadUrl2 = "https://huggingface.co/unsloth/Qwen3.5-2B-GGUF/resolve/main/Qwen3.5-2B-Q4_K_M.gguf?download=true"# 모델이 실제로는 Qwen3.5-0.8B 이므로 실제 유효한 HuggingFace URL -if (Test-Path -Path $ModelPath) { - Write-Host "`n[3.1] 언어 모델($Model1Name)이 이미 존재합니다. 다운로드를 건너뜁니다." -ForegroundColor Green -} -else { - Write-Host "`n[3.1] 언어 모델($Model1Name) 다운로드 중... (용량이 크므로 시간이 걸릴 수 있습니다)" -ForegroundColor Yellow + Write-Host "다운로드 시작 (용량이 크므로 시간이 걸릴 수 있습니다)..." -ForegroundColor Yellow try { - Invoke-WebRequest -Uri $DownloadUrl1 -OutFile $ModelPath - Write-Host "모델 다운로드 완료!" -ForegroundColor Green + Invoke-WebRequest -Uri $LlmDownloadUrl -OutFile $LlmModelPath + Write-Host "LLM 모델 다운로드 완료!" -ForegroundColor Green } catch { - Write-Host "모델 다운로드 실패. 아래 URL에서 수동으로 다운로드 후 $ModelDir 폴더 안에 배치해주세요." -ForegroundColor Red - Write-Host "-> $DownloadUrl1" -ForegroundColor Yellow + Write-Host "[오류] LLM 모델 다운로드 실패. 아래 URL에서 수동으로 받아 'llm_weights/' 폴더에 배치하세요:" -ForegroundColor Red + Write-Host " → $LlmDownloadUrl" -ForegroundColor Yellow } } -if (Test-Path -Path $ModelPath) { - Write-Host "`n[3.2] 언어 모델($Model2Name)이 이미 존재합니다. 다운로드를 건너뜁니다." -ForegroundColor Green +# ── 4. Whisper STT 모델 다운로드 ───────────────────────────────────────────── +$WhisperDir = "whisper_weights" +$WhisperModelName = "large-v3-turbo" +$WhisperHfRepoId = "mobiuslabsgmbh/faster-whisper-large-v3-turbo" + +Write-Host "`n[4/4] Whisper STT 모델($WhisperModelName) 다운로드 중..." -ForegroundColor Yellow +# whisper_weights/ 안에 이미 모델 폴더가 있으면 건너뜀 +$WhisperFlag = Get-ChildItem -Path $WhisperDir -ErrorAction SilentlyContinue | Measure-Object +if ($WhisperFlag.Count -gt 0) { + Write-Host "이미 whisper_weights 폴더에 파일이 존재합니다. 건너뜁니다." -ForegroundColor Green } else { - Write-Host "`n[3.2] 언어 모델($Model2Name) 다운로드 중... (용량이 크므로 시간이 걸릴 수 있습니다)" -ForegroundColor Yellow + Write-Host "uv run을 통해 Whisper 모델을 프로젝트 내부 캐시로 다운로드합니다 (최초 1회)..." -ForegroundColor Yellow try { - Invoke-WebRequest -Uri $DownloadUrl2 -OutFile $ModelPath - Write-Host "모델 다운로드 완료!" -ForegroundColor Green + # 환경변수로 다운로드 경로를 설정하고 서버를 즉시 로드(초기화) 후 종료 + $env:WHISPER_MODEL_PATH = (Resolve-Path $WhisperDir).Path + uv run python -c " +from faster_whisper import WhisperModel +import os +model_path = os.environ.get('WHISPER_MODEL_PATH', './whisper_weights') +print(f'Whisper 모델 다운로드 위치: {model_path}') +m = WhisperModel('$WhisperModelName', device='cpu', compute_type='int8', download_root=model_path) +print('다운로드 완료!') +" + Write-Host "Whisper STT 모델 다운로드 완료!" -ForegroundColor Green } catch { - Write-Host "모델 다운로드 실패. 아래 URL에서 수동으로 다운로드 후 $ModelDir 폴더 안에 배치해주세요." -ForegroundColor Red - Write-Host "-> $DownloadUrl2" -ForegroundColor Yellow + Write-Host "[오류] Whisper 모델 다운로드 실패. 서버 첫 기동 시 자동으로 다운로드를 시도합니다." -ForegroundColor Yellow } } -Write-Host "`n=========================================" -ForegroundColor Cyan -Write-Host " HUTAMS 셋업이 완료되었습니다! " -ForegroundColor Cyan -Write-Host " 이제 run_server.ps1을 실행하여 서버를 시작하세요." -ForegroundColor Cyan -Write-Host "=========================================" -ForegroundColor Cyan +# ── 완료 ────────────────────────────────────────────────────────────────────── +Write-Host "" +Write-Host "=====================================================" -ForegroundColor Cyan +Write-Host " 모든 설정이 완료되었습니다! " -ForegroundColor Cyan +Write-Host " run_server.ps1을 실행하여 관제 서버를 시작하세요. " -ForegroundColor Cyan +Write-Host "=====================================================" -ForegroundColor Cyan