278 lines
9.4 KiB
Python
278 lines
9.4 KiB
Python
"""
|
|
Router para endpoints de operaciones con bases de datos vectoriales.
|
|
|
|
Este módulo define todos los endpoints de la API relacionados con
|
|
la gestión de colecciones y chunks en bases de datos vectoriales.
|
|
"""
|
|
|
|
import logging
|
|
from fastapi import APIRouter, HTTPException, status, Query
|
|
from typing import Optional
|
|
|
|
from app.services.vector_service import vector_service
|
|
from app.models.vector_models import (
|
|
CollectionExistsRequest,
|
|
CollectionExistsResponse,
|
|
CollectionCreateRequest,
|
|
CollectionCreateResponse,
|
|
CollectionDeleteResponse,
|
|
CollectionInfoResponse,
|
|
FileExistsInCollectionRequest,
|
|
FileExistsInCollectionResponse,
|
|
GetChunksByFileRequest,
|
|
GetChunksByFileResponse,
|
|
DeleteFileFromCollectionRequest,
|
|
DeleteFileFromCollectionResponse,
|
|
AddChunksRequest,
|
|
AddChunksResponse,
|
|
VectorDBHealthResponse,
|
|
VectorDBErrorResponse
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(
|
|
prefix="/vectors",
|
|
tags=["Vectors"],
|
|
responses={
|
|
500: {"model": VectorDBErrorResponse, "description": "Error interno del servidor"}
|
|
}
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Endpoints de Health Check
|
|
# ============================================================================
|
|
|
|
@router.get(
|
|
"/health",
|
|
response_model=VectorDBHealthResponse,
|
|
summary="Verificar estado de la base de datos vectorial",
|
|
description="Verifica que la conexión con la base de datos vectorial esté funcionando correctamente"
|
|
)
|
|
async def health_check():
|
|
"""Health check de la base de datos vectorial."""
|
|
try:
|
|
return await vector_service.health_check()
|
|
except Exception as e:
|
|
logger.error(f"Error en health check: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Error al verificar estado de la base de datos: {str(e)}"
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Endpoints de Colecciones
|
|
# ============================================================================
|
|
|
|
@router.post(
|
|
"/collections/exists",
|
|
response_model=CollectionExistsResponse,
|
|
summary="Verificar si una colección existe",
|
|
description="Verifica si existe una colección con el nombre especificado"
|
|
)
|
|
async def check_collection_exists(request: CollectionExistsRequest):
|
|
"""Verifica si una colección existe."""
|
|
try:
|
|
return await vector_service.check_collection_exists(request.collection_name)
|
|
except Exception as e:
|
|
logger.error(f"Error al verificar colección: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Error al verificar colección: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.post(
|
|
"/collections/create",
|
|
response_model=CollectionCreateResponse,
|
|
status_code=status.HTTP_201_CREATED,
|
|
summary="Crear una nueva colección",
|
|
description="Crea una nueva colección en la base de datos vectorial"
|
|
)
|
|
async def create_collection(request: CollectionCreateRequest):
|
|
"""Crea una nueva colección."""
|
|
try:
|
|
return await vector_service.create_collection(request)
|
|
except ValueError as e:
|
|
logger.warning(f"Error de validación al crear colección: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=str(e)
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error al crear colección: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Error al crear colección: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.delete(
|
|
"/collections/{collection_name}",
|
|
response_model=CollectionDeleteResponse,
|
|
summary="Eliminar una colección",
|
|
description="Elimina completamente una colección y todos sus datos"
|
|
)
|
|
async def delete_collection(collection_name: str):
|
|
"""Elimina una colección completa."""
|
|
try:
|
|
return await vector_service.delete_collection(collection_name)
|
|
except ValueError as e:
|
|
logger.warning(f"Error de validación al eliminar colección: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=str(e)
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error al eliminar colección: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Error al eliminar colección: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.get(
|
|
"/collections/{collection_name}/info",
|
|
response_model=CollectionInfoResponse,
|
|
summary="Obtener información de una colección",
|
|
description="Obtiene información detallada sobre una colección"
|
|
)
|
|
async def get_collection_info(collection_name: str):
|
|
"""Obtiene información de una colección."""
|
|
try:
|
|
info = await vector_service.get_collection_info(collection_name)
|
|
|
|
if info is None:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"Colección '{collection_name}' no encontrada"
|
|
)
|
|
|
|
return info
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error al obtener info de colección: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Error al obtener información de colección: {str(e)}"
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Endpoints de Archivos en Colecciones
|
|
# ============================================================================
|
|
|
|
@router.post(
|
|
"/files/exists",
|
|
response_model=FileExistsInCollectionResponse,
|
|
summary="Verificar si un archivo existe en una colección",
|
|
description="Verifica si un archivo específico existe en una colección"
|
|
)
|
|
async def check_file_exists_in_collection(request: FileExistsInCollectionRequest):
|
|
"""Verifica si un archivo existe en una colección."""
|
|
try:
|
|
return await vector_service.check_file_exists_in_collection(
|
|
request.collection_name,
|
|
request.file_name
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error al verificar archivo en colección: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Error al verificar archivo: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.get(
|
|
"/collections/{collection_name}/files/{file_name}/chunks",
|
|
response_model=GetChunksByFileResponse,
|
|
summary="Obtener chunks de un archivo",
|
|
description="Obtiene todos los chunks de un archivo específico en una colección"
|
|
)
|
|
async def get_chunks_by_file(
|
|
collection_name: str,
|
|
file_name: str,
|
|
limit: Optional[int] = Query(None, description="Límite de chunks a retornar")
|
|
):
|
|
"""Obtiene todos los chunks de un archivo."""
|
|
try:
|
|
return await vector_service.get_chunks_by_file(
|
|
collection_name,
|
|
file_name,
|
|
limit
|
|
)
|
|
except ValueError as e:
|
|
logger.warning(f"Error de validación al obtener chunks: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=str(e)
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error al obtener chunks: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Error al obtener chunks: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.delete(
|
|
"/collections/{collection_name}/files/{file_name}",
|
|
response_model=DeleteFileFromCollectionResponse,
|
|
summary="Eliminar un archivo de una colección",
|
|
description="Elimina todos los chunks de un archivo de una colección"
|
|
)
|
|
async def delete_file_from_collection(collection_name: str, file_name: str):
|
|
"""Elimina todos los chunks de un archivo."""
|
|
try:
|
|
return await vector_service.delete_file_from_collection(
|
|
collection_name,
|
|
file_name
|
|
)
|
|
except ValueError as e:
|
|
logger.warning(f"Error de validación al eliminar archivo: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=str(e)
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error al eliminar archivo: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Error al eliminar archivo: {str(e)}"
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Endpoints de Chunks
|
|
# ============================================================================
|
|
|
|
@router.post(
|
|
"/chunks/add",
|
|
response_model=AddChunksResponse,
|
|
status_code=status.HTTP_201_CREATED,
|
|
summary="Agregar chunks a una colección",
|
|
description="Agrega múltiples chunks a una colección existente"
|
|
)
|
|
async def add_chunks(request: AddChunksRequest):
|
|
"""Agrega chunks a una colección."""
|
|
try:
|
|
return await vector_service.add_chunks(
|
|
request.collection_name,
|
|
request.chunks
|
|
)
|
|
except ValueError as e:
|
|
logger.warning(f"Error de validación al agregar chunks: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=str(e)
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error al agregar chunks: {e}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Error al agregar chunks: {str(e)}"
|
|
)
|