* Add migration
continuous-integration/drone/push Build is passing

* Delete legacy from bot
* Clear old models
* Единый http клиент
* РАГ полечен
This commit is contained in:
2025-12-24 13:44:52 +03:00
parent 1ce1c23d10
commit 8bdacb4f7a
19 changed files with 200 additions and 565 deletions
@@ -0,0 +1,33 @@
"""Remove unused embeddings table
Revision ID: 003
Revises: 002
Create Date: 2024-12-24 12:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
revision = '003'
down_revision = '002'
branch_labels = None
depends_on = None
def upgrade() -> None:
op.drop_table('embeddings')
def downgrade() -> None:
op.create_table(
'embeddings',
sa.Column('embedding_id', sa.dialects.postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('document_id', sa.dialects.postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('embedding', sa.dialects.postgresql.JSON(astext_type=sa.Text()), nullable=True),
sa.Column('model_version', sa.String(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['document_id'], ['documents.document_id'], ),
sa.PrimaryKeyConstraint('embedding_id')
)
@@ -8,6 +8,7 @@ from src.domain.repositories.document_repository import IDocumentRepository
from src.domain.repositories.collection_repository import ICollectionRepository
from src.domain.repositories.collection_access_repository import ICollectionAccessRepository
from src.application.services.document_parser_service import DocumentParserService
from src.application.services.rag_service import RAGService
from src.shared.exceptions import NotFoundError, ForbiddenError
@@ -19,12 +20,14 @@ class DocumentUseCases:
document_repository: IDocumentRepository,
collection_repository: ICollectionRepository,
access_repository: ICollectionAccessRepository,
parser_service: DocumentParserService
parser_service: DocumentParserService,
rag_service: Optional[RAGService] = None
):
self.document_repository = document_repository
self.collection_repository = collection_repository
self.access_repository = access_repository
self.parser_service = parser_service
self.rag_service = rag_service
async def _check_collection_access(self, user_id: UUID, collection) -> bool:
"""Проверить доступ пользователя к коллекции"""
@@ -64,7 +67,7 @@ class DocumentUseCases:
filename: str,
user_id: UUID
) -> Document:
"""Загрузить и распарсить документ"""
"""Загрузить и распарсить документ, затем автоматически проиндексировать"""
collection = await self.collection_repository.get_by_id(collection_id)
if not collection:
raise NotFoundError(f"Коллекция {collection_id} не найдена")
@@ -81,7 +84,15 @@ class DocumentUseCases:
content=content,
metadata={"filename": filename}
)
return await self.document_repository.create(document)
document = await self.document_repository.create(document)
if self.rag_service:
try:
await self.rag_service.index_document(document)
except Exception as e:
print(f"Ошибка при автоматической индексации документа {document.document_id}: {e}")
return document
async def get_document(self, document_id: UUID) -> Document:
"""Получить документ по ID"""
-25
View File
@@ -1,25 +0,0 @@
"""
Доменная сущность Embedding
"""
from datetime import datetime
from uuid import UUID, uuid4
from typing import Any
class Embedding:
"""Эмбеддинг документа"""
def __init__(
self,
document_id: UUID,
embedding: list[float] | None = None,
model_version: str = "",
embedding_id: UUID | None = None,
created_at: datetime | None = None
):
self.embedding_id = embedding_id or uuid4()
self.document_id = document_id
self.embedding = embedding or []
self.model_version = model_version
self.created_at = created_at or datetime.utcnow()
@@ -53,19 +53,6 @@ class DocumentModel(Base):
document_metadata = Column("metadata", JSON, nullable=True, default={})
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
collection = relationship("CollectionModel", back_populates="documents")
embeddings = relationship("EmbeddingModel", back_populates="document", cascade="all, delete-orphan")
class EmbeddingModel(Base):
"""Модель эмбеддинга (заглушка)"""
__tablename__ = "embeddings"
embedding_id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
document_id = Column(UUID(as_uuid=True), ForeignKey("documents.document_id"), nullable=False)
embedding = Column(JSON, nullable=True)
model_version = Column(String, nullable=True)
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
document = relationship("DocumentModel", back_populates="embeddings")
class ConversationModel(Base):
+1 -18
View File
@@ -1,5 +1,5 @@
"""
API для RAG: индексация документов и ответы на вопросы
API для RAG: ответы на вопросы
"""
from fastapi import APIRouter, status, Request
from typing import Annotated
@@ -9,30 +9,13 @@ from src.presentation.middleware.auth_middleware import get_current_user
from src.presentation.schemas.rag_schemas import (
QuestionRequest,
RAGAnswer,
IndexDocumentRequest,
IndexDocumentResponse,
)
from src.application.use_cases.rag_use_cases import RAGUseCases
from src.domain.entities.user import User
router = APIRouter(prefix="/rag", tags=["rag"])
@router.post("/index", response_model=IndexDocumentResponse, status_code=status.HTTP_200_OK)
@inject
async def index_document(
body: IndexDocumentRequest,
request: Request,
user_repo: Annotated[IUserRepository, FromDishka()],
use_cases: Annotated[RAGUseCases, FromDishka()],
):
"""Индексирование идет через чанкирование, далее эмбеддинг и загрузка в векторную бд"""
current_user = await get_current_user(request, user_repo)
result = await use_cases.index_document(body.document_id)
return IndexDocumentResponse(**result)
@router.post("/question", response_model=RAGAnswer, status_code=status.HTTP_200_OK)
@inject
async def ask_question(
@@ -26,10 +26,3 @@ class RAGAnswer(BaseModel):
usage: dict[str, Any] = {}
class IndexDocumentRequest(BaseModel):
document_id: UUID
class IndexDocumentResponse(BaseModel):
chunks_indexed: int
+3 -2
View File
@@ -152,9 +152,10 @@ class UseCaseProvider(Provider):
document_repo: IDocumentRepository,
collection_repo: ICollectionRepository,
access_repo: ICollectionAccessRepository,
parser_service: DocumentParserService
parser_service: DocumentParserService,
rag_service: RAGService
) -> DocumentUseCases:
return DocumentUseCases(document_repo, collection_repo, access_repo, parser_service)
return DocumentUseCases(document_repo, collection_repo, access_repo, parser_service, rag_service)
@provide(scope=Scope.REQUEST)
def get_conversation_use_cases(