Compare commits

...

3 Commits

Author SHA1 Message Date
Petr 7378820649 (feat) добавлена интеграция с sentry 2026-09-18 16:27:40 +05:00
Petr d871f1fe32 (fix) 2026-09-02 17:38:24 +05:00
Petr 42df024940 (fix) не брать в обработку файлы c префиксами _r, _t 2026-09-01 15:53:55 +05:00
7 changed files with 99 additions and 33 deletions
+8
View File
@@ -15,3 +15,11 @@
# HOST=0.0.0.0
# PORT=8000
# RELOAD=True
# Sentry Configuration
# SENTRY_DSN=https://a5da7e2cd15fd85150e012a6d62fc13c@sentry.pirogov.ai/26
# SENTRY_ENVIRONMENT=production
# SENTRY_TRACES_SAMPLE_RATE=0.2
# SENTRY_PROFILES_SAMPLE_RATE=0.1
# SENTRY_SEND_DEFAULT_PII=true
+8
View File
@@ -26,3 +26,11 @@ FILESAPTH = audiofiles
GIGAAM_API_URL = http://host.docker.internal:5002
CALLS_WEB_CLIENT_URL=http://web_client_test:8642
AUDIO_FILES_PATH_TEST=D:\SpeechAnalitics\audiofiles
# Sentry Configuration
SENTRY_DSN=https://a5da7e2cd15fd85150e012a6d62fc13c@sentry.pirogov.ai/26
SENTRY_ENVIRONMENT=test
SENTRY_TRACES_SAMPLE_RATE=0.2
SENTRY_PROFILES_SAMPLE_RATE=0.1
SENTRY_SEND_DEFAULT_PII=true
+7
View File
@@ -44,3 +44,10 @@ AUTO_RESTORE_DELAY = int(os.getenv("AUTO_RESTORE_DELAY", "5")) # Задержк
# Recognition retry policy (FileAudioAPI side)
MAX_RECOGNITION_ATTEMPTS = int(os.getenv("MAX_RECOGNITION_ATTEMPTS", "3"))
# Sentry Configuration
SENTRY_DSN = os.getenv("SENTRY_DSN", "")
SENTRY_ENVIRONMENT = os.getenv("SENTRY_ENVIRONMENT", "production")
SENTRY_TRACES_SAMPLE_RATE = float(os.getenv("SENTRY_TRACES_SAMPLE_RATE", "0.2"))
SENTRY_PROFILES_SAMPLE_RATE = float(os.getenv("SENTRY_PROFILES_SAMPLE_RATE", "0.1"))
SENTRY_SEND_DEFAULT_PII = os.getenv("SENTRY_SEND_DEFAULT_PII", "true").lower() == "true"
+23 -13
View File
@@ -9,6 +9,7 @@ from apiApp.database import get_db
from apiApp.database.Audio import Audio
from apiApp.database.AiConclusion import AiConclusion
from datetime import datetime
import time
import logging
import requests
from apiApp.config import WEBHOOK_ENDPOINT, WEBHOOK_API_KEY, CALLS_WEB_CLIENT_URL
@@ -168,8 +169,7 @@ async def save_ai_conclusion(request: AiConclusionRequest, db: Session = Depends
if (audio.sourse or "").lower() == "external" and request.callback_url:
_send_callback(request.callback_url, audio, conclusion_data)
# Отправляем webhook в Calls_WEB_Client_main для анализа
try:
# Отправляем webhook в Calls_WEB_Client_main для анализа (с retry при перезагрузке контейнера)
if (audio.sourse or "").lower() != "external":
logger.info(f"📤 Отправка webhook в Calls_WEB_Client_main для {request.filename}")
@@ -182,25 +182,35 @@ async def save_ai_conclusion(request: AiConclusionRequest, db: Session = Depends
"processing_time_seconds": request.processing_time_seconds
}
max_retries = 5
delays = [2, 4, 8, 15, 30]
delivered = False
for attempt in range(1, max_retries + 1):
try:
webhook_response = requests.post(
WEBHOOK_ENDPOINT,
json=webhook_payload,
headers={"X-Webhook-Key": WEBHOOK_API_KEY},
timeout=30
)
if webhook_response.status_code == 200:
logger.info(f"✅ Webhook успешно отправлен для {request.filename}")
if webhook_response.status_code in (200, 201):
logger.info(f"✅ Webhook успешно отправлен для {request.filename} (попытка {attempt}/{max_retries})")
delivered = True
break
else:
logger.warning(f"⚠️ Webhook вернул статус {webhook_response.status_code}")
logger.warning(f"Response: {webhook_response.text}")
except requests.exceptions.ConnectionError:
logger.error(f"❌ Не удалось подключиться к Calls_WEB_Client_main webhook: {WEBHOOK_ENDPOINT}")
except requests.exceptions.Timeout:
logger.warning(f"⚠️ Таймаут при отправке webhook для {request.filename}")
logger.warning(f"⚠️ Webhook вернул статус {webhook_response.status_code} (попытка {attempt}/{max_retries})")
logger.warning(f"Response: {webhook_response.text[:200]}")
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as e:
logger.warning(f"⏳ Calls_WEB_Client_main временно недоступен ({type(e).__name__}), попытка {attempt}/{max_retries}...")
except Exception as e:
logger.error(f"❌ Ошибка при отправке webhook: {e}")
logger.error(f"❌ Непредвиденная ошибка при отправке webhook (попытка {attempt}): {e}")
if attempt < max_retries:
time.sleep(delays[attempt - 1])
if not delivered:
logger.error(f"❌ Не удалось доставить webhook для {request.filename} после {max_retries} попыток")
return AiConclusionResponse(
success=True,
+9 -1
View File
@@ -11,7 +11,15 @@ class Loader():
self.call_types = ['in']
self.loaded_files = [] # Список загруженных файлов
def filter_call(self, filename: str):
def filter_call(self, filename: str) -> bool:
if not filename or not filename.endswith('.wav'):
return False
# Пропускаем раздельные каналы звонков (_r = receive/клиент, _t = transmit/оператор)
name_without_ext = filename[:-4]
if name_without_ext.endswith(('_r', '_t', '_in', '_out', '-r', '-t')):
return False
if filename.split("-")[0] in self.call_types:
return True
return False
+25 -1
View File
@@ -5,7 +5,10 @@ from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy.exc import SQLAlchemyError
import logging
from apiApp.config import APP_TITLE, APP_VERSION, API_V1_PREFIX, UPLOAD_FOLDER, DATABASE_URL, PORT, HOST
from apiApp.config import (
APP_TITLE, APP_VERSION, API_V1_PREFIX, UPLOAD_FOLDER, DATABASE_URL, PORT, HOST,
SENTRY_DSN, SENTRY_ENVIRONMENT, SENTRY_TRACES_SAMPLE_RATE, SENTRY_PROFILES_SAMPLE_RATE, SENTRY_SEND_DEFAULT_PII
)
from apiApp.database import engine, Base
from apiApp.routers import audio_router, recognition_router
from apiApp.routers.ai_conclusion_router import ai_conclusion_router
@@ -19,6 +22,20 @@ print("✅ audio_management_router imported successfully")
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Инициализация Sentry SDK до создания FastAPI приложения
if SENTRY_DSN:
import sentry_sdk
sentry_sdk.init(
dsn=SENTRY_DSN,
traces_sample_rate=SENTRY_TRACES_SAMPLE_RATE,
profiles_sample_rate=SENTRY_PROFILES_SAMPLE_RATE,
send_default_pii=SENTRY_SEND_DEFAULT_PII,
environment=SENTRY_ENVIRONMENT,
release=f"speech-fileaudio-api@{APP_VERSION}",
)
logger.info("✅ Sentry initialized for speech-fileaudio-api")
# Создание FastAPI приложения
app = FastAPI(
title=APP_TITLE,
@@ -28,6 +45,13 @@ app = FastAPI(
openapi_url=f"{API_V1_PREFIX}/openapi.json"
)
@app.get("/sentry-debug")
async def trigger_sentry_debug():
"""Эндпоинт для проверки интеграции Sentry"""
division_by_zero = 1 / 0
return {"status": "ok"}
# CORS middleware
app.add_middleware(
CORSMiddleware,
+1
View File
@@ -7,3 +7,4 @@ aiofiles==24.1.0
psycopg2-binary
paramiko
requests
sentry-sdk[fastapi]>=2.0.0