Просмотр исходного кода

Delete associated VectorStoreFile when attachment is deleted

Shun Miyazawa 10 месяцев назад
Родитель
Сommit
106bc1307a

+ 18 - 0
apps/app/src/features/openai/server/services/openai.ts

@@ -86,6 +86,7 @@ export interface IOpenaiService {
   deleteVectorStoreFile(vectorStoreRelationId: Types.ObjectId, pageId: Types.ObjectId): Promise<void>;
   deleteVectorStoreFilesByPageIds(pageIds: Types.ObjectId[]): Promise<void>;
   deleteObsoleteVectorStoreFile(limit: number, apiCallInterval: number): Promise<void>; // for CronJob
+  deleteVectorStoreFileOnDeleteAttachment(attachmentId: string): Promise<void>;
   isAiAssistantUsable(aiAssistantId: string, user: IUserHasId): Promise<boolean>;
   createAiAssistant(data: UpsertAiAssistantData, user: IUserHasId): Promise<AiAssistantDocument>;
   updateAiAssistant(aiAssistantId: string, data: UpsertAiAssistantData, user: IUserHasId): Promise<AiAssistantDocument>;
@@ -97,6 +98,9 @@ class OpenaiService implements IOpenaiService {
   constructor(crowi: Crowi) {
     this.createVectorStoreFileOnUploadAttachment = this.createVectorStoreFileOnUploadAttachment.bind(this);
     crowi.attachmentService.addAttachHandler(this.createVectorStoreFileOnUploadAttachment);
+
+    this.deleteVectorStoreFileOnDeleteAttachment = this.deleteVectorStoreFileOnDeleteAttachment.bind(this);
+    crowi.attachmentService.addDetachHandler(this.deleteVectorStoreFileOnDeleteAttachment);
   }
 
   private get client() {
@@ -500,6 +504,20 @@ class OpenaiService implements IOpenaiService {
     }
   }
 
+  async deleteVectorStoreFileOnDeleteAttachment(attachmentId: string) {
+    const vectorStoreFileRelation = await VectorStoreFileRelationModel.findOne({ attachment: attachmentId });
+    if (vectorStoreFileRelation == null) {
+      return;
+    }
+
+    for await (const fileId of vectorStoreFileRelation.fileIds) {
+      const response = await this.client.deleteFile(fileId);
+      logger.debug('Delete vector store file', response);
+    }
+
+    await VectorStoreFileRelationModel.deleteMany({ attachment: attachmentId });
+  }
+
   async filterPagesByAccessScope(aiAssistant: AiAssistantDocument, pages: HydratedDocument<PageDocument>[]) {
     const isPublicPage = (page :HydratedDocument<PageDocument>) => page.grant === PageGrant.GRANT_PUBLIC;
 

+ 10 - 0
apps/app/src/server/service/attachment.js

@@ -107,6 +107,16 @@ class AttachmentService {
     await fileUploadService.deleteFile(attachment);
     await attachment.remove();
 
+    const detachedHandlerPromises = this.detachHandlers.map((handler) => {
+      return handler(attachment._id);
+    });
+
+    // Do not await, run in background
+    Promise.all(detachedHandlerPromises)
+      .catch((err) => {
+        logger.error('Error while executing detached handler', err);
+      });
+
     return;
   }