"""项目文件变更后的后台向量索引同步。""" 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()