nex_docus/backend/app/services/file_vector_sync_service.py

71 lines
2.3 KiB
Python

"""项目文件变更后的后台向量索引同步。"""
import asyncio
import logging
from typing import Set
from app.core.database import AsyncSessionLocal
from app.services.zvec_service import zvec_service
logger = logging.getLogger(__name__)
class FileVectorSyncService:
"""使用独立数据库会话串行同步单个项目的文件向量。"""
def __init__(self):
self._project_locks = {}
self._tasks: Set[asyncio.Task] = set()
def revectorize(self, project_id: int, file_path: str) -> None:
self._schedule(project_id, "revectorize", file_path)
def delete(self, project_id: int, file_path: str) -> None:
self._schedule(project_id, "delete", file_path)
def move(self, project_id: int, old_path: str, new_path: str) -> None:
self._schedule(project_id, "move", old_path, new_path)
def _schedule(
self,
project_id: int,
operation: str,
file_path: str,
new_path: str = "",
) -> None:
task = asyncio.create_task(
self._run(project_id, operation, file_path, new_path)
)
self._tasks.add(task)
task.add_done_callback(self._tasks.discard)
async def _run(
self,
project_id: int,
operation: str,
file_path: str,
new_path: str,
) -> None:
lock = self._project_locks.setdefault(project_id, asyncio.Lock())
async with lock:
async with AsyncSessionLocal() as db:
try:
if operation == "delete":
await zvec_service.delete_vector(db, project_id, file_path)
elif operation == "move":
await zvec_service.delete_vector(db, project_id, file_path)
await zvec_service.revectorize_path(db, project_id, new_path)
else:
await zvec_service.revectorize_path(db, project_id, file_path)
except Exception:
await db.rollback()
logger.exception(
"File vector sync failed: project=%s operation=%s path=%s",
project_id,
operation,
file_path,
)
file_vector_sync_service = FileVectorSyncService()