@lobehub/chat 1.11.9 → 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (356) hide show
  1. package/.github/workflows/release.yml +2 -1
  2. package/.github/workflows/test.yml +2 -1
  3. package/CHANGELOG.md +50 -0
  4. package/Dockerfile.database +2 -0
  5. package/docs/self-hosting/advanced/knowledge-base.zh-CN.mdx +65 -0
  6. package/locales/ar/chat.json +13 -3
  7. package/locales/ar/components.json +2 -0
  8. package/locales/bg-BG/chat.json +13 -3
  9. package/locales/bg-BG/components.json +2 -0
  10. package/locales/de-DE/chat.json +13 -3
  11. package/locales/de-DE/components.json +2 -0
  12. package/locales/en-US/chat.json +13 -3
  13. package/locales/en-US/components.json +2 -0
  14. package/locales/es-ES/chat.json +13 -3
  15. package/locales/es-ES/components.json +2 -0
  16. package/locales/fr-FR/chat.json +13 -3
  17. package/locales/fr-FR/components.json +2 -0
  18. package/locales/it-IT/chat.json +13 -3
  19. package/locales/it-IT/components.json +2 -0
  20. package/locales/ja-JP/chat.json +13 -3
  21. package/locales/ja-JP/components.json +2 -0
  22. package/locales/ko-KR/chat.json +13 -3
  23. package/locales/ko-KR/components.json +2 -0
  24. package/locales/nl-NL/chat.json +13 -3
  25. package/locales/nl-NL/components.json +2 -0
  26. package/locales/pl-PL/chat.json +13 -3
  27. package/locales/pl-PL/components.json +2 -0
  28. package/locales/pt-BR/chat.json +13 -3
  29. package/locales/pt-BR/components.json +2 -0
  30. package/locales/ru-RU/chat.json +13 -3
  31. package/locales/ru-RU/components.json +2 -0
  32. package/locales/tr-TR/chat.json +13 -3
  33. package/locales/tr-TR/components.json +2 -0
  34. package/locales/vi-VN/chat.json +13 -3
  35. package/locales/vi-VN/components.json +2 -0
  36. package/locales/zh-CN/chat.json +13 -3
  37. package/locales/zh-CN/components.json +2 -0
  38. package/locales/zh-TW/chat.json +13 -3
  39. package/locales/zh-TW/components.json +2 -0
  40. package/package.json +3 -2
  41. package/scripts/migrateServerDB/docker.cjs +6 -0
  42. package/scripts/migrateServerDB/errorHint.js +17 -0
  43. package/scripts/migrateServerDB/index.ts +6 -0
  44. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/FileItem/Content.tsx +37 -0
  45. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/FileItem/index.tsx +87 -0
  46. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/FileItem/style.ts +4 -0
  47. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/FileItem/utils.ts +28 -0
  48. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/FileList.tsx +41 -0
  49. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/FilePreview/index.tsx +40 -0
  50. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx +1 -1
  51. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx +7 -22
  52. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx +1 -1
  53. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +2 -4
  54. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/FileItem/File.tsx +72 -0
  55. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/FileItem/Image.tsx +74 -0
  56. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/FileItem/index.tsx +39 -0
  57. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/FileItem/style.ts +1 -0
  58. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/index.tsx +33 -0
  59. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/Container.tsx +41 -0
  60. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/index.tsx +154 -0
  61. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Send.tsx +34 -0
  62. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx +24 -11
  63. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/components/UploadDetail/UploadStatus.tsx +71 -0
  64. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/components/UploadDetail/index.tsx +49 -0
  65. package/src/app/(main)/chat/(workspace)/@portal/FilePreview/index.tsx +26 -0
  66. package/src/app/(main)/chat/(workspace)/@portal/Home/Files/FileList/Item.tsx +53 -0
  67. package/src/app/(main)/chat/(workspace)/@portal/Home/Files/FileList/index.tsx +50 -0
  68. package/src/app/(main)/chat/(workspace)/@portal/Home/Files/index.tsx +21 -0
  69. package/src/app/(main)/chat/(workspace)/@portal/Home/index.tsx +2 -0
  70. package/src/app/(main)/chat/(workspace)/@portal/router.tsx +4 -0
  71. package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/KnowledgeTag.tsx +41 -0
  72. package/src/app/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/Tags.tsx +7 -2
  73. package/src/app/(main)/chat/(workspace)/_layout/Desktop/HotKeys.tsx +1 -1
  74. package/src/app/(main)/files/(content)/@menu/default.tsx +27 -0
  75. package/src/app/(main)/files/(content)/@menu/features/FileMenu/index.tsx +97 -0
  76. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/EmptyStatus.tsx +53 -0
  77. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/Item/Content.tsx +175 -0
  78. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/Item/index.tsx +69 -0
  79. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/KnowledgeBaseList.tsx +30 -0
  80. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/SkeletonList.tsx +57 -0
  81. package/src/app/(main)/files/(content)/@menu/features/KnowledgeBase/index.tsx +54 -0
  82. package/src/app/(main)/files/(content)/@modal/(.)[id]/FileDetail.tsx +16 -0
  83. package/src/app/(main)/files/(content)/@modal/(.)[id]/FilePreview.tsx +15 -0
  84. package/src/app/(main)/files/(content)/@modal/(.)[id]/FullscreenModal.tsx +85 -0
  85. package/src/app/(main)/files/(content)/@modal/(.)[id]/page.tsx +19 -0
  86. package/src/app/(main)/files/(content)/@modal/default.tsx +3 -0
  87. package/src/app/(main)/files/(content)/NotSupportClient.tsx +152 -0
  88. package/src/app/(main)/files/(content)/_layout/Desktop/index.tsx +28 -0
  89. package/src/app/(main)/files/(content)/_layout/Mobile.tsx +47 -0
  90. package/src/app/(main)/files/(content)/_layout/type.ts +7 -0
  91. package/src/app/(main)/files/(content)/layout.tsx +18 -0
  92. package/src/app/(main)/files/(content)/page.tsx +14 -0
  93. package/src/app/(main)/files/[id]/Header.tsx +63 -0
  94. package/src/app/(main)/files/[id]/page.tsx +44 -0
  95. package/src/app/(main)/files/features/FileDetail.tsx +93 -0
  96. package/src/app/(main)/files/hooks/useFileCategory.ts +9 -0
  97. package/src/app/(main)/files/layout.tsx +12 -0
  98. package/src/app/(main)/files/loading.tsx +21 -0
  99. package/src/app/(main)/repos/[id]/@menu/Head/index.tsx +33 -0
  100. package/src/app/(main)/repos/[id]/@menu/Menu/index.tsx +56 -0
  101. package/src/app/(main)/repos/[id]/@menu/default.tsx +25 -0
  102. package/src/app/(main)/repos/[id]/_layout/Desktop/index.tsx +25 -0
  103. package/src/app/(main)/repos/[id]/_layout/Mobile.tsx +47 -0
  104. package/src/app/(main)/repos/[id]/_layout/type.ts +6 -0
  105. package/src/app/(main)/repos/[id]/hooks/useKnowledgeItem.ts +7 -0
  106. package/src/app/(main)/repos/[id]/layout.tsx +13 -0
  107. package/src/app/(main)/repos/[id]/page.tsx +18 -0
  108. package/src/app/(main)/repos/layout.tsx +13 -0
  109. package/src/app/(main)/repos/page.tsx +5 -0
  110. package/src/app/trpc/async/[trpc]/route.ts +28 -0
  111. package/src/chains/abstractChunk.ts +19 -0
  112. package/src/chains/answerWithContext.ts +34 -0
  113. package/src/chains/rewriteQuery.ts +22 -0
  114. package/src/components/DragUpload/index.tsx +6 -99
  115. package/src/components/DragUpload/useDragUpload.tsx +146 -0
  116. package/src/components/FeatureList/index.tsx +64 -0
  117. package/src/components/FileParsingStatus/index.tsx +230 -0
  118. package/src/components/ImageItem/index.tsx +10 -2
  119. package/src/components/KnowledgeIcon/index.tsx +28 -0
  120. package/src/config/app.ts +6 -1
  121. package/src/config/featureFlags/schema.ts +1 -1
  122. package/src/const/file.ts +1 -0
  123. package/src/const/url.ts +1 -0
  124. package/src/database/client/models/file.ts +8 -2
  125. package/src/database/server/migrations/0005_pgvector.sql +2 -0
  126. package/src/database/server/migrations/0006_add_knowledge_base.sql +307 -0
  127. package/src/database/server/migrations/0007_fix_embedding_table.sql +18 -0
  128. package/src/database/server/migrations/meta/0005_snapshot.json +2119 -0
  129. package/src/database/server/migrations/meta/0006_snapshot.json +3006 -0
  130. package/src/database/server/migrations/meta/0007_snapshot.json +3012 -0
  131. package/src/database/server/migrations/meta/_journal.json +21 -0
  132. package/src/database/server/models/__tests__/_test_template.ts +155 -0
  133. package/src/database/server/models/__tests__/agent.test.ts +226 -0
  134. package/src/database/server/models/__tests__/asyncTask.test.ts +176 -0
  135. package/src/database/server/models/__tests__/chunk.test.ts +336 -0
  136. package/src/database/server/models/__tests__/file.test.ts +317 -29
  137. package/src/database/server/models/__tests__/fixtures/embedding.ts +568 -0
  138. package/src/database/server/models/__tests__/knowledgeBase.test.ts +132 -0
  139. package/src/database/server/models/__tests__/message.test.ts +7 -4
  140. package/src/database/server/models/_template.ts +10 -1
  141. package/src/database/server/models/agent.ts +165 -0
  142. package/src/database/server/models/asyncTask.ts +96 -0
  143. package/src/database/server/models/chunk.ts +203 -0
  144. package/src/database/server/models/embedding.ts +50 -0
  145. package/src/database/server/models/file.ts +231 -12
  146. package/src/database/server/models/knowledgeBase.ts +94 -0
  147. package/src/database/server/models/message.ts +156 -30
  148. package/src/database/server/models/user.ts +12 -1
  149. package/src/database/server/schemas/lobechat/agent.ts +93 -0
  150. package/src/database/server/schemas/lobechat/discover.ts +1 -1
  151. package/src/database/server/schemas/lobechat/file.ts +118 -1
  152. package/src/database/server/schemas/lobechat/index.ts +5 -1
  153. package/src/database/server/schemas/lobechat/message.ts +169 -0
  154. package/src/database/server/schemas/lobechat/rag.ts +53 -0
  155. package/src/database/server/schemas/lobechat/relations.ts +68 -48
  156. package/src/database/server/schemas/lobechat/session.ts +77 -0
  157. package/src/database/server/schemas/lobechat/topic.ts +32 -0
  158. package/src/database/server/schemas/lobechat/user.ts +40 -25
  159. package/src/database/server/utils/idGenerator.ts +1 -0
  160. package/src/features/ChatInput/ActionBar/Clear.tsx +1 -1
  161. package/src/features/ChatInput/ActionBar/Knowledge/Dropdown.tsx +160 -0
  162. package/src/features/ChatInput/ActionBar/Knowledge/ListItem.tsx +52 -0
  163. package/src/features/ChatInput/ActionBar/Knowledge/index.tsx +54 -0
  164. package/src/features/ChatInput/ActionBar/Tools/index.tsx +1 -1
  165. package/src/features/ChatInput/ActionBar/Upload/ClientMode.tsx +52 -0
  166. package/src/features/ChatInput/ActionBar/Upload/ServerMode.tsx +104 -0
  167. package/src/features/ChatInput/ActionBar/Upload/index.tsx +8 -0
  168. package/src/features/ChatInput/ActionBar/config.ts +14 -5
  169. package/src/features/ChatInput/useSend.ts +16 -7
  170. package/src/features/Conversation/Messages/Assistant/FileChunks/Item/index.tsx +51 -0
  171. package/src/features/Conversation/Messages/Assistant/FileChunks/Item/style.ts +38 -0
  172. package/src/features/Conversation/Messages/Assistant/FileChunks/index.tsx +76 -0
  173. package/src/features/Conversation/Messages/Assistant/index.tsx +13 -4
  174. package/src/features/Conversation/Messages/Default.tsx +4 -0
  175. package/src/features/Conversation/Messages/User/BelowMessage.tsx +78 -0
  176. package/src/features/Conversation/Messages/User/FileListViewer/Item.tsx +53 -0
  177. package/src/features/Conversation/Messages/User/FileListViewer/index.tsx +21 -0
  178. package/src/{components/FileList/FileListViewer.tsx → features/Conversation/Messages/User/ImageFileListViewer.tsx} +10 -2
  179. package/src/features/Conversation/Messages/{User.tsx → User/index.tsx} +11 -2
  180. package/src/features/Conversation/Messages/index.ts +8 -3
  181. package/src/features/Conversation/components/ChatItem/index.tsx +33 -10
  182. package/src/features/Conversation/components/InboxWelcome/QuestionSuggest.tsx +1 -1
  183. package/src/features/Conversation/types/index.tsx +1 -0
  184. package/src/features/FileManager/ChunkDrawer/ChunkList/ChunkItem.tsx +61 -0
  185. package/src/features/FileManager/ChunkDrawer/ChunkList/index.tsx +42 -0
  186. package/src/features/FileManager/ChunkDrawer/Content.tsx +38 -0
  187. package/src/features/FileManager/ChunkDrawer/Loading/index.tsx +16 -0
  188. package/src/features/FileManager/ChunkDrawer/SimilaritySearchList/Item.tsx +62 -0
  189. package/src/features/FileManager/ChunkDrawer/SimilaritySearchList/index.tsx +31 -0
  190. package/src/features/FileManager/ChunkDrawer/index.tsx +48 -0
  191. package/src/features/FileManager/FileList/EmptyStatus.tsx +153 -0
  192. package/src/features/FileManager/FileList/FileListItem/ChunkTag.tsx +35 -0
  193. package/src/features/FileManager/FileList/FileListItem/DropdownMenu.tsx +150 -0
  194. package/src/features/FileManager/FileList/FileListItem/index.tsx +211 -0
  195. package/src/features/FileManager/FileList/FileSkeleton.tsx +25 -0
  196. package/src/features/FileManager/FileList/ToolBar/Config.tsx +28 -0
  197. package/src/features/FileManager/FileList/ToolBar/MultiSelectActions.tsx +152 -0
  198. package/src/features/FileManager/FileList/ToolBar/index.tsx +114 -0
  199. package/src/features/FileManager/FileList/index.tsx +143 -0
  200. package/src/features/FileManager/FileList/useCheckTaskStatus.ts +27 -0
  201. package/src/features/FileManager/Header/FilesSearchBar.tsx +41 -0
  202. package/src/features/FileManager/Header/UploadFileButton.tsx +79 -0
  203. package/src/features/FileManager/Header/index.tsx +39 -0
  204. package/src/features/FileManager/UploadDock/Item.tsx +124 -0
  205. package/src/features/FileManager/UploadDock/index.tsx +183 -0
  206. package/src/features/FileManager/index.tsx +38 -0
  207. package/src/features/FileSidePanel/index.tsx +79 -0
  208. package/src/features/FileViewer/NotSupport/index.tsx +54 -0
  209. package/src/features/FileViewer/PDFViewer/HighlightLayer.tsx +81 -0
  210. package/src/features/FileViewer/PDFViewer/index.tsx +93 -0
  211. package/src/features/FileViewer/PDFViewer/style.ts +20 -0
  212. package/src/features/FileViewer/PDFViewer/useResizeObserver.ts +33 -0
  213. package/src/features/FileViewer/TXTViewer/index.tsx +41 -0
  214. package/src/features/FileViewer/index.tsx +45 -0
  215. package/src/features/KnowledgeBaseModal/AddFilesToKnowledgeBase/SelectForm.tsx +115 -0
  216. package/src/features/KnowledgeBaseModal/AddFilesToKnowledgeBase/index.tsx +43 -0
  217. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/Action.tsx +103 -0
  218. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/EditCustomPlugin.tsx +55 -0
  219. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/PluginTag.tsx +58 -0
  220. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Item/index.tsx +70 -0
  221. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/List.tsx +49 -0
  222. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Loading.tsx +13 -0
  223. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/index.tsx +39 -0
  224. package/src/features/KnowledgeBaseModal/CreateNew/CreateForm.tsx +73 -0
  225. package/src/features/KnowledgeBaseModal/CreateNew/index.tsx +35 -0
  226. package/src/features/KnowledgeBaseModal/index.ts +3 -0
  227. package/src/libs/langchain/loaders/pdf/index.ts +2 -2
  228. package/src/libs/trpc/async/asyncAuth.ts +24 -0
  229. package/src/libs/trpc/async/index.ts +10 -0
  230. package/src/libs/trpc/async/init.ts +11 -0
  231. package/src/libs/trpc/client/async.ts +14 -0
  232. package/src/libs/trpc/client/index.ts +1 -0
  233. package/src/libs/trpc/client/lambda.ts +9 -0
  234. package/src/libs/trpc/middleware/keyVaults.ts +18 -0
  235. package/src/libs/unstructured/__tests__/index.test.ts +0 -10
  236. package/src/libs/unstructured/index.ts +6 -11
  237. package/src/locales/default/chat.ts +47 -3
  238. package/src/locales/default/common.ts +1 -0
  239. package/src/locales/default/components.ts +2 -0
  240. package/src/locales/default/error.ts +6 -1
  241. package/src/locales/default/file.ts +92 -0
  242. package/src/locales/default/index.ts +4 -0
  243. package/src/locales/default/knowledgeBase.ts +31 -0
  244. package/src/locales/default/portal.ts +1 -0
  245. package/src/middleware.ts +3 -0
  246. package/src/server/asyncContext.ts +40 -0
  247. package/src/server/modules/ContentChunk/index.ts +135 -0
  248. package/src/server/modules/S3/index.ts +30 -5
  249. package/src/server/routers/async/caller.ts +27 -0
  250. package/src/server/routers/async/file.ts +247 -0
  251. package/src/server/routers/async/index.ts +12 -0
  252. package/src/server/routers/lambda/_template.ts +77 -0
  253. package/src/server/routers/lambda/agent.ts +159 -0
  254. package/src/server/routers/lambda/chunk.ts +189 -0
  255. package/src/server/routers/lambda/file.ts +129 -5
  256. package/src/server/routers/lambda/index.ts +6 -0
  257. package/src/server/routers/lambda/knowledgeBase.ts +79 -0
  258. package/src/server/routers/lambda/message.ts +6 -0
  259. package/src/server/routers/lambda/session.ts +0 -25
  260. package/src/server/routers/lambda/user.ts +5 -1
  261. package/src/server/services/chunk/index.ts +74 -0
  262. package/src/server/utils/files.ts +9 -0
  263. package/src/services/__tests__/chat.test.ts +18 -20
  264. package/src/services/__tests__/{upload.test.ts → upload_legacy.test.ts} +1 -1
  265. package/src/services/agent.ts +45 -0
  266. package/src/services/chat.ts +17 -15
  267. package/src/services/file/client.test.ts +1 -50
  268. package/src/services/file/client.ts +12 -25
  269. package/src/services/file/server.ts +25 -3
  270. package/src/services/file/type.ts +7 -4
  271. package/src/services/knowledgeBase.ts +34 -0
  272. package/src/services/message/client.test.ts +1 -1
  273. package/src/services/message/client.ts +29 -3
  274. package/src/services/message/index.ts +0 -2
  275. package/src/services/message/server.ts +9 -3
  276. package/src/services/message/type.ts +1 -13
  277. package/src/services/rag.ts +29 -0
  278. package/src/services/session/server.ts +1 -1
  279. package/src/services/upload.ts +89 -84
  280. package/src/services/upload_legacy.ts +104 -0
  281. package/src/services/user/client.ts +7 -2
  282. package/src/services/user/server.ts +6 -2
  283. package/src/services/user/type.ts +3 -2
  284. package/src/store/agent/slices/chat/action.ts +90 -18
  285. package/src/store/agent/slices/chat/initialState.ts +1 -0
  286. package/src/store/agent/slices/chat/selectors.ts +58 -0
  287. package/src/store/chat/slices/builtinTool/action.test.ts +2 -2
  288. package/src/store/chat/slices/builtinTool/action.ts +2 -2
  289. package/src/store/chat/slices/message/action.test.ts +2 -1
  290. package/src/store/chat/slices/message/action.ts +102 -26
  291. package/src/store/chat/slices/message/actions/rag.ts +148 -0
  292. package/src/store/chat/slices/message/initialState.ts +7 -0
  293. package/src/store/chat/slices/message/reducer.ts +6 -2
  294. package/src/store/chat/slices/message/selectors.ts +38 -3
  295. package/src/store/chat/slices/plugin/action.ts +8 -2
  296. package/src/store/chat/slices/portal/action.ts +8 -0
  297. package/src/store/chat/slices/portal/initialState.ts +3 -0
  298. package/src/store/chat/slices/portal/selectors.ts +8 -2
  299. package/src/store/file/initialState.ts +5 -1
  300. package/src/store/file/reducers/uploadFileList.ts +133 -0
  301. package/src/store/file/selectors.ts +3 -0
  302. package/src/store/file/slices/chat/action.test.ts +90 -90
  303. package/src/store/file/slices/chat/action.ts +164 -109
  304. package/src/store/file/slices/chat/initialState.ts +7 -2
  305. package/src/store/file/slices/chat/selectors.test.ts +84 -61
  306. package/src/store/file/slices/chat/selectors.ts +22 -32
  307. package/src/store/file/slices/chunk/action.ts +36 -0
  308. package/src/store/file/slices/chunk/index.ts +3 -0
  309. package/src/store/file/slices/chunk/initialState.ts +15 -0
  310. package/src/store/file/slices/chunk/selectors.ts +10 -0
  311. package/src/store/file/slices/fileManager/action.ts +187 -0
  312. package/src/store/file/slices/fileManager/index.ts +3 -0
  313. package/src/store/file/slices/fileManager/initialState.ts +18 -0
  314. package/src/store/file/slices/fileManager/selectors.ts +58 -0
  315. package/src/store/file/slices/tts/action.test.ts +1 -1
  316. package/src/store/file/slices/tts/action.ts +2 -2
  317. package/src/store/file/slices/upload/action.ts +164 -0
  318. package/src/store/file/store.ts +12 -1
  319. package/src/store/knowledgeBase/index.ts +2 -0
  320. package/src/store/knowledgeBase/initialState.ts +7 -0
  321. package/src/store/knowledgeBase/selectors.ts +1 -0
  322. package/src/store/knowledgeBase/slices/content/action.ts +27 -0
  323. package/src/store/knowledgeBase/slices/content/index.ts +1 -0
  324. package/src/store/knowledgeBase/slices/crud/action.ts +78 -0
  325. package/src/store/knowledgeBase/slices/crud/index.ts +3 -0
  326. package/src/store/knowledgeBase/slices/crud/initialState.ts +12 -0
  327. package/src/store/knowledgeBase/slices/crud/selectors.ts +7 -0
  328. package/src/store/knowledgeBase/store.ts +30 -0
  329. package/src/store/serverConfig/selectors.test.ts +1 -1
  330. package/src/store/user/slices/preference/selectors.ts +8 -0
  331. package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
  332. package/src/tools/dalle/Render/Item/ImageFileItem.tsx +3 -23
  333. package/src/types/agent/index.ts +9 -0
  334. package/src/types/asyncTask.ts +31 -0
  335. package/src/types/chunk/document.ts +9 -0
  336. package/src/types/chunk/index.ts +52 -0
  337. package/src/types/files/index.ts +35 -0
  338. package/src/types/files/list.ts +44 -0
  339. package/src/types/files/upload.ts +91 -0
  340. package/src/types/knowledgeBase/index.ts +45 -0
  341. package/src/types/message/index.ts +54 -5
  342. package/src/types/rag.ts +16 -0
  343. package/src/utils/filter.test.ts +2 -0
  344. package/src/utils/server/auth.ts +23 -0
  345. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/LocalFiles.tsx +0 -46
  346. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files.tsx +0 -19
  347. package/src/components/FileList/EditableFileList.tsx +0 -47
  348. package/src/components/FileList/index.ts +0 -2
  349. package/src/components/FileList/type.tsx +0 -7
  350. package/src/database/server/schemas/lobechat/chat.ts +0 -331
  351. package/src/features/ChatInput/ActionBar/FileUpload.tsx +0 -69
  352. package/src/features/ChatInput/useChatInput.ts +0 -45
  353. package/src/features/FileList/EditableFileList.tsx +0 -31
  354. package/src/features/FileList/FileListPreviewer.tsx +0 -17
  355. package/src/features/FileList/index.tsx +0 -2
  356. package/src/types/files.ts +0 -42
