r2r_file.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import tempfile
  2. import uuid
  3. from typing import List
  4. import aiofiles
  5. import aiofiles.os
  6. from fastapi import UploadFile
  7. from sqlalchemy.ext.asyncio import AsyncSession
  8. import os
  9. from app.models import File
  10. from app.providers.r2r import R2R
  11. from app.providers.storage import storage
  12. from app.services.file.impl.oss_file import OSSFileService
  13. # import asyncio
  14. from pathlib import Path
  15. allowed_formats = [
  16. ".bmp",
  17. ".heic",
  18. ".jpeg",
  19. ".jpg",
  20. ".png",
  21. ".tiff",
  22. ".mp3",
  23. ".mp4",
  24. ".wav"
  25. ]
  26. class R2RFileService(OSSFileService):
  27. @staticmethod
  28. async def create_file(
  29. *, session: AsyncSession, purpose: str, file: UploadFile
  30. ) -> File:
  31. # 文件是否存在
  32. # statement = (
  33. # select(File)
  34. # .where(File.purpose == purpose)
  35. # .where(File.filename == file.filename)
  36. # .where(File.bytes == file.size)
  37. # )
  38. # result = await session.execute(statement)
  39. # ext_file = result.scalars().first()
  40. # if ext_file is not None:
  41. # # TODO: 文件去重策略
  42. # return ext_file
  43. file_extension = Path(file.filename).suffix
  44. file_key = f"{uuid.uuid4()}{file_extension}"
  45. #print(file_key)
  46. fileinfo = {"document_id": file_key}
  47. # file_key = f"{uuid.uuid4()}-{file.filename}"
  48. with tempfile.NamedTemporaryFile(
  49. suffix="_" + file.filename, delete=True
  50. ) as temp_file:
  51. tmp_file_path = temp_file.name
  52. async with aiofiles.open(tmp_file_path, "wb") as f:
  53. while content := await file.read(1024):
  54. await f.write(content)
  55. # storage.save_from_path(filename=file_key, local_file_path=tmp_file_path)
  56. r2r = R2R()
  57. # await r2r.init()
  58. fileinfo = await r2r.ingest_file(
  59. file_path=tmp_file_path,
  60. metadata={"file_key": file_key, "title": file.filename},
  61. )
  62. fileinfo = fileinfo.results
  63. # 存储
  64. db_file = File(
  65. purpose=purpose,
  66. filename=file.filename,
  67. bytes=file.size,
  68. key=fileinfo.document_id,
  69. )
  70. session.add(db_file)
  71. await session.commit()
  72. await session.refresh(db_file)
  73. return db_file
  74. @staticmethod
  75. def search_in_files(
  76. query: str, file_keys: List[str], folder_keys: List[str] = None
  77. ) -> dict:
  78. files = []
  79. # {}
  80. file_key = {"$in": []}
  81. document_id = {"$in": []}
  82. filters = {"$or": []}
  83. #print("ggggggggggggggggggggggggggggggggggggddddddddddddddddddccccccccccccccccccccc")
  84. for key in file_keys:
  85. if len(key) == 36:
  86. document_id["$in"].append(key)
  87. else:
  88. file_key["$in"].append(key)
  89. if len(document_id["$in"]) > 0:
  90. filters["$or"].append({"document_id": document_id})
  91. if len(file_key["$in"]) > 0:
  92. filters["$or"].append({"file_key": file_key})
  93. #print(file_key)
  94. #print(document_id)
  95. #print(filters)
  96. #print(folder_keys)
  97. if folder_keys:
  98. filters["$or"].append({"collection_ids": {"$overlap": folder_keys}})
  99. ## {"$or": [filters, {"collection_ids": {"$in": folder_keys}}]}
  100. ##filters["collection_ids"] = {"$overlap": folder_keys}
  101. ## {"$and": {"$document_id": ..., "collection_ids": ...}}
  102. """
  103. {
  104. "$or": [
  105. {"document_id": {"$eq": "9fbe403b-..."}},
  106. {"collection_ids": {"$in": ["122fdf6a-...", "..."]}}
  107. ]
  108. }
  109. """
  110. #print(filters)
  111. if len(filters["$or"]) < 2:
  112. filters = filters["$or"][0]
  113. #print("filtersfiltersfiltersfiltersfiltersfiltersfiltersfiltersfiltersfilters")
  114. #print(filters)
  115. """
  116. loop = asyncio.get_event_loop() # 获取当前事件循环
  117. loop.run_until_complete(r2r.init()) # 确保 r2r 已初始化
  118. search_results = loop.run_until_complete(r2r.search(query, filters=filters))
  119. asyncio.run(r2r.init())
  120. search_results = asyncio.run(r2r.search(query, filters=filters))
  121. search_results = loop.run_until_complete(
  122. r2r.search(query, filters={"file_key": {"$in": file_keys}})
  123. )
  124. """
  125. r2r = R2R()
  126. # r2r.init_sync()
  127. search_results = r2r.search(query, filters=filters)
  128. if not search_results:
  129. return files
  130. for doc in search_results:
  131. file_extension = os.path.splitext(doc.metadata["title"])[1].lower()
  132. if file_extension in allowed_formats:
  133. files.append(
  134. {
  135. "id": str(doc.id),
  136. "text": doc.text,
  137. "title": doc.metadata["title"],
  138. "url": "https://r2r.s3.cn-north-1.amazonaws.com.cn/r2r/documents/"
  139. + str(doc.document_id),
  140. }
  141. )
  142. # #print(doc.metadata)
  143. else:
  144. files.append(
  145. {
  146. "id": str(doc.id),
  147. "text": doc.text,
  148. "title": doc.metadata["title"],
  149. }
  150. )
  151. # file_key = doc.metadata.file_key
  152. # file_key = doc.metadata.title if file_key is None else file_key
  153. # text = doc.text
  154. # if "text" in files:
  155. # files["text"] += f"\n\n{text}"
  156. # else:
  157. # files["text"] = text
  158. # #print("aaaaaaaaaaaaaa")
  159. # #print(files)
  160. return files
  161. @staticmethod
  162. def list_in_files(
  163. ids: list[str] = None,
  164. offset: int = 0,
  165. limit: int = 100,
  166. ) -> dict:
  167. """
  168. loop = asyncio.get_event_loop() # 获取当前事件循环
  169. loop.run_until_complete(r2r.init()) # 确保 r2r 已初始化
  170. list_results = loop.run_until_complete(
  171. r2r.list(ids=ids, offset=offset, limit=limit)
  172. )
  173. asyncio.run(r2r.init())
  174. list_results = asyncio.run(r2r.list(ids=ids, offset=offset, limit=limit))
  175. """
  176. r2r = R2R()
  177. # r2r.init_sync()
  178. list_results = r2r.list(ids=ids, offset=offset, limit=limit)
  179. return list_results
  180. @staticmethod
  181. def list_documents(
  182. id: str = "",
  183. offset: int = 0,
  184. limit: int = 100,
  185. ) -> dict:
  186. """
  187. loop = asyncio.get_event_loop() # 获取当前事件循环
  188. loop.run_until_complete(r2r.init()) # 确保 r2r 已初始化
  189. list_results = loop.run_until_complete(
  190. r2r.list_documents(id=id, offset=offset, limit=limit)
  191. )
  192. asyncio.run(r2r.init())
  193. list_results = asyncio.run(
  194. r2r.list_documents(id=id, offset=offset, limit=limit)
  195. )
  196. """
  197. r2r = R2R()
  198. # r2r.init_sync()
  199. list_results = r2r.list_documents(id=id, offset=offset, limit=limit)
  200. return list_results
  201. @staticmethod
  202. def list_chunks(ids: list[str]) -> dict:
  203. if len(ids) > 0:
  204. r2r = R2R()
  205. # r2r.init_sync()
  206. #print("list_chunkslist_chunkslist_chunkslist_chunkslist_chunkslist_chunks")
  207. list_results = r2r.list_chunks(ids=ids)
  208. #print(list_results)
  209. files = []
  210. for doc in list_results:
  211. file_extension = os.path.splitext(doc.metadata["title"])[1].lower()
  212. if file_extension in allowed_formats:
  213. files.append(
  214. {
  215. "id": str(doc.id),
  216. "text": doc.text,
  217. "title": doc.metadata["title"],
  218. "url": "https://r2r.s3.cn-north-1.amazonaws.com.cn/r2r/documents/"
  219. + str(doc.document_id),
  220. }
  221. )
  222. # #print(doc.metadata)
  223. else:
  224. files.append(
  225. {
  226. "id": str(doc.id),
  227. "text": doc.text,
  228. "title": doc.metadata["title"],
  229. }
  230. )
  231. """
  232. for doc in list_results:
  233. text = doc.text
  234. if "text" in files:
  235. files["text"] += f"\n\n{text}"
  236. else:
  237. files["text"] = text
  238. """
  239. #print(files)
  240. return files
  241. else:
  242. return {}
  243. # TODO 删除s3&r2r文件