Spaces:
Runtime error
Runtime error
| """ | |
| Vector Store API 라우터 | |
| ChromaDB 벡터 스토어 관련 엔드포인트들 | |
| """ | |
| from fastapi import APIRouter, HTTPException, Depends, status | |
| from typing import List | |
| import time | |
| from loguru import logger | |
| from ..core.vector_store import get_vector_store | |
| from ..models.vector_models import ( | |
| VectorSearchRequest, VectorSearchResponse, | |
| DocumentAddRequest, DocumentAddResponse, | |
| VectorStoreStats, SearchResult | |
| ) | |
| router = APIRouter() | |
| async def search_vectors( | |
| request: VectorSearchRequest, | |
| vector_store = Depends(get_vector_store) | |
| ): | |
| """ | |
| 🔍 벡터 유사도 검색 | |
| - 쿼리와 유사한 문서들을 벡터 검색으로 찾기 | |
| - 감정, 관계 등 메타데이터 필터링 지원 | |
| - top_k 개수만큼 결과 반환 | |
| """ | |
| try: | |
| logger.info(f"벡터 검색 요청: '{request.query[:50]}...', top_k: {request.top_k}") | |
| start_time = time.time() | |
| # 벡터 검색 실행 | |
| results = await vector_store.search( | |
| query=request.query, | |
| top_k=request.top_k, | |
| filter_metadata=request.filter_metadata | |
| ) | |
| search_time_ms = (time.time() - start_time) * 1000 | |
| return VectorSearchResponse( | |
| results=results, | |
| query=request.query, | |
| total_results=len(results), | |
| search_time_ms=search_time_ms | |
| ) | |
| except Exception as e: | |
| logger.error(f"벡터 검색 실패: {e}") | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail=f"벡터 검색 중 오류가 발생했습니다: {str(e)}" | |
| ) | |
| async def add_documents( | |
| request: DocumentAddRequest, | |
| vector_store = Depends(get_vector_store) | |
| ): | |
| """ | |
| 📝 문서 추가 | |
| - 새 문서들을 벡터 DB에 추가 | |
| - 자동으로 임베딩 생성 및 인덱싱 | |
| - 배치 처리로 효율적 추가 | |
| """ | |
| try: | |
| logger.info(f"문서 추가 요청: {len(request.documents)}개") | |
| start_time = time.time() | |
| # 문서 추가 실행 | |
| document_ids = await vector_store.add_documents(request.documents) | |
| processing_time_ms = (time.time() - start_time) * 1000 | |
| return DocumentAddResponse( | |
| success=True, | |
| added_count=len(document_ids), | |
| document_ids=document_ids, | |
| processing_time_ms=processing_time_ms, | |
| errors=[] | |
| ) | |
| except Exception as e: | |
| logger.error(f"문서 추가 실패: {e}") | |
| return DocumentAddResponse( | |
| success=False, | |
| added_count=0, | |
| document_ids=[], | |
| processing_time_ms=0, | |
| errors=[str(e)] | |
| ) | |
| async def get_vector_stats(vector_store = Depends(get_vector_store)): | |
| """ | |
| 📊 벡터 스토어 통계 | |
| - 총 문서 수, 컬렉션 정보 | |
| - 임베딩 모델 정보 | |
| - 시스템 상태 확인 | |
| """ | |
| try: | |
| stats = await vector_store.get_collection_stats() | |
| return stats | |
| except Exception as e: | |
| logger.error(f"통계 조회 실패: {e}") | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail=f"통계 조회 중 오류가 발생했습니다: {str(e)}" | |
| ) | |
| async def delete_document( | |
| document_id: str, | |
| vector_store = Depends(get_vector_store) | |
| ): | |
| """ | |
| 🗑️ 문서 삭제 | |
| - 특정 문서를 벡터 DB에서 삭제 | |
| """ | |
| try: | |
| success = await vector_store.delete_documents([document_id]) | |
| if success: | |
| return {"message": f"문서 {document_id} 삭제 완료", "success": True} | |
| else: | |
| raise HTTPException( | |
| status_code=status.HTTP_404_NOT_FOUND, | |
| detail=f"문서 {document_id}를 찾을 수 없습니다" | |
| ) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"문서 삭제 실패: {e}") | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail=f"문서 삭제 중 오류가 발생했습니다: {str(e)}" | |
| ) | |
| async def clear_collection(vector_store = Depends(get_vector_store)): | |
| """ | |
| ⚠️ 컬렉션 초기화 | |
| - 모든 문서 삭제 및 컬렉션 초기화 | |
| - 주의: 모든 데이터가 삭제됩니다! | |
| """ | |
| try: | |
| success = await vector_store.clear_collection() | |
| if success: | |
| return { | |
| "message": "컬렉션이 성공적으로 초기화되었습니다", | |
| "success": True, | |
| "warning": "모든 데이터가 삭제되었습니다" | |
| } | |
| else: | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail="컬렉션 초기화에 실패했습니다" | |
| ) | |
| except Exception as e: | |
| logger.error(f"컬렉션 초기화 실패: {e}") | |
| raise HTTPException( | |
| status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | |
| detail=f"컬렉션 초기화 중 오류가 발생했습니다: {str(e)}" | |
| ) | |
| async def vector_health_check(vector_store = Depends(get_vector_store)): | |
| """ | |
| 💊 벡터 스토어 헬스 체크 | |
| - 벡터 DB 연결 상태 확인 | |
| - 임베딩 모델 상태 확인 | |
| """ | |
| try: | |
| stats = await vector_store.get_collection_stats() | |
| health_status = { | |
| "status": "healthy" if stats.status == "healthy" else "unhealthy", | |
| "collection_name": stats.collection_name, | |
| "total_documents": stats.total_documents, | |
| "embedding_model": stats.embedding_model, | |
| "database_path": stats.database_path, | |
| "checks": { | |
| "chromadb_connection": True, | |
| "embedding_model_loaded": stats.embedding_dimension is not None, | |
| "collection_accessible": stats.total_documents >= 0 | |
| }, | |
| "last_updated": stats.last_updated | |
| } | |
| return health_status | |
| except Exception as e: | |
| logger.error(f"헬스 체크 실패: {e}") | |
| return { | |
| "status": "unhealthy", | |
| "error": str(e), | |
| "checks": { | |
| "chromadb_connection": False, | |
| "embedding_model_loaded": False, | |
| "collection_accessible": False | |
| } | |
| } | |
| async def search_demo(): | |
| """ | |
| 🎯 검색 데모 쿼리 예시 | |
| - 테스트용 검색 쿼리들 | |
| - API 사용법 가이드 | |
| """ | |
| return { | |
| "demo_queries": [ | |
| { | |
| "description": "기본 검색", | |
| "query": "친구와 싸웠어요", | |
| "example_request": { | |
| "query": "친구와 싸웠어요", | |
| "top_k": 5 | |
| } | |
| }, | |
| { | |
| "description": "감정 필터 검색", | |
| "query": "학교에서 스트레스 받아", | |
| "example_request": { | |
| "query": "학교에서 스트레스 받아", | |
| "top_k": 3, | |
| "filter_metadata": { | |
| "emotion": "분노" | |
| } | |
| } | |
| }, | |
| { | |
| "description": "관계 맥락 검색", | |
| "query": "잔소리 때문에 힘들어", | |
| "example_request": { | |
| "query": "잔소리 때문에 힘들어", | |
| "top_k": 5, | |
| "filter_metadata": { | |
| "relationship": "부모님", | |
| "data_source": "aihub" | |
| } | |
| } | |
| } | |
| ], | |
| "usage_tips": [ | |
| "구체적인 상황을 포함한 쿼리가 더 좋은 결과를 제공합니다", | |
| "감정과 관계 맥락을 필터로 활용하면 정확도가 높아집니다", | |
| "top_k는 1-20 사이의 값을 권장합니다" | |
| ] | |
| } |