FAISS-индекс — файловый синглтон в RAM одного воркера (save на каждую запись, без блокировок, без HA, не горизонтален). Добавлен альтернативный бэкенд Qdrant с тем же classmethod-интерфейсом, выбор через VECTOR_BACKEND — аддитивно и безопасно: дефолт остаётся faiss, ничего не ломается, пока не переключат. - app/qdrant_manager.py — search/add_vectors/save(no-op)/total_vectors поверх qdrant-client (коллекция Cosine, id точки = doc_id, upsert идемпотентен); - app/vector_store.py — get_backend() по настройке; задачи search/plagiarism переведены на него (больше не импортируют FAISSManager напрямую); - app/migrate_faiss_to_qdrant.py — перелив существующих векторов (reconstruct из IndexIDMap2 → upsert), идемпотентно; - docker-compose.prod.yml — сервис qdrant под профилем `qdrant` (по умолчанию не поднимается, ресурсов не ест) + volume; README — раздел про переключение. Тесты (9) гоняют QdrantManager против ВСТРОЕННОГО Qdrant (qdrant-client :memory:, не моки) + диспетчеризацию бэкенда. Всего тестов: 82 (indexer 24, gost 24, gpu 34). Плюсы Qdrant активируются только после явного переключения + миграции. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
72 lines
2.4 KiB
Python
72 lines
2.4 KiB
Python
"""Конфигурация GPU воркера."""
|
|
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""Настройки GPU воркера."""
|
|
|
|
model_config = SettingsConfigDict(
|
|
env_file=".env",
|
|
env_file_encoding="utf-8",
|
|
case_sensitive=False,
|
|
extra="ignore", # игнорировать поля общего .env, не относящиеся к воркеру
|
|
)
|
|
|
|
# PostgreSQL
|
|
POSTGRES_HOST: str = "postgres"
|
|
POSTGRES_PORT: int = 5432
|
|
POSTGRES_DB: str = "antiplagiator"
|
|
POSTGRES_USER: str = "antiplagiator"
|
|
POSTGRES_PASSWORD: str = "changeme"
|
|
|
|
# Redis
|
|
REDIS_URL: str = "redis://redis:6379/0"
|
|
|
|
# RabbitMQ
|
|
RABBITMQ_URL: str = "amqp://guest:guest@rabbitmq:5672/"
|
|
|
|
# MinIO
|
|
MINIO_ENDPOINT: str = "minio:9000"
|
|
MINIO_ACCESS_KEY: str = "minioadmin"
|
|
MINIO_SECRET_KEY: str = "changeme"
|
|
MINIO_BUCKET_DOCS: str = "documents"
|
|
|
|
# Elasticsearch
|
|
ELASTICSEARCH_URL: str = "http://elasticsearch:9200"
|
|
|
|
# Ollama
|
|
OLLAMA_URL: str = "http://ollama:11434"
|
|
|
|
# FAISS / ML
|
|
FAISS_INDEX_PATH: str = "/data/index/faiss.index"
|
|
FAISS_ID_MAP_PATH: str = "/data/index/faiss_id_map.json"
|
|
EMBED_MODEL: str = "paraphrase-multilingual-mpnet-base-v2"
|
|
EMBED_DEVICE: str = "cuda"
|
|
EMBED_BATCH_SIZE: int = 64
|
|
EMBED_DIM: int = 768 # Размерность вектора paraphrase-multilingual-mpnet-base-v2
|
|
FAISS_NLIST: int = 1024 # Количество кластеров для IVFFlat
|
|
FAISS_NPROBE: int = 64 # Количество кластеров для поиска
|
|
|
|
# Векторный бэкенд: "faiss" (файловый синглтон, дефолт) или "qdrant" (сервис,
|
|
# снимает SPOF и конкурентную запись). Переключается без изменения кода.
|
|
VECTOR_BACKEND: str = "faiss"
|
|
QDRANT_URL: str = "http://qdrant:6333"
|
|
QDRANT_COLLECTION: str = "documents"
|
|
|
|
# App
|
|
APP_URL: str = "https://academic.jze9.ru"
|
|
ENVIRONMENT: str = "development"
|
|
DEBUG: bool = False
|
|
|
|
@property
|
|
def database_url_sync(self) -> str:
|
|
"""Синхронный URL для SQLAlchemy (psycopg2)."""
|
|
return (
|
|
f"postgresql+psycopg2://{self.POSTGRES_USER}:{self.POSTGRES_PASSWORD}"
|
|
f"@{self.POSTGRES_HOST}:{self.POSTGRES_PORT}/{self.POSTGRES_DB}"
|
|
)
|
|
|
|
|
|
settings = Settings()
|