Files
microdao-daarion/services/stt-service/main.py
Apple e0cb3ddbdb refactor: rewrite STT service to use qwen3_asr_toolkit Python API
- Replace Whisper subprocess calls with direct qwen3_asr_toolkit API
- Remove subprocess dependencies, use pure Python API
- Update to use DASHSCOPE_API_KEY instead of WHISPER_MODEL
- Cleaner code without CLI calls
- Better Ukrainian language recognition quality
2025-11-15 12:55:21 -08:00

171 lines
5.7 KiB
Python

"""
STT Service (Speech-to-Text) для DAGI Router
Використовує qwen3_asr_toolkit для розпізнавання голосу
"""
import os
import uuid
import logging
from pathlib import Path
from typing import Optional
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(
title="STT Service",
description="Speech-to-Text service using Qwen3 ASR Toolkit",
version="2.0.0"
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Configuration
DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY")
TEMP_DIR = Path("/tmp/stt")
TEMP_DIR.mkdir(exist_ok=True)
# Initialize Qwen3 ASR Toolkit
try:
from qwen3_asr_toolkit import transcribe_audio
ASR_AVAILABLE = True
logger.info("qwen3_asr_toolkit loaded successfully")
except ImportError:
ASR_AVAILABLE = False
logger.warning("qwen3_asr_toolkit not available, install with: pip install qwen3-asr-toolkit")
class STTResponse(BaseModel):
text: str
language: Optional[str] = None
duration: Optional[float] = None
def transcribe_with_qwen(audio_path: str) -> tuple[str, Optional[str], Optional[float]]:
"""
Розпізнати мову з аудіо файлу через qwen3_asr_toolkit
Повертає (text, language, duration)
"""
if not ASR_AVAILABLE:
raise ImportError("qwen3_asr_toolkit not installed. Install with: pip install qwen3-asr-toolkit")
if not DASHSCOPE_API_KEY:
raise ValueError("DASHSCOPE_API_KEY environment variable not set")
try:
# qwen3_asr_toolkit автоматично обробляє різні формати аудіо
# та виконує необхідні конвертації
transcript = transcribe_audio(audio_path)
# transcribe_audio повертає текст
# Можна також отримати додаткову інформацію, якщо API підтримує
text = transcript.strip() if isinstance(transcript, str) else str(transcript).strip()
# Для української мови встановлюємо language="uk"
# qwen3_asr_toolkit може автоматично визначати мову
language = "uk" # Можна змінити на автоматичне визначення
# Duration можна отримати з аудіо файлу, якщо потрібно
# Поки що повертаємо None
duration = None
return text, language, duration
except Exception as e:
logger.error(f"Qwen3 ASR transcription failed: {e}", exc_info=True)
raise
@app.post("/stt", response_model=STTResponse)
async def stt(file: UploadFile = File(...)):
"""
Розпізнати мову з аудіо файлу через qwen3_asr_toolkit
Підтримує формати: ogg, mp3, wav, m4a, webm, flac
qwen3_asr_toolkit автоматично обробляє конвертацію
"""
if not ASR_AVAILABLE:
raise HTTPException(
status_code=503,
detail="qwen3_asr_toolkit not available. Install with: pip install qwen3-asr-toolkit"
)
if not DASHSCOPE_API_KEY:
raise HTTPException(
status_code=500,
detail="DASHSCOPE_API_KEY not configured"
)
tmp_id = str(uuid.uuid4())
# Визначаємо розширення файлу
file_ext = "ogg"
if file.filename and "." in file.filename:
file_ext = file.filename.split(".")[-1].lower()
tmp_input = TEMP_DIR / f"{tmp_id}.{file_ext}"
try:
# Зберігаємо вхідний файл
content = await file.read()
tmp_input.write_bytes(content)
logger.info(f"Received audio file: {file.filename}, size: {len(content)} bytes, format: {file_ext}")
# qwen3_asr_toolkit автоматично обробляє різні формати
# та виконує необхідні конвертації всередині
text, language, duration = transcribe_with_qwen(str(tmp_input))
logger.info(f"Transcribed: {text[:100]}... (lang: {language})")
return STTResponse(
text=text,
language=language,
duration=duration
)
except HTTPException:
raise
except ValueError as e:
logger.error(f"STT configuration error: {e}")
raise HTTPException(status_code=500, detail=str(e))
except ImportError as e:
logger.error(f"STT import error: {e}")
raise HTTPException(status_code=503, detail=str(e))
except Exception as e:
logger.error(f"STT error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"STT failed: {str(e)}")
finally:
# Очищаємо тимчасові файли
if tmp_input.exists():
try:
tmp_input.unlink()
except Exception as e:
logger.warning(f"Failed to delete temp file {tmp_input}: {e}")
@app.get("/health")
async def health():
"""Health check"""
return {
"status": "ok" if ASR_AVAILABLE else "degraded",
"service": "stt-service",
"engine": "qwen3_asr_toolkit",
"asr_available": ASR_AVAILABLE,
"api_key_configured": DASHSCOPE_API_KEY is not None
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=9000)