@@ -0,0 +1,31 @@
1
+ export default {
2
+ addToKnowledgeBase: {
3
+ addSuccess: '文件添加成功,<1>立即查看</1>',
4
+ confirm: '添加',
5
+ id: {
6
+ placeholder: '请选择要添加的知识库',
7
+ required: '请选择知识库',
8
+ title: '目标知识库',
9
+ },
10
+ title: '添加到知识库',
11
+ totalFiles: '已选择 {{count}} 个文件',
12
+ },
13
+ createNew: {
14
+ confirm: '新建',
15
+ description: {
16
+ placeholder: '知识库简介(选填)',
17
+ },
18
+ formTitle: '基本信息',
19
+ name: {
20
+ placeholder: '知识库名称',
21
+ required: '请填写知识库名称',
22
+ },
23
+ title: '新建知识库',
24
+ },
25
+ tab: {
26
+ files: '文档',
27
+ settings: '设置',
28
+ testing: '召回测试',
29
+ },
30
+ title: '知识库',
31
+ };
@@ -6,5 +6,6 @@ export default {
6
6
  summaryTooltip: '总结当前内容',
7
7
  },
8
8
  emptyArtifactList: '当前 Artifacts 列表为空,请在会话中按需使用插件后再查看',
9
+ files: '文件',
9
10
  title: '工作区',
10
11
  };
package/src/middleware.ts CHANGED
@@ -14,6 +14,8 @@ export const config = {
14
14
  '/',
15
15
  '/chat(.*)',
16
16
  '/settings(.*)',
17
+ '/files(.*)',
18
+ '/repos(.*)',
17
19
  // ↓ cloud ↓
18
20
  ],
19
21
  };
@@ -46,6 +48,7 @@ const nextAuthMiddleware = NextAuthEdge.auth((req) => {
46
48
 
47
49
  const isProtectedRoute = createRouteMatcher([
48
50
  '/settings(.*)',
51
+ '/files(.*)',
49
52
  // ↓ cloud ↓
50
53
  ]);
51
54
 
@@ -0,0 +1,40 @@
1
+ import { NextRequest } from 'next/server';
2
+
3
+ import { JWTPayload, LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
4
+ import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
5
+
6
+ export interface AsyncAuthContext {
7
+ jwtPayload: JWTPayload;
8
+ secret: string;
9
+ userId?: string | null;
10
+ }
11
+
12
+ /**
13
+ * Inner function for `createContext` where we create the context.
14
+ * This is useful for testing when we don't want to mock Next.js' request/response
15
+ */
16
+ export const createAsyncContextInner = async (params?: {
17
+ jwtPayload?: JWTPayload;
18
+ secret?: string;
19
+ userId?: string | null;
20
+ }): Promise<AsyncAuthContext> => ({
21
+ jwtPayload: params?.jwtPayload || {},
22
+ secret: params?.secret || '',
23
+ userId: params?.userId,
24
+ });
25
+
26
+ export type AsyncContext = Awaited<ReturnType<typeof createAsyncContextInner>>;
27
+
28
+ export const createAsyncRouteContext = async (request: NextRequest): Promise<AsyncContext> => {
29
+ // for API-response caching see https://trpc.io/docs/v11/caching
30
+
31
+ const authorization = request.headers.get('Authorization');
32
+ const lobeChatAuthorization = request.headers.get(LOBE_CHAT_AUTH_HEADER);
33
+
34
+ const secret = authorization?.split(' ')[1];
35
+ const gateKeeper = await KeyVaultsGateKeeper.initWithEnvKey();
36
+ const { plaintext } = await gateKeeper.decrypt(lobeChatAuthorization || '');
37
+
38
+ const { userId, payload } = JSON.parse(plaintext);
39
+ return createAsyncContextInner({ jwtPayload: payload, secret, userId });
40
+ };
@@ -0,0 +1,135 @@
1
+ import { ChunkingLoader } from 'src/libs/langchain';
2
+ import { Strategy } from 'unstructured-client/sdk/models/shared';
3
+
4
+ import { NewChunkItem, NewUnstructuredChunkItem } from '@/database/server/schemas/lobechat';
5
+ import { ChunkingStrategy, Unstructured } from '@/libs/unstructured';
6
+
7
+ export interface ChunkContentParams {
8
+ content: Uint8Array;
9
+ fileType: string;
10
+ filename: string;
11
+ mode?: 'fast' | 'hi-res';
12
+ }
13
+
14
+ interface ChunkResult {
15
+ chunks: NewChunkItem[];
16
+ unstructuredChunks?: NewUnstructuredChunkItem[];
17
+ }
18
+
19
+ export class ContentChunk {
20
+ private unstructuredClient: Unstructured;
21
+ private langchainClient: ChunkingLoader;
22
+
23
+ constructor() {
24
+ this.unstructuredClient = new Unstructured();
25
+ this.langchainClient = new ChunkingLoader();
26
+ }
27
+
28
+ isUsingUnstructured(params: ChunkContentParams) {
29
+ return params.fileType === 'application/pdf' && params.mode === 'hi-res';
30
+ }
31
+
32
+ async chunkContent(params: ChunkContentParams): Promise<ChunkResult> {
33
+ if (this.isUsingUnstructured(params))
34
+ return await this.chunkByUnstructured(params.filename, params.content);
35
+
36
+ return await this.chunkByLangChain(params.filename, params.content);
37
+ }
38
+
39
+ private chunkByUnstructured = async (
40
+ filename: string,
41
+ content: Uint8Array,
42
+ ): Promise<ChunkResult> => {
43
+ const result = await this.unstructuredClient.partition({
44
+ chunkingStrategy: ChunkingStrategy.ByPage,
45
+ fileContent: content,
46
+ filename,
47
+ strategy: Strategy.Auto,
48
+ });
49
+
50
+ // after finish partition, we need to filter out some elements
51
+ const documents = result.compositeElements
52
+ .filter((e) => !new Set(['PageNumber', 'Footer']).has(e.type))
53
+ .map((item, index): NewChunkItem => {
54
+ const {
55
+ text_as_html,
56
+ page_number,
57
+ page_name,
58
+ image_mime_type,
59
+ image_base64,
60
+ parent_id,
61
+ languages,
62
+ coordinates,
63
+ } = item.metadata;
64
+
65
+ return {
66
+ id: item.element_id,
67
+ index,
68
+ metadata: {
69
+ coordinates,
70
+ image_base64,
71
+ image_mime_type,
72
+ languages,
73
+ page_name,
74
+ page_number,
75
+ parent_id,
76
+ text_as_html,
77
+ },
78
+ text: item.text,
79
+ type: item.type,
80
+ };
81
+ });
82
+
83
+ const chunks = result.originElements
84
+ .filter((e) => !new Set(['PageNumber', 'Footer']).has(e.type))
85
+ .map((item, index): NewUnstructuredChunkItem => {
86
+ const {
87
+ text_as_html,
88
+ page_number,
89
+ page_name,
90
+ image_mime_type,
91
+ image_base64,
92
+ parent_id,
93
+ languages,
94
+ coordinates,
95
+ } = item.metadata;
96
+
97
+ return {
98
+ compositeId: item.compositeId,
99
+ id: item.element_id,
100
+ index,
101
+ metadata: {
102
+ coordinates,
103
+ image_base64,
104
+ image_mime_type,
105
+ languages,
106
+ page_name,
107
+ page_number,
108
+ text_as_html,
109
+ },
110
+ parentId: parent_id,
111
+ text: item.text,
112
+ type: item.type,
113
+ };
114
+ });
115
+
116
+ return { chunks: documents, unstructuredChunks: chunks };
117
+ };
118
+
119
+ private chunkByLangChain = async (
120
+ filename: string,
121
+ content: Uint8Array,
122
+ ): Promise<ChunkResult> => {
123
+ const res = await this.langchainClient.partitionContent(filename, content);
124
+
125
+ const documents = res.map((item, index) => ({
126
+ id: item.id,
127
+ index,
128
+ metadata: item.metadata,
129
+ text: item.pageContent,
130
+ type: 'LangChainElement',
131
+ }));
132
+
133
+ return { chunks: documents };
134
+ };
135
+ }
@@ -1,6 +1,7 @@
1
1
  import {
2
+ DeleteObjectCommand,
3
+ DeleteObjectsCommand,
2
4
  GetObjectCommand,
3
- ListObjectsCommand,
4
5
  PutObjectCommand,
5
6
  S3Client,
6
7
  } from '@aws-sdk/client-s3';
@@ -45,13 +46,22 @@ export class S3 {
45
46
  });
46
47
  }
47
48
 
48
- public async getImages(): Promise<FileType[]> {
49
- const command = new ListObjectsCommand({
49
+ public async deleteFile(key: string) {
50
+ const command = new DeleteObjectCommand({
50
51
  Bucket: this.bucket,
52
+ Key: key,
53
+ });
54
+
55
+ return this.client.send(command);
56
+ }
57
+
58
+ public async deleteFiles(keys: string[]) {
59
+ const command = new DeleteObjectsCommand({
60
+ Bucket: this.bucket,
61
+ Delete: { Objects: keys.map((key) => ({ Key: key })) },
51
62
  });
52
63
 
53
- const res = await this.client.send(command);
54
- return listFileSchema.parse(res.Contents);
64
+ return this.client.send(command);
55
65
  }
56
66
 
57
67
  public async getFileContent(key: string): Promise<string> {
@@ -69,6 +79,21 @@ export class S3 {
69
79
  return response.Body.transformToString();
70
80
  }
71
81
 
82
+ public async getFileByteArray(key: string): Promise<Uint8Array> {
83
+ const command = new GetObjectCommand({
84
+ Bucket: this.bucket,
85
+ Key: key,
86
+ });
87
+
88
+ const response = await this.client.send(command);
89
+
90
+ if (!response.Body) {
91
+ throw new Error(`No body in response with ${key}`);
92
+ }
93
+
94
+ return response.Body.transformToByteArray();
95
+ }
96
+
72
97
  public async createPreSignedUrl(key: string): Promise<string> {
73
98
  const command = new PutObjectCommand({
74
99
  ACL: this.setAcl ? 'public-read' : undefined,
@@ -0,0 +1,27 @@
1
+ import { createTRPCClient, httpBatchLink } from '@trpc/client';
2
+ import superjson from 'superjson';
3
+ import urlJoin from 'url-join';
4
+
5
+ import { appEnv } from '@/config/app';
6
+ import { serverDBEnv } from '@/config/db';
7
+ import { JWTPayload, LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
8
+ import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
9
+
10
+ import type { AsyncRouter } from './index';
11
+
12
+ export const createAsyncServerClient = async (userId: string, payload: JWTPayload) => {
13
+ const gateKeeper = await KeyVaultsGateKeeper.initWithEnvKey();
14
+
15
+ return createTRPCClient<AsyncRouter>({
16
+ links: [
17
+ httpBatchLink({
18
+ headers: {
19
+ Authorization: `Bearer ${serverDBEnv.KEY_VAULTS_SECRET}`,
20
+ [LOBE_CHAT_AUTH_HEADER]: await gateKeeper.encrypt(JSON.stringify({ payload, userId })),
21
+ },
22
+ transformer: superjson,
23
+ url: urlJoin(appEnv.APP_URL!, '/trpc/async'),
24
+ }),
25
+ ],
26
+ });
27
+ };
@@ -0,0 +1,247 @@
1
+ import { TRPCError } from '@trpc/server';
2
+ import { chunk } from 'lodash-es';
3
+ import pMap from 'p-map';
4
+ import { z } from 'zod';
5
+
6
+ import { initAgentRuntimeWithUserPayload } from '@/app/api/chat/agentRuntime';
7
+ import { fileEnv } from '@/config/file';
8
+ import { DEFAULT_EMBEDDING_MODEL } from '@/const/settings';
9
+ import { ASYNC_TASK_TIMEOUT, AsyncTaskModel } from '@/database/server/models/asyncTask';
10
+ import { ChunkModel } from '@/database/server/models/chunk';
11
+ import { EmbeddingModel } from '@/database/server/models/embedding';
12
+ import { FileModel } from '@/database/server/models/file';
13
+ import { NewChunkItem, NewEmbeddingsItem } from '@/database/server/schemas/lobechat';
14
+ import { ModelProvider } from '@/libs/agent-runtime';
15
+ import { asyncAuthedProcedure, asyncRouter as router } from '@/libs/trpc/async';
16
+ import { S3 } from '@/server/modules/S3';
17
+ import { ChunkService } from '@/server/services/chunk';
18
+ import { AsyncTaskError, AsyncTaskErrorType, AsyncTaskStatus } from '@/types/asyncTask';
19
+ import { safeParseJSON } from '@/utils/safeParseJSON';
20
+
21
+ const fileProcedure = asyncAuthedProcedure.use(async (opts) => {
22
+ const { ctx } = opts;
23
+
24
+ return opts.next({
25
+ ctx: {
26
+ asyncTaskModel: new AsyncTaskModel(ctx.userId),
27
+ chunkModel: new ChunkModel(ctx.userId),
28
+ chunkService: new ChunkService(ctx.userId),
29
+ embeddingModel: new EmbeddingModel(ctx.userId),
30
+ fileModel: new FileModel(ctx.userId),
31
+ },
32
+ });
33
+ });
34
+
35
+ export const fileRouter = router({
36
+ embeddingChunks: fileProcedure
37
+ .input(
38
+ z.object({
39
+ fileId: z.string(),
40
+ model: z.string().default(DEFAULT_EMBEDDING_MODEL),
41
+ taskId: z.string(),
42
+ }),
43
+ )
44
+ .mutation(async ({ ctx, input }) => {
45
+ const file = await ctx.fileModel.findById(input.fileId);
46
+
47
+ if (!file) {
48
+ throw new TRPCError({ code: 'BAD_REQUEST', message: 'File not found' });
49
+ }
50
+
51
+ const asyncTask = await ctx.asyncTaskModel.findById(input.taskId);
52
+
53
+ if (!asyncTask) throw new TRPCError({ code: 'BAD_REQUEST', message: 'Async Task not found' });
54
+
55
+ try {
56
+ const timeoutPromise = new Promise((_, reject) => {
57
+ setTimeout(() => {
58
+ reject({
59
+ body: { detail: 'embedding task is timeout, please try again' },
60
+ name: AsyncTaskErrorType.Timeout,
61
+ } as AsyncTaskError);
62
+ }, ASYNC_TASK_TIMEOUT);
63
+ });
64
+
65
+ const embeddingPromise = async () => {
66
+ // update the task status to success
67
+ await ctx.asyncTaskModel.update(input.taskId, {
68
+ status: AsyncTaskStatus.Processing,
69
+ });
70
+
71
+ const startAt = Date.now();
72
+
73
+ const CHUNK_SIZE = 50;
74
+ const CONCURRENCY = 10;
75
+
76
+ const chunks = await ctx.chunkModel.getChunksTextByFileId(input.fileId);
77
+ const requestArray = chunk(chunks, CHUNK_SIZE);
78
+
79
+ await pMap(
80
+ requestArray,
81
+ async (chunks, index) => {
82
+ const agentRuntime = await initAgentRuntimeWithUserPayload(
83
+ ModelProvider.OpenAI,
84
+ ctx.jwtPayload,
85
+ );
86
+
87
+ const number = index + 1;
88
+ console.log(`执行第 ${number} 个任务`);
89
+
90
+ console.time(`任务[${number}]: embeddings`);
91
+
92
+ const embeddings = await agentRuntime.embeddings({
93
+ dimensions: 1024,
94
+ input: chunks.map((c) => c.text),
95
+ model: input.model,
96
+ });
97
+ console.timeEnd(`任务[${number}]: embeddings`);
98
+
99
+ const items: NewEmbeddingsItem[] =
100
+ embeddings?.map((e) => ({
101
+ chunkId: chunks[e.index].id,
102
+ embeddings: e.embedding,
103
+ fileId: input.fileId,
104
+ model: input.model,
105
+ })) || [];
106
+
107
+ console.time(`任务[${number}]: insert db`);
108
+ await ctx.embeddingModel.bulkCreate(items);
109
+ console.timeEnd(`任务[${number}]: insert db`);
110
+ },
111
+ { concurrency: CONCURRENCY },
112
+ );
113
+
114
+ const duration = Date.now() - startAt;
115
+ // update the task status to success
116
+ await ctx.asyncTaskModel.update(input.taskId, {
117
+ duration,
118
+ status: AsyncTaskStatus.Success,
119
+ });
120
+
121
+ return { success: true };
122
+ };
123
+
124
+ // Race between the chunking process and the timeout
125
+ return await Promise.race([embeddingPromise(), timeoutPromise]);
126
+ } catch (e) {
127
+ console.error('embeddingChunks error', e);
128
+ await ctx.asyncTaskModel.update(input.taskId, {
129
+ error: e,
130
+ status: AsyncTaskStatus.Error,
131
+ });
132
+
133
+ return {
134
+ message: `File ${file.name}(${input.taskId}) failed to embedding: ${(e as Error).message}`,
135
+ success: false,
136
+ };
137
+ }
138
+ }),
139
+
140
+ parseFileToChunks: fileProcedure
141
+ .input(
142
+ z.object({
143
+ fileId: z.string(),
144
+ taskId: z.string(),
145
+ }),
146
+ )
147
+ .mutation(async ({ ctx, input }) => {
148
+ const file = await ctx.fileModel.findById(input.fileId);
149
+ if (!file) {
150
+ throw new TRPCError({ code: 'BAD_REQUEST', message: 'File not found' });
151
+ }
152
+
153
+ const s3 = new S3();
154
+
155
+ let content: Uint8Array | undefined;
156
+ try {
157
+ content = await s3.getFileByteArray(file.url);
158
+ } catch (e) {
159
+ console.error(e);
160
+ // if file not found, delete it from db
161
+ if ((e as any).Code === 'NoSuchKey') {
162
+ await ctx.fileModel.delete(input.fileId);
163
+ throw new TRPCError({ code: 'BAD_REQUEST', message: 'File not found' });
164
+ }
165
+ }
166
+
167
+ if (!content) return;
168
+
169
+ const asyncTask = await ctx.asyncTaskModel.findById(input.taskId);
170
+
171
+ if (!asyncTask) throw new TRPCError({ code: 'BAD_REQUEST', message: 'Async Task not found' });
172
+
173
+ try {
174
+ const startAt = Date.now();
175
+
176
+ const timeoutPromise = new Promise((_, reject) => {
177
+ setTimeout(() => {
178
+ reject({
179
+ body: { detail: 'chunking task is timeout, please try again' },
180
+ name: AsyncTaskErrorType.Timeout,
181
+ } as AsyncTaskError);
182
+ }, ASYNC_TASK_TIMEOUT);
183
+ });
184
+
185
+ const chunkingPromise = async () => {
186
+ const chunkService = ctx.chunkService;
187
+ // update the task status to processing
188
+ await ctx.asyncTaskModel.update(input.taskId, { status: AsyncTaskStatus.Processing });
189
+
190
+ // partition file to chunks
191
+ const chunkResult = await chunkService.chunkContent({
192
+ content,
193
+ fileType: file.fileType,
194
+ filename: file.name,
195
+ });
196
+
197
+ // after finish partition, we need to filter out some elements
198
+ const chunks = chunkResult.chunks.map(
199
+ (item): NewChunkItem => ({ ...item, userId: ctx.userId }),
200
+ );
201
+
202
+ const duration = Date.now() - startAt;
203
+
204
+ await ctx.chunkModel.bulkCreate(chunks, input.fileId);
205
+
206
+ if (chunkResult.unstructuredChunks) {
207
+ const unstructuredChunks = chunkResult.unstructuredChunks.map(
208
+ (item): NewChunkItem => ({ ...item, fileId: input.fileId, userId: ctx.userId }),
209
+ );
210
+ await ctx.chunkModel.bulkCreateUnstructuredChunks(unstructuredChunks);
211
+ }
212
+
213
+ // update the task status to success
214
+ await ctx.asyncTaskModel.update(input.taskId, {
215
+ duration,
216
+ status: AsyncTaskStatus.Success,
217
+ });
218
+
219
+ // if enable auto embedding, trigger the embedding task
220
+ if (fileEnv.CHUNKS_AUTO_EMBEDDING) {
221
+ await chunkService.asyncEmbeddingFileChunks(input.fileId, ctx.jwtPayload);
222
+ }
223
+
224
+ return { success: true };
225
+ };
226
+ // Race between the chunking process and the timeout
227
+ return await Promise.race([chunkingPromise(), timeoutPromise]);
228
+ } catch (e) {
229
+ const error = e as any;
230
+
231
+ const asyncTaskError: AsyncTaskError = error.body
232
+ ? { body: safeParseJSON(error.body) ?? error.body, name: error.name }
233
+ : { body: { detail: error.message }, name: (error as Error).name };
234
+
235
+ console.error('[Chunking Error]', asyncTaskError);
236
+ await ctx.asyncTaskModel.update(input.taskId, {
237
+ error: asyncTaskError,
238
+ status: AsyncTaskStatus.Error,
239
+ });
240
+
241
+ return {
242
+ message: `File ${file.name}(${input.taskId}) failed to chunking: ${(e as Error).message}`,
243
+ success: false,
244
+ };
245
+ }
246
+ }),
247
+ });
@@ -0,0 +1,12 @@
1
+ import { publicProcedure, asyncRouter as router } from '@/libs/trpc/async';
2
+
3
+ import { fileRouter } from './file';
4
+
5
+ export const asyncRouter = router({
6
+ file: fileRouter,
7
+ healthcheck: publicProcedure.query(() => "i'm live!"),
8
+ });
9
+
10
+ export type AsyncRouter = typeof asyncRouter;
11
+
12
+ export * from './caller';
@@ -0,0 +1,77 @@
1
+ import { z } from 'zod';
2
+
3
+ import { SessionGroupModel } from '@/database/server/models/sessionGroup';
4
+ import { insertSessionGroupSchema } from '@/database/server/schemas/lobechat';
5
+ import { authedProcedure, router } from '@/libs/trpc';
6
+ import { SessionGroupItem } from '@/types/session';
7
+
8
+ const sessionProcedure = authedProcedure.use(async (opts) => {
9
+ const { ctx } = opts;
10
+
11
+ return opts.next({
12
+ ctx: {
13
+ sessionGroupModel: new SessionGroupModel(ctx.userId),
14
+ },
15
+ });
16
+ });
17
+
18
+ export const sessionGroupRouter = router({
19
+ createSessionGroup: sessionProcedure
20
+ .input(
21
+ z.object({
22
+ name: z.string(),
23
+ sort: z.number().optional(),
24
+ }),
25
+ )
26
+ .mutation(async ({ input, ctx }) => {
27
+ const data = await ctx.sessionGroupModel.create({
28
+ name: input.name,
29
+ sort: input.sort,
30
+ });
31
+
32
+ return data?.id;
33
+ }),
34
+
35
+ getSessionGroup: sessionProcedure.query(async ({ ctx }): Promise<SessionGroupItem[]> => {
36
+ return ctx.sessionGroupModel.query() as any;
37
+ }),
38
+
39
+ removeAllSessionGroups: sessionProcedure.mutation(async ({ ctx }) => {
40
+ return ctx.sessionGroupModel.deleteAll();
41
+ }),
42
+
43
+ removeSessionGroup: sessionProcedure
44
+ .input(z.object({ id: z.string(), removeChildren: z.boolean().optional() }))
45
+ .mutation(async ({ input, ctx }) => {
46
+ return ctx.sessionGroupModel.delete(input.id);
47
+ }),
48
+
49
+ updateSessionGroup: sessionProcedure
50
+ .input(
51
+ z.object({
52
+ id: z.string(),
53
+ value: insertSessionGroupSchema.partial(),
54
+ }),
55
+ )
56
+ .mutation(async ({ input, ctx }) => {
57
+ return ctx.sessionGroupModel.update(input.id, input.value);
58
+ }),
59
+ updateSessionGroupOrder: sessionProcedure
60
+ .input(
61
+ z.object({
62
+ sortMap: z.array(
63
+ z.object({
64
+ id: z.string(),
65
+ sort: z.number(),
66
+ }),
67
+ ),
68
+ }),
69
+ )
70
+ .mutation(async ({ input, ctx }) => {
71
+ console.log('sortMap:', input.sortMap);
72
+
73
+ return ctx.sessionGroupModel.updateOrder(input.sortMap);
74
+ }),
75
+ });
76
+
77
+ export type SessionGroupRouter = typeof sessionGroupRouter;