This commit is contained in:
@@ -9,11 +9,12 @@ from typing import Any, Dict, List
|
||||
|
||||
import muvera
|
||||
import numpy as np
|
||||
from sentence_transformers import SentenceTransformer
|
||||
|
||||
from settings import MUVERA_INDEX_NAME, SEARCH_MAX_BATCH_SIZE, SEARCH_PREFETCH_SIZE
|
||||
from utils.logger import root_logger as logger
|
||||
|
||||
# Отложенный импорт SentenceTransformer для избежания блокировки запуска
|
||||
SentenceTransformer = None
|
||||
primary_model = "paraphrase-multilingual-MiniLM-L12-v2"
|
||||
|
||||
|
||||
@@ -22,7 +23,9 @@ def get_models_cache_dir() -> str:
|
||||
"""Определяет лучшую папку для кеша моделей"""
|
||||
# Пробуем /dump если доступен для записи
|
||||
dump_path = Path("/dump")
|
||||
logger.info(f"🔍 Checking /dump - exists: {dump_path.exists()}, writable: {os.access('/dump', os.W_OK) if dump_path.exists() else 'N/A'}")
|
||||
logger.info(
|
||||
f"🔍 Checking /dump - exists: {dump_path.exists()}, writable: {os.access('/dump', os.W_OK) if dump_path.exists() else 'N/A'}"
|
||||
)
|
||||
|
||||
if dump_path.exists() and os.access("/dump", os.W_OK):
|
||||
cache_dir = "/dump/huggingface"
|
||||
@@ -41,13 +44,28 @@ def get_models_cache_dir() -> str:
|
||||
|
||||
|
||||
MODELS_CACHE_DIR = get_models_cache_dir()
|
||||
os.environ.setdefault("TRANSFORMERS_CACHE", MODELS_CACHE_DIR)
|
||||
# Используем HF_HOME вместо устаревшего TRANSFORMERS_CACHE
|
||||
os.environ.setdefault("HF_HOME", MODELS_CACHE_DIR)
|
||||
|
||||
# Global collection for background tasks
|
||||
background_tasks: List[asyncio.Task] = []
|
||||
|
||||
|
||||
def _lazy_import_sentence_transformers():
|
||||
"""🔄 Lazy import SentenceTransformer для избежания блокировки старта приложения"""
|
||||
global SentenceTransformer # noqa: PLW0603
|
||||
if SentenceTransformer is None:
|
||||
try:
|
||||
from sentence_transformers import SentenceTransformer as SentenceTransformerClass
|
||||
|
||||
SentenceTransformer = SentenceTransformerClass
|
||||
logger.info("✅ SentenceTransformer импортирован успешно")
|
||||
except ImportError as e:
|
||||
logger.error(f"❌ Не удалось импортировать SentenceTransformer: {e}")
|
||||
SentenceTransformer = None
|
||||
return SentenceTransformer
|
||||
|
||||
|
||||
class MuveraWrapper:
|
||||
"""🔍 Real vector search with SentenceTransformers + FDE encoding"""
|
||||
|
||||
@@ -60,42 +78,10 @@ class MuveraWrapper:
|
||||
self.documents: Dict[str, Dict[str, Any]] = {} # Simple in-memory storage for demo
|
||||
self.embeddings: Dict[str, np.ndarray | None] = {} # Store encoded embeddings
|
||||
|
||||
# 🚀 Инициализируем реальную модель эмбедингов с локальным кешом
|
||||
try:
|
||||
logger.info(f"💾 Using models cache directory: {MODELS_CACHE_DIR}")
|
||||
|
||||
# Проверяем наличие основной модели
|
||||
is_cached = self._is_model_cached(primary_model)
|
||||
if is_cached:
|
||||
logger.info(f"🔍 Found cached model: {primary_model}")
|
||||
else:
|
||||
logger.info(f"🔽 Downloading model: {primary_model}")
|
||||
|
||||
# Используем многоязычную модель, хорошо работающую с русским
|
||||
self.encoder = SentenceTransformer(
|
||||
primary_model,
|
||||
cache_folder=MODELS_CACHE_DIR,
|
||||
local_files_only=is_cached, # Не скачиваем если уже есть в кеше
|
||||
)
|
||||
logger.info("🔍 SentenceTransformer model loaded successfully")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load primary SentenceTransformer: {e}")
|
||||
# Fallback - простая модель
|
||||
try:
|
||||
fallback_model = "all-MiniLM-L6-v2"
|
||||
is_fallback_cached = self._is_model_cached(fallback_model)
|
||||
if is_fallback_cached:
|
||||
logger.info(f"🔍 Found cached fallback model: {fallback_model}")
|
||||
else:
|
||||
logger.info(f"🔽 Downloading fallback model: {fallback_model}")
|
||||
|
||||
self.encoder = SentenceTransformer(
|
||||
fallback_model, cache_folder=MODELS_CACHE_DIR, local_files_only=is_fallback_cached
|
||||
)
|
||||
logger.info("🔍 Fallback SentenceTransformer model loaded")
|
||||
except Exception:
|
||||
logger.error("Failed to load any SentenceTransformer model")
|
||||
self.encoder = None
|
||||
# 🚀 Откладываем инициализацию модели до первого использования
|
||||
logger.info("🔄 MuveraWrapper инициализирован - модель будет загружена при первом использовании")
|
||||
self.encoder = None
|
||||
self._model_loaded = False
|
||||
|
||||
def _is_model_cached(self, model_name: str) -> bool:
|
||||
"""🔍 Проверяет наличие модели в кеше"""
|
||||
@@ -128,6 +114,60 @@ class MuveraWrapper:
|
||||
logger.debug(f"Error checking model cache for {model_name}: {e}")
|
||||
return False
|
||||
|
||||
def _ensure_model_loaded(self) -> bool:
|
||||
"""🔄 Убеждаемся что модель загружена (lazy loading)"""
|
||||
if self._model_loaded:
|
||||
return self.encoder is not None
|
||||
|
||||
# Импортируем SentenceTransformer при первой необходимости
|
||||
sentence_transformer_class = _lazy_import_sentence_transformers()
|
||||
if sentence_transformer_class is None:
|
||||
logger.error("❌ SentenceTransformer недоступен")
|
||||
return False
|
||||
|
||||
try:
|
||||
logger.info(f"💾 Using models cache directory: {MODELS_CACHE_DIR}")
|
||||
|
||||
# Проверяем наличие основной модели
|
||||
is_cached = self._is_model_cached(primary_model)
|
||||
if is_cached:
|
||||
logger.info(f"🔍 Found cached model: {primary_model}")
|
||||
else:
|
||||
logger.info(f"🔽 Downloading model: {primary_model}")
|
||||
|
||||
# Используем многоязычную модель, хорошо работающую с русским
|
||||
self.encoder = sentence_transformer_class(
|
||||
primary_model,
|
||||
cache_folder=MODELS_CACHE_DIR,
|
||||
local_files_only=is_cached, # Не скачиваем если уже есть в кеше
|
||||
)
|
||||
logger.info("🔍 SentenceTransformer model loaded successfully")
|
||||
self._model_loaded = True
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to load primary SentenceTransformer: {e}")
|
||||
# Fallback - простая модель
|
||||
try:
|
||||
fallback_model = "all-MiniLM-L6-v2"
|
||||
is_fallback_cached = self._is_model_cached(fallback_model)
|
||||
if is_fallback_cached:
|
||||
logger.info(f"🔍 Found cached fallback model: {fallback_model}")
|
||||
else:
|
||||
logger.info(f"🔽 Downloading fallback model: {fallback_model}")
|
||||
|
||||
self.encoder = sentence_transformer_class(
|
||||
fallback_model, cache_folder=MODELS_CACHE_DIR, local_files_only=is_fallback_cached
|
||||
)
|
||||
logger.info("🔍 Fallback SentenceTransformer model loaded")
|
||||
self._model_loaded = True
|
||||
return True
|
||||
except Exception:
|
||||
logger.error("Failed to load any SentenceTransformer model")
|
||||
self.encoder = None
|
||||
self._model_loaded = True # Помечаем как попытка завершена
|
||||
return False
|
||||
|
||||
async def async_init(self) -> None:
|
||||
"""🔄 Асинхронная инициализация - восстановление индекса из файла"""
|
||||
try:
|
||||
@@ -153,7 +193,12 @@ class MuveraWrapper:
|
||||
|
||||
async def search(self, query: str, limit: int) -> List[Dict[str, Any]]:
|
||||
"""🔍 Real vector search using SentenceTransformers + FDE encoding"""
|
||||
if not query.strip() or not self.encoder:
|
||||
if not query.strip():
|
||||
return []
|
||||
|
||||
# Загружаем модель при первом использовании
|
||||
if not self._ensure_model_loaded():
|
||||
logger.warning("🔍 Search unavailable - model not loaded")
|
||||
return []
|
||||
|
||||
try:
|
||||
@@ -194,7 +239,8 @@ class MuveraWrapper:
|
||||
|
||||
async def index(self, documents: List[Dict[str, Any]], silent: bool = False) -> None:
|
||||
"""🚀 Index documents using real SentenceTransformers + FDE encoding"""
|
||||
if not self.encoder:
|
||||
# Загружаем модель при первом использовании
|
||||
if not self._ensure_model_loaded():
|
||||
if not silent:
|
||||
logger.warning("🔍 No encoder available for indexing")
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user