model-path-fix
Some checks failed
Deploy on push / deploy (push) Failing after 1m14s

This commit is contained in:
2025-09-01 16:10:10 +03:00
parent a1e4d0d391
commit 9daade05c0
6 changed files with 103 additions and 53 deletions

View File

@@ -130,6 +130,13 @@
- Убраны избыточные логи из `precache_topics_followers`
- Более чистое и информативное логирование процесса кеширования
### 🚨 Исправлено
- **Запуск приложения**: Исправлена блокировка при старте из-за SentenceTransformers
- Переведен импорт `sentence_transformers` на lazy loading
- Модель загружается только при первом использовании поиска
- Исправлена ошибка deprecated `TRANSFORMERS_CACHE` на `HF_HOME`
- Приложение теперь запускается мгновенно без ожидания загрузки ML моделей
## [0.9.13] - 2025-08-27
### 🗑️ Удалено

View File

@@ -209,11 +209,6 @@ class MockInfo:
}
self.field_nodes = [MockFieldNode(requested_fields or [])]
# Патчинг зависимостей
@patch('storage.redis.aioredis')
def test_redis_connection(mock_aioredis):
# Тест логики
pass
```
### Асинхронные тесты

View File

@@ -57,7 +57,7 @@ dependencies = [
# https://docs.astral.sh/uv/concepts/dependencies/#development-dependencies
[dependency-groups]
dev = [
"fakeredis[aioredis]",
"fakeredis",
"pytest",
"pytest-asyncio",
"pytest-cov",
@@ -68,7 +68,7 @@ dev = [
]
test = [
"fakeredis[aioredis]",
"fakeredis",
"pytest",
"pytest-asyncio",
"pytest-cov",

View File

@@ -15,8 +15,10 @@ def get_models_cache_dir() -> str:
"""Определяет лучшую папку для кеша моделей"""
# Пробуем /dump если доступен для записи
dump_path = Path("/dump")
print(f"🔍 Checking /dump - exists: {dump_path.exists()}, writable: {os.access('/dump', os.W_OK) if dump_path.exists() else 'N/A'}")
print(
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"
try:

View File

@@ -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

6
uv.lock generated
View File

@@ -400,7 +400,7 @@ wheels = [
[[package]]
name = "discours-core"
version = "0.9.14"
version = "0.9.18"
source = { editable = "." }
dependencies = [
{ name = "ariadne" },
@@ -492,7 +492,7 @@ requires-dist = [
[package.metadata.requires-dev]
dev = [
{ name = "fakeredis", extras = ["aioredis"] },
{ name = "fakeredis" },
{ name = "mypy" },
{ name = "playwright" },
{ name = "pytest" },
@@ -506,7 +506,7 @@ lint = [
{ name = "ruff" },
]
test = [
{ name = "fakeredis", extras = ["aioredis"] },
{ name = "fakeredis" },
{ name = "playwright" },
{ name = "pytest" },
{ name = "pytest-asyncio" },