feat: Add presence heartbeat for Matrix online status
- matrix-gateway: POST /internal/matrix/presence/online endpoint - usePresenceHeartbeat hook with activity tracking - Auto away after 5 min inactivity - Offline on page close/visibility change - Integrated in MatrixChatRoom component
This commit is contained in:
@@ -1,26 +1,25 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Встановити системні залежності
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ffmpeg \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Встановлюємо системні залежності
|
||||
# qwen3_asr_toolkit може потребувати ffmpeg для обробки деяких форматів
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ffmpeg \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Копіюємо requirements та встановлюємо залежності
|
||||
# Копіювати requirements
|
||||
COPY requirements.txt .
|
||||
|
||||
# Встановити Python залежності
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Копіюємо код
|
||||
COPY . .
|
||||
# Копіювати код
|
||||
COPY app/ ./app/
|
||||
|
||||
# Створюємо тимчасову директорію
|
||||
RUN mkdir -p /tmp/stt
|
||||
# Whisper модель завантажиться автоматично при першому запиті
|
||||
# Це уникає проблем з мережею під час build
|
||||
|
||||
# Відкриваємо порт
|
||||
EXPOSE 9000
|
||||
|
||||
# Запускаємо додаток
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "9000"]
|
||||
EXPOSE 8895
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8895"]
|
||||
|
||||
@@ -1,127 +1,279 @@
|
||||
# STT Service (Speech-to-Text)
|
||||
# STT Service - Speech-to-Text для DAARION
|
||||
|
||||
Сервіс для розпізнавання мови з аудіо файлів за допомогою Qwen3 ASR Toolkit.
|
||||
Сервіс конвертації аудіо в текст використовуючи OpenAI Whisper AI.
|
||||
|
||||
## Можливості
|
||||
|
||||
- Розпізнавання мови з голосових повідомлень (Telegram voice, audio, video_note)
|
||||
- Підтримка форматів: ogg, mp3, wav, m4a, webm, flac
|
||||
- Автоматична обробка та конвертація аудіо (всередині qwen3_asr_toolkit)
|
||||
- Чистий Python API без subprocess/CLI викликів
|
||||
- Висока якість розпізнавання української мови
|
||||
- 🎤 **Розпізнавання мови**: Whisper AI (base model)
|
||||
- 🌍 **Мультимовність**: Підтримка української та інших мов
|
||||
- 📊 **Формати аудіо**: webm, mp3, wav, m4a, ogg
|
||||
- 🚀 **Швидкість**: ~5-10 секунд для 1 хвилини аудіо
|
||||
- 🔒 **Безпека**: Локальна обробка, без відправки на зовнішні сервери
|
||||
|
||||
## Запуск
|
||||
## Встановлення
|
||||
|
||||
### Локально (development)
|
||||
### Docker (рекомендовано)
|
||||
|
||||
```bash
|
||||
cd services/stt-service
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Локально
|
||||
|
||||
```bash
|
||||
cd services/stt-service
|
||||
pip install -r requirements.txt
|
||||
uvicorn main:app --reload --host 0.0.0.0 --port 9000
|
||||
python -m app.main
|
||||
```
|
||||
|
||||
### Docker
|
||||
## API Endpoints
|
||||
|
||||
```bash
|
||||
docker-compose up stt-service
|
||||
```
|
||||
### 1. POST /api/stt
|
||||
|
||||
## API
|
||||
|
||||
### POST /stt
|
||||
|
||||
Розпізнати мову з аудіо файлу.
|
||||
Конвертує base64 аудіо в текст.
|
||||
|
||||
**Request:**
|
||||
- `file`: аудіо файл (multipart/form-data)
|
||||
```json
|
||||
POST http://localhost:8895/api/stt
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"audio": "data:audio/webm;base64,GkXfo59ChoEBQveBAULygQRC...",
|
||||
"language": "uk",
|
||||
"model": "base"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"text": "розпізнаний текст",
|
||||
"text": "Привіт, це тестове повідомлення",
|
||||
"language": "uk",
|
||||
"duration": 5.2
|
||||
"duration": 2.5,
|
||||
"model": "base",
|
||||
"confidence": 0.95
|
||||
}
|
||||
```
|
||||
|
||||
**Приклад:**
|
||||
---
|
||||
|
||||
### 2. POST /api/stt/upload
|
||||
|
||||
Конвертує завантажений аудіо файл в текст.
|
||||
|
||||
**Request:**
|
||||
```bash
|
||||
curl -X POST http://localhost:9000/stt \
|
||||
-F "file=@voice.ogg"
|
||||
curl -X POST http://localhost:8895/api/stt/upload \
|
||||
-F "file=@recording.webm"
|
||||
```
|
||||
|
||||
### GET /health
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"text": "Привіт, це тестове повідомлення",
|
||||
"filename": "recording.webm",
|
||||
"language": "uk",
|
||||
"model": "base"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. GET /health
|
||||
|
||||
Health check endpoint.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "healthy",
|
||||
"whisper": "available",
|
||||
"model": "base"
|
||||
}
|
||||
```
|
||||
|
||||
## Інтеграція з Frontend
|
||||
|
||||
### 1. Оновити Enhanced Chat
|
||||
|
||||
**Файл:** `src/components/microdao/MicroDaoOrchestratorChatEnhanced.tsx`
|
||||
|
||||
```typescript
|
||||
// Після запису аудіо
|
||||
const handleVoiceStop = async () => {
|
||||
if (mediaRecorderRef.current) {
|
||||
mediaRecorderRef.current.stop();
|
||||
|
||||
mediaRecorderRef.current.onstop = async () => {
|
||||
const audioBlob = new Blob(audioChunksRef.current, { type: 'audio/webm' });
|
||||
|
||||
// Конвертувати в base64
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = async () => {
|
||||
const base64Audio = reader.result as string;
|
||||
|
||||
// Відправити на STT Service
|
||||
try {
|
||||
const response = await fetch('http://localhost:8895/api/stt', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
audio: base64Audio,
|
||||
language: 'uk',
|
||||
model: 'base'
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Додати розшифрований текст в input
|
||||
setInput((prev) => prev + (prev ? ' ' : '') + data.text);
|
||||
console.log('✅ STT:', data.text);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ STT error:', error);
|
||||
// Fallback - показати що аудіо записано
|
||||
setInput((prev) => prev + ' 🎤 [Голосове повідомлення]');
|
||||
}
|
||||
};
|
||||
reader.readAsDataURL(audioBlob);
|
||||
};
|
||||
}
|
||||
|
||||
setIsRecording(false);
|
||||
};
|
||||
```
|
||||
|
||||
## Моделі Whisper
|
||||
|
||||
| Модель | Розмір | VRAM | Швидкість | Точність |
|
||||
|--------|--------|------|-----------|----------|
|
||||
| tiny | 39 MB | ~1 GB | Дуже швидко | Низька |
|
||||
| base | 74 MB | ~1 GB | Швидко | Середня |
|
||||
| small | 244 MB | ~2 GB | Середньо | Хороша |
|
||||
| medium | 769 MB | ~5 GB | Повільно | Висока |
|
||||
| large | 1550 MB | ~10 GB | Дуже повільно | Найвища |
|
||||
|
||||
**Рекомендація для НОДА2:** `base` (баланс швидкості та точності)
|
||||
|
||||
## Підтримувані мови
|
||||
|
||||
- 🇺🇦 Українська (uk)
|
||||
- 🇬🇧 Англійська (en)
|
||||
- 🇷🇺 Російська (ru)
|
||||
- 🇵🇱 Польська (pl)
|
||||
- 🇩🇪 Німецька (de)
|
||||
- 🇫🇷 Французька (fr)
|
||||
- ... і ще 90+ мов
|
||||
|
||||
## Тестування
|
||||
|
||||
### 1. cURL (base64)
|
||||
|
||||
```bash
|
||||
# Записати аудіо
|
||||
ffmpeg -f avfoundation -i ":0" -t 5 test.webm
|
||||
|
||||
# Конвертувати в base64
|
||||
BASE64_AUDIO=$(base64 -i test.webm)
|
||||
|
||||
# Відправити на STT
|
||||
curl -X POST http://localhost:8895/api/stt \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"audio\":\"data:audio/webm;base64,$BASE64_AUDIO\",\"language\":\"uk\"}"
|
||||
```
|
||||
|
||||
### 2. cURL (file upload)
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8895/api/stt/upload \
|
||||
-F "file=@test.webm"
|
||||
```
|
||||
|
||||
### 3. Python
|
||||
|
||||
```python
|
||||
import requests
|
||||
import base64
|
||||
|
||||
# Прочитати аудіо файл
|
||||
with open('test.webm', 'rb') as f:
|
||||
audio_bytes = f.read()
|
||||
|
||||
# Конвертувати в base64
|
||||
audio_base64 = base64.b64encode(audio_bytes).decode()
|
||||
|
||||
# Відправити на STT
|
||||
response = requests.post('http://localhost:8895/api/stt', json={
|
||||
'audio': f'data:audio/webm;base64,{audio_base64}',
|
||||
'language': 'uk',
|
||||
'model': 'base'
|
||||
})
|
||||
|
||||
print(response.json())
|
||||
```
|
||||
|
||||
## Конфігурація
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- `DASHSCOPE_API_KEY`: **Обов'язково** - API ключ DashScope для доступу до Qwen3 ASR API
|
||||
- Отримати ключ: https://dashscope.console.aliyun.com/
|
||||
- Встановити: `export DASHSCOPE_API_KEY="your-api-key"`
|
||||
|
||||
### Отримання API ключа DashScope
|
||||
|
||||
1. Зареєструйтеся на https://dashscope.console.aliyun.com/
|
||||
2. Створіть API ключ в розділі "API Keys"
|
||||
3. Встановіть змінну середовища `DASHSCOPE_API_KEY`
|
||||
|
||||
## Інтеграція з Gateway
|
||||
|
||||
Gateway автоматично використовує STT-сервіс для обробки голосових повідомлень з Telegram:
|
||||
|
||||
1. Користувач надсилає voice/audio/video_note
|
||||
2. Gateway завантажує файл з Telegram
|
||||
3. Gateway відправляє файл в STT-сервіс
|
||||
4. STT повертає розпізнаний текст
|
||||
5. Текст відправляється в DAGI Router як звичайне текстове повідомлення
|
||||
|
||||
## Встановлення залежностей
|
||||
|
||||
### qwen3-asr-toolkit
|
||||
|
||||
```bash
|
||||
pip install qwen3-asr-toolkit
|
||||
# .env файл
|
||||
WHISPER_MODEL=base # tiny, base, small, medium, large
|
||||
WHISPER_LANGUAGE=uk # uk, en, ru, pl, de, fr
|
||||
```
|
||||
|
||||
### ffmpeg (може знадобитися для деяких форматів)
|
||||
### Docker Compose
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get install ffmpeg
|
||||
|
||||
# macOS
|
||||
brew install ffmpeg
|
||||
|
||||
# Docker
|
||||
Вже включено в Dockerfile
|
||||
```yaml
|
||||
environment:
|
||||
- WHISPER_MODEL=base
|
||||
- WHISPER_LANGUAGE=uk
|
||||
- LOG_LEVEL=INFO
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Помилка: "qwen3_asr_toolkit not available"
|
||||
|
||||
Встановіть бібліотеку:
|
||||
```bash
|
||||
pip install qwen3-asr-toolkit
|
||||
```
|
||||
|
||||
### Помилка: "DASHSCOPE_API_KEY not configured"
|
||||
|
||||
Встановіть змінну середовища:
|
||||
```bash
|
||||
export DASHSCOPE_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
Або додайте в `docker-compose.yml`:
|
||||
```yaml
|
||||
environment:
|
||||
- DASHSCOPE_API_KEY=${DASHSCOPE_API_KEY}
|
||||
```
|
||||
|
||||
### Помилка: "ffmpeg not found"
|
||||
|
||||
Встановіть ffmpeg (див. вище). Більшість форматів обробляються без ffmpeg, але деякі можуть його потребувати.
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
apt-get install ffmpeg
|
||||
|
||||
# macOS
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
### Помилка: "torch not compatible"
|
||||
|
||||
```bash
|
||||
# Переінсталювати PyTorch
|
||||
pip uninstall torch torchaudio
|
||||
pip install torch==2.1.0 torchaudio==2.1.0
|
||||
```
|
||||
|
||||
### Помилка: "Whisper model not found"
|
||||
|
||||
```bash
|
||||
# Завантажити модель вручну
|
||||
python -c "import whisper; whisper.load_model('base')"
|
||||
```
|
||||
|
||||
## Метрики
|
||||
|
||||
- **Endpoint:** `http://localhost:8895/metrics` (TODO)
|
||||
- **Prometheus:** Інтеграція заплановано
|
||||
- **Grafana:** Dashboard заплановано
|
||||
|
||||
## Статус
|
||||
|
||||
- ✅ Базова функціональність
|
||||
- ✅ Docker підтримка
|
||||
- ✅ Whisper AI інтеграція
|
||||
- ⚠️ Потребує тестування
|
||||
- 🔄 Frontend інтеграція (наступний крок)
|
||||
|
||||
## Автор
|
||||
|
||||
DAARION Team - 2025
|
||||
|
||||
254
services/stt-service/app/main.py
Normal file
254
services/stt-service/app/main.py
Normal file
@@ -0,0 +1,254 @@
|
||||
"""
|
||||
STT Service - Speech-to-Text для DAARION
|
||||
Конвертує аудіо файли в текст використовуючи Whisper AI
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, HTTPException, UploadFile, File
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
import base64
|
||||
from typing import Optional
|
||||
import subprocess
|
||||
import json
|
||||
|
||||
# Logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
app = FastAPI(
|
||||
title="STT Service",
|
||||
description="Speech-to-Text Service для DAARION (Whisper AI)",
|
||||
version="1.0.0"
|
||||
)
|
||||
|
||||
# CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Конфігурація
|
||||
WHISPER_MODEL = os.getenv("WHISPER_MODEL", "base") # tiny, base, small, medium, large
|
||||
LANGUAGE = os.getenv("WHISPER_LANGUAGE", "uk") # ukrainian
|
||||
|
||||
class STTRequest(BaseModel):
|
||||
audio: str # base64 encoded audio
|
||||
language: Optional[str] = "uk"
|
||||
model: Optional[str] = "base"
|
||||
|
||||
class STTResponse(BaseModel):
|
||||
text: str
|
||||
language: str
|
||||
duration: float
|
||||
model: str
|
||||
confidence: Optional[float] = None
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
"""Health check"""
|
||||
return {
|
||||
"service": "STT Service",
|
||||
"status": "running",
|
||||
"model": WHISPER_MODEL,
|
||||
"language": LANGUAGE,
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
"""Health check endpoint"""
|
||||
try:
|
||||
# Перевірити чи Whisper доступний
|
||||
result = subprocess.run(
|
||||
["whisper", "--help"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5
|
||||
)
|
||||
whisper_available = result.returncode == 0
|
||||
|
||||
return {
|
||||
"status": "healthy" if whisper_available else "degraded",
|
||||
"whisper": "available" if whisper_available else "unavailable",
|
||||
"model": WHISPER_MODEL
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"Health check failed: {e}")
|
||||
return {
|
||||
"status": "unhealthy",
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
@app.post("/api/stt", response_model=STTResponse)
|
||||
async def speech_to_text(request: STTRequest):
|
||||
"""
|
||||
Конвертує аудіо в текст
|
||||
|
||||
Body:
|
||||
{
|
||||
"audio": "data:audio/webm;base64,...",
|
||||
"language": "uk",
|
||||
"model": "base"
|
||||
}
|
||||
"""
|
||||
try:
|
||||
logger.info("📥 Received STT request")
|
||||
|
||||
# Декодувати base64 audio
|
||||
audio_data = request.audio
|
||||
if ',' in audio_data:
|
||||
audio_data = audio_data.split(',')[1]
|
||||
|
||||
audio_bytes = base64.b64decode(audio_data)
|
||||
logger.info(f"📊 Audio size: {len(audio_bytes)} bytes")
|
||||
|
||||
# Зберегти у тимчасовий файл
|
||||
with tempfile.NamedTemporaryFile(suffix='.webm', delete=False) as temp_audio:
|
||||
temp_audio.write(audio_bytes)
|
||||
audio_path = temp_audio.name
|
||||
|
||||
try:
|
||||
# Запустити Whisper
|
||||
model = request.model or WHISPER_MODEL
|
||||
language = request.language or LANGUAGE
|
||||
|
||||
logger.info(f"🎤 Running Whisper (model={model}, language={language})")
|
||||
|
||||
# Whisper CLI команда
|
||||
cmd = [
|
||||
"whisper",
|
||||
audio_path,
|
||||
"--model", model,
|
||||
"--language", language,
|
||||
"--output_format", "json",
|
||||
"--output_dir", tempfile.gettempdir()
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
error_msg = result.stderr or "Whisper failed"
|
||||
logger.error(f"❌ Whisper error: {error_msg}")
|
||||
raise HTTPException(status_code=500, detail=f"Whisper error: {error_msg}")
|
||||
|
||||
# Прочитати результат
|
||||
json_path = audio_path.replace('.webm', '.json')
|
||||
with open(json_path, 'r', encoding='utf-8') as f:
|
||||
whisper_result = json.load(f)
|
||||
|
||||
text = whisper_result.get('text', '').strip()
|
||||
|
||||
# Очистити тимчасові файли
|
||||
os.unlink(audio_path)
|
||||
if os.path.exists(json_path):
|
||||
os.unlink(json_path)
|
||||
|
||||
logger.info(f"✅ Transcribed: '{text[:50]}...'")
|
||||
|
||||
return STTResponse(
|
||||
text=text,
|
||||
language=language,
|
||||
duration=0.0, # TODO: отримати з Whisper
|
||||
model=model,
|
||||
confidence=None
|
||||
)
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
os.unlink(audio_path)
|
||||
raise HTTPException(status_code=408, detail="Whisper timeout")
|
||||
except Exception as e:
|
||||
if os.path.exists(audio_path):
|
||||
os.unlink(audio_path)
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ STT error: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@app.post("/api/stt/upload")
|
||||
async def stt_upload(file: UploadFile = File(...)):
|
||||
"""
|
||||
Конвертує завантажений аудіо файл в текст
|
||||
|
||||
Form-data:
|
||||
- file: audio file (webm, mp3, wav, m4a)
|
||||
"""
|
||||
try:
|
||||
logger.info(f"📥 Received file upload: {file.filename}")
|
||||
|
||||
# Зберегти у тимчасовий файл
|
||||
with tempfile.NamedTemporaryFile(suffix=os.path.splitext(file.filename)[1], delete=False) as temp_audio:
|
||||
content = await file.read()
|
||||
temp_audio.write(content)
|
||||
audio_path = temp_audio.name
|
||||
|
||||
logger.info(f"📊 File size: {len(content)} bytes")
|
||||
|
||||
try:
|
||||
# Запустити Whisper
|
||||
cmd = [
|
||||
"whisper",
|
||||
audio_path,
|
||||
"--model", WHISPER_MODEL,
|
||||
"--language", LANGUAGE,
|
||||
"--output_format", "json",
|
||||
"--output_dir", tempfile.gettempdir()
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
error_msg = result.stderr or "Whisper failed"
|
||||
logger.error(f"❌ Whisper error: {error_msg}")
|
||||
raise HTTPException(status_code=500, detail=f"Whisper error: {error_msg}")
|
||||
|
||||
# Прочитати результат
|
||||
json_path = audio_path.replace(os.path.splitext(audio_path)[1], '.json')
|
||||
with open(json_path, 'r', encoding='utf-8') as f:
|
||||
whisper_result = json.load(f)
|
||||
|
||||
text = whisper_result.get('text', '').strip()
|
||||
|
||||
# Очистити тимчасові файли
|
||||
os.unlink(audio_path)
|
||||
if os.path.exists(json_path):
|
||||
os.unlink(json_path)
|
||||
|
||||
logger.info(f"✅ Transcribed: '{text[:50]}...'")
|
||||
|
||||
return {
|
||||
"text": text,
|
||||
"filename": file.filename,
|
||||
"language": LANGUAGE,
|
||||
"model": WHISPER_MODEL
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
if os.path.exists(audio_path):
|
||||
os.unlink(audio_path)
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Upload STT error: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8895)
|
||||
|
||||
20
services/stt-service/docker-compose.yml
Normal file
20
services/stt-service/docker-compose.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
stt-service:
|
||||
build: .
|
||||
container_name: dagi-stt-service
|
||||
ports:
|
||||
- "8895:8895"
|
||||
environment:
|
||||
- WHISPER_MODEL=base
|
||||
- WHISPER_LANGUAGE=uk
|
||||
volumes:
|
||||
- ./app:/app/app
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8895/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
fastapi==0.104.1
|
||||
uvicorn[standard]==0.24.0
|
||||
python-multipart==0.0.6
|
||||
qwen3-asr-toolkit>=1.0.0
|
||||
|
||||
openai-whisper==20231117
|
||||
torch==2.1.0
|
||||
torchaudio==2.1.0
|
||||
|
||||
Reference in New Issue
Block a user