@lobehub/chat 1.33.4 → 1.34.0

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 (295) hide show
  1. package/.i18nrc.js +8 -2
  2. package/.releaserc.js +10 -1
  3. package/CHANGELOG.md +51 -16624
  4. package/changelog/CHANGELOG.v0.md +16621 -0
  5. package/changelog/v0.json +6064 -0
  6. package/changelog/v1.json +3356 -0
  7. package/docs/changelog/2024-11-25-november-providers.mdx +19 -0
  8. package/docs/changelog/2024-11-25-november-providers.zh-CN.mdx +17 -0
  9. package/docs/changelog/index.json +12 -0
  10. package/docs/changelog/schema.json +70 -0
  11. package/docs/self-hosting/advanced/auth/clerk.mdx +19 -23
  12. package/docs/self-hosting/advanced/auth/clerk.zh-CN.mdx +5 -4
  13. package/docs/self-hosting/advanced/auth/next-auth/authelia.mdx +2 -4
  14. package/docs/self-hosting/advanced/auth/next-auth/authelia.zh-CN.mdx +3 -5
  15. package/docs/self-hosting/advanced/auth/next-auth/authentik.zh-CN.mdx +2 -2
  16. package/docs/self-hosting/advanced/auth/next-auth/casdoor.mdx +49 -44
  17. package/docs/self-hosting/advanced/auth/next-auth/casdoor.zh-CN.mdx +42 -41
  18. package/docs/self-hosting/advanced/auth/next-auth/logto.mdx +29 -21
  19. package/docs/self-hosting/advanced/auth/next-auth/logto.zh-CN.mdx +2 -1
  20. package/docs/self-hosting/advanced/auth.mdx +10 -10
  21. package/docs/self-hosting/advanced/auth.zh-CN.mdx +10 -10
  22. package/docs/self-hosting/advanced/feature-flags.zh-CN.mdx +1 -1
  23. package/docs/self-hosting/advanced/model-list.mdx +1 -1
  24. package/docs/self-hosting/advanced/s3/cloudflare-r2.mdx +17 -12
  25. package/docs/self-hosting/advanced/s3/tencent-cloud.mdx +33 -20
  26. package/docs/self-hosting/advanced/s3.mdx +31 -28
  27. package/docs/self-hosting/advanced/s3.zh-CN.mdx +1 -0
  28. package/docs/self-hosting/advanced/webrtc.mdx +1 -0
  29. package/docs/self-hosting/environment-variables/s3.mdx +4 -3
  30. package/docs/self-hosting/environment-variables/s3.zh-CN.mdx +1 -0
  31. package/docs/self-hosting/environment-variables.mdx +6 -9
  32. package/docs/self-hosting/platform/alibaba-cloud.mdx +1 -2
  33. package/docs/self-hosting/platform/alibaba-cloud.zh-CN.mdx +1 -2
  34. package/docs/self-hosting/platform/btpanel.mdx +7 -13
  35. package/docs/self-hosting/platform/btpanel.zh-CN.mdx +8 -13
  36. package/docs/self-hosting/platform/docker.zh-CN.mdx +2 -1
  37. package/docs/self-hosting/platform/netlify.zh-CN.mdx +2 -1
  38. package/docs/self-hosting/server-database/docker.mdx +18 -5
  39. package/docs/self-hosting/server-database/docker.zh-CN.mdx +9 -6
  40. package/docs/self-hosting/server-database/netlify.mdx +0 -1
  41. package/docs/self-hosting/server-database/railway.mdx +0 -1
  42. package/docs/self-hosting/server-database/repocloud.mdx +5 -2
  43. package/docs/self-hosting/server-database/repocloud.zh-CN.mdx +23 -3
  44. package/docs/self-hosting/server-database/sealos.mdx +3 -0
  45. package/docs/self-hosting/server-database/vercel.mdx +35 -32
  46. package/docs/self-hosting/server-database/vercel.zh-CN.mdx +25 -25
  47. package/docs/self-hosting/server-database/zeabur.mdx +2 -2
  48. package/docs/self-hosting/server-database/zeabur.zh-CN.mdx +3 -4
  49. package/docs/self-hosting/server-database.mdx +23 -8
  50. package/docs/self-hosting/start.mdx +8 -2
  51. package/docs/usage/features/database.zh-CN.mdx +1 -1
  52. package/docs/usage/foundation/text2image.mdx +1 -2
  53. package/docs/usage/foundation/text2image.zh-CN.mdx +1 -3
  54. package/docs/usage/providers/ai21.mdx +14 -15
  55. package/docs/usage/providers/ai21.zh-CN.mdx +1 -3
  56. package/docs/usage/providers/ai360.mdx +14 -15
  57. package/docs/usage/providers/ai360.zh-CN.mdx +1 -3
  58. package/docs/usage/providers/cloudflare.mdx +1 -1
  59. package/docs/usage/providers/fireworksai.mdx +19 -19
  60. package/docs/usage/providers/fireworksai.zh-CN.mdx +1 -3
  61. package/docs/usage/providers/github.mdx +22 -21
  62. package/docs/usage/providers/github.zh-CN.mdx +6 -6
  63. package/docs/usage/providers/hunyuan.mdx +17 -18
  64. package/docs/usage/providers/hunyuan.zh-CN.mdx +1 -3
  65. package/docs/usage/providers/siliconcloud.mdx +14 -15
  66. package/docs/usage/providers/siliconcloud.zh-CN.mdx +3 -5
  67. package/docs/usage/providers/spark.mdx +17 -18
  68. package/docs/usage/providers/spark.zh-CN.mdx +1 -3
  69. package/docs/usage/providers/upstage.mdx +14 -15
  70. package/docs/usage/providers/upstage.zh-CN.mdx +1 -3
  71. package/docs/usage/providers/wenxin.mdx +17 -18
  72. package/docs/usage/providers/wenxin.zh-CN.mdx +1 -3
  73. package/docs/usage/providers/zeroone.mdx +2 -2
  74. package/locales/ar/chat.json +7 -0
  75. package/locales/ar/common.json +2 -0
  76. package/locales/ar/models.json +24 -0
  77. package/locales/ar/providers.json +3 -0
  78. package/locales/ar/setting.json +5 -0
  79. package/locales/ar/thread.json +5 -0
  80. package/locales/bg-BG/chat.json +7 -0
  81. package/locales/bg-BG/common.json +2 -0
  82. package/locales/bg-BG/models.json +24 -0
  83. package/locales/bg-BG/providers.json +3 -0
  84. package/locales/bg-BG/setting.json +5 -0
  85. package/locales/bg-BG/thread.json +5 -0
  86. package/locales/de-DE/chat.json +7 -0
  87. package/locales/de-DE/common.json +2 -0
  88. package/locales/de-DE/models.json +24 -0
  89. package/locales/de-DE/providers.json +3 -0
  90. package/locales/de-DE/setting.json +5 -0
  91. package/locales/de-DE/thread.json +5 -0
  92. package/locales/en-US/chat.json +7 -0
  93. package/locales/en-US/common.json +2 -0
  94. package/locales/en-US/models.json +24 -0
  95. package/locales/en-US/providers.json +3 -0
  96. package/locales/en-US/setting.json +5 -0
  97. package/locales/en-US/thread.json +5 -0
  98. package/locales/es-ES/chat.json +7 -0
  99. package/locales/es-ES/common.json +2 -0
  100. package/locales/es-ES/models.json +24 -0
  101. package/locales/es-ES/providers.json +3 -0
  102. package/locales/es-ES/setting.json +5 -0
  103. package/locales/es-ES/thread.json +5 -0
  104. package/locales/fa-IR/chat.json +7 -0
  105. package/locales/fa-IR/common.json +2 -0
  106. package/locales/fa-IR/models.json +24 -0
  107. package/locales/fa-IR/providers.json +3 -0
  108. package/locales/fa-IR/setting.json +5 -0
  109. package/locales/fa-IR/thread.json +5 -0
  110. package/locales/fr-FR/chat.json +7 -0
  111. package/locales/fr-FR/common.json +2 -0
  112. package/locales/fr-FR/models.json +24 -0
  113. package/locales/fr-FR/providers.json +3 -0
  114. package/locales/fr-FR/setting.json +5 -0
  115. package/locales/fr-FR/thread.json +5 -0
  116. package/locales/it-IT/chat.json +7 -0
  117. package/locales/it-IT/common.json +2 -0
  118. package/locales/it-IT/models.json +24 -0
  119. package/locales/it-IT/providers.json +3 -0
  120. package/locales/it-IT/setting.json +5 -0
  121. package/locales/it-IT/thread.json +5 -0
  122. package/locales/ja-JP/chat.json +7 -0
  123. package/locales/ja-JP/common.json +2 -0
  124. package/locales/ja-JP/models.json +24 -0
  125. package/locales/ja-JP/providers.json +3 -0
  126. package/locales/ja-JP/setting.json +5 -0
  127. package/locales/ja-JP/thread.json +5 -0
  128. package/locales/ko-KR/chat.json +7 -0
  129. package/locales/ko-KR/common.json +2 -0
  130. package/locales/ko-KR/models.json +24 -0
  131. package/locales/ko-KR/providers.json +3 -0
  132. package/locales/ko-KR/setting.json +5 -0
  133. package/locales/ko-KR/thread.json +5 -0
  134. package/locales/nl-NL/chat.json +7 -0
  135. package/locales/nl-NL/common.json +2 -0
  136. package/locales/nl-NL/models.json +24 -0
  137. package/locales/nl-NL/providers.json +3 -0
  138. package/locales/nl-NL/setting.json +5 -0
  139. package/locales/nl-NL/thread.json +5 -0
  140. package/locales/pl-PL/chat.json +7 -0
  141. package/locales/pl-PL/common.json +2 -0
  142. package/locales/pl-PL/models.json +24 -0
  143. package/locales/pl-PL/providers.json +3 -0
  144. package/locales/pl-PL/setting.json +5 -0
  145. package/locales/pl-PL/thread.json +5 -0
  146. package/locales/pt-BR/chat.json +7 -0
  147. package/locales/pt-BR/common.json +2 -0
  148. package/locales/pt-BR/models.json +24 -0
  149. package/locales/pt-BR/providers.json +3 -0
  150. package/locales/pt-BR/setting.json +5 -0
  151. package/locales/pt-BR/thread.json +5 -0
  152. package/locales/ru-RU/chat.json +7 -0
  153. package/locales/ru-RU/common.json +2 -0
  154. package/locales/ru-RU/models.json +24 -0
  155. package/locales/ru-RU/providers.json +3 -0
  156. package/locales/ru-RU/setting.json +5 -0
  157. package/locales/ru-RU/thread.json +5 -0
  158. package/locales/tr-TR/chat.json +7 -0
  159. package/locales/tr-TR/common.json +2 -0
  160. package/locales/tr-TR/models.json +24 -0
  161. package/locales/tr-TR/providers.json +3 -0
  162. package/locales/tr-TR/setting.json +5 -0
  163. package/locales/tr-TR/thread.json +5 -0
  164. package/locales/vi-VN/chat.json +7 -0
  165. package/locales/vi-VN/common.json +2 -0
  166. package/locales/vi-VN/models.json +24 -0
  167. package/locales/vi-VN/providers.json +3 -0
  168. package/locales/vi-VN/setting.json +5 -0
  169. package/locales/vi-VN/thread.json +5 -0
  170. package/locales/zh-CN/chat.json +7 -0
  171. package/locales/zh-CN/common.json +2 -0
  172. package/locales/zh-CN/models.json +24 -0
  173. package/locales/zh-CN/providers.json +3 -0
  174. package/locales/zh-CN/setting.json +5 -0
  175. package/locales/zh-CN/thread.json +5 -0
  176. package/locales/zh-TW/chat.json +7 -0
  177. package/locales/zh-TW/common.json +2 -0
  178. package/locales/zh-TW/models.json +24 -0
  179. package/locales/zh-TW/providers.json +3 -0
  180. package/locales/zh-TW/setting.json +5 -0
  181. package/locales/zh-TW/thread.json +5 -0
  182. package/package.json +6 -1
  183. package/scripts/changelogWorkflow/buildStaticChangelog.ts +135 -0
  184. package/scripts/changelogWorkflow/const.ts +11 -0
  185. package/scripts/changelogWorkflow/index.ts +10 -0
  186. package/src/app/(main)/chat/(workspace)/@conversation/default.tsx +2 -0
  187. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatHydration/index.tsx +11 -2
  188. package/src/{features → app/(main)/chat/(workspace)/@conversation/features}/ChatInput/Desktop/Footer/index.tsx +7 -9
  189. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +7 -2
  190. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/Thread.tsx +62 -0
  191. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/ThreadItem.tsx +68 -0
  192. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/index.tsx +62 -2
  193. package/src/app/(main)/chat/(workspace)/@conversation/features/ThreadHydration.tsx +47 -0
  194. package/src/app/(main)/chat/(workspace)/@portal/_layout/Desktop.tsx +3 -2
  195. package/src/app/(main)/chat/(workspace)/@portal/_layout/Mobile.tsx +47 -6
  196. package/src/app/(main)/chat/(workspace)/@topic/features/SkeletonList.tsx +3 -2
  197. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ByTimeMode/index.tsx +10 -3
  198. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/FlatMode/index.tsx +1 -1
  199. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ThreadItem/Content.tsx +164 -0
  200. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ThreadItem/index.tsx +98 -0
  201. package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{TopicItem.tsx → TopicItem/index.tsx} +33 -22
  202. package/src/app/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +12 -5
  203. package/src/app/(main)/chat/(workspace)/_layout/Mobile/index.tsx +1 -2
  204. package/src/const/message.ts +2 -0
  205. package/src/const/settings/systemAgent.ts +1 -0
  206. package/src/database/server/migrations/0012_add_thread.sql +39 -0
  207. package/src/database/server/migrations/meta/0012_snapshot.json +3671 -0
  208. package/src/database/server/migrations/meta/_journal.json +7 -0
  209. package/src/database/server/models/_template.ts +2 -2
  210. package/src/database/server/models/message.ts +1 -0
  211. package/src/database/server/models/thread.ts +79 -0
  212. package/src/database/server/schemas/lobechat/message.ts +2 -1
  213. package/src/database/server/schemas/lobechat/relations.ts +13 -1
  214. package/src/database/server/schemas/lobechat/topic.ts +30 -1
  215. package/src/database/server/utils/idGenerator.ts +1 -0
  216. package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +6 -4
  217. package/src/features/ChatInput/ActionBar/Token/index.tsx +24 -5
  218. package/src/features/ChatInput/ActionBar/config.ts +3 -2
  219. package/src/features/ChatInput/Desktop/index.tsx +15 -7
  220. package/src/features/ChatInput/Mobile/index.tsx +4 -4
  221. package/src/features/Conversation/Actions/Assistant.tsx +24 -5
  222. package/src/features/Conversation/Actions/User.tsx +21 -4
  223. package/src/features/Conversation/Actions/index.ts +1 -66
  224. package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/index.tsx +3 -1
  225. package/src/features/Conversation/Messages/{Tool/index.tsx → Assistant/ToolCallItem/Tool.tsx} +10 -11
  226. package/src/features/Conversation/Messages/Assistant/ToolCallItem/index.tsx +5 -3
  227. package/src/features/Conversation/Messages/Assistant/index.tsx +22 -14
  228. package/src/features/Conversation/Messages/index.ts +0 -2
  229. package/src/features/Conversation/components/AutoScroll.tsx +1 -1
  230. package/src/features/Conversation/components/ChatItem/ActionsBar.tsx +79 -5
  231. package/src/features/Conversation/components/ChatItem/InPortalThreadContext.ts +3 -0
  232. package/src/features/Conversation/components/ChatItem/index.tsx +16 -5
  233. package/src/features/Conversation/components/MarkdownElements/LobeArtifact/Render/index.tsx +9 -1
  234. package/src/features/Conversation/components/ThreadDivider/index.tsx +19 -0
  235. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +19 -4
  236. package/src/features/Portal/Thread/Chat/ChatInput/Footer.tsx +90 -0
  237. package/src/features/Portal/Thread/Chat/ChatInput/TextArea.tsx +30 -0
  238. package/src/features/Portal/Thread/Chat/ChatInput/index.tsx +66 -0
  239. package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +50 -0
  240. package/src/features/Portal/Thread/Chat/ChatItem.tsx +62 -0
  241. package/src/features/Portal/Thread/Chat/ChatList.tsx +49 -0
  242. package/src/features/Portal/Thread/Chat/ThreadDivider/index.tsx +19 -0
  243. package/src/features/Portal/Thread/Chat/index.tsx +28 -0
  244. package/src/features/Portal/Thread/Header/Active.tsx +35 -0
  245. package/src/features/Portal/Thread/Header/New.tsx +37 -0
  246. package/src/features/Portal/Thread/Header/Title.tsx +18 -0
  247. package/src/features/Portal/Thread/Header/index.tsx +20 -0
  248. package/src/features/Portal/Thread/hook.ts +8 -0
  249. package/src/features/Portal/Thread/index.ts +12 -0
  250. package/src/features/Portal/router.tsx +2 -1
  251. package/src/hooks/useFetchTopics.ts +7 -1
  252. package/src/locales/default/chat.ts +8 -1
  253. package/src/locales/default/common.ts +3 -0
  254. package/src/locales/default/index.ts +2 -0
  255. package/src/locales/default/setting.ts +5 -0
  256. package/src/locales/default/thread.ts +5 -0
  257. package/src/server/routers/lambda/index.ts +2 -0
  258. package/src/server/routers/lambda/thread.ts +83 -0
  259. package/src/services/thread.ts +54 -0
  260. package/src/store/chat/initialState.ts +3 -0
  261. package/src/store/chat/selectors.ts +2 -1
  262. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +1 -1
  263. package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +1 -1
  264. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +31 -8
  265. package/src/store/chat/slices/aiChat/actions/rag.ts +1 -1
  266. package/src/store/chat/slices/message/selectors.test.ts +3 -3
  267. package/src/store/chat/slices/message/selectors.ts +50 -29
  268. package/src/store/chat/slices/plugin/action.ts +26 -8
  269. package/src/store/chat/slices/portal/action.ts +1 -0
  270. package/src/store/chat/slices/portal/initialState.ts +1 -0
  271. package/src/store/chat/slices/portal/selectors/thread.ts +17 -0
  272. package/src/store/chat/slices/portal/selectors.ts +2 -0
  273. package/src/store/chat/slices/thread/action.ts +326 -0
  274. package/src/store/chat/slices/thread/initialState.ts +34 -0
  275. package/src/store/chat/slices/thread/reducer.ts +48 -0
  276. package/src/store/chat/slices/thread/selectors/index.ts +202 -0
  277. package/src/store/chat/slices/thread/selectors/util.ts +22 -0
  278. package/src/store/chat/slices/topic/action.ts +5 -1
  279. package/src/store/chat/store.ts +5 -2
  280. package/src/store/global/initialState.ts +4 -0
  281. package/src/store/global/selectors.ts +4 -0
  282. package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
  283. package/src/types/message/index.ts +17 -1
  284. package/src/types/topic/index.ts +1 -0
  285. package/src/types/topic/thread.ts +42 -0
  286. package/src/types/user/settings/systemAgent.ts +1 -0
  287. package/src/app/(main)/chat/(workspace)/@portal/features/Header.tsx +0 -11
  288. package/src/app/(main)/chat/(workspace)/_layout/Mobile/PortalModal.tsx +0 -35
  289. /package/src/{features → app/(main)/chat/(workspace)/@conversation/features}/ChatInput/Desktop/Footer/SendMore.tsx +0 -0
  290. /package/src/{features → app/(main)/chat/(workspace)/@conversation/features}/ChatInput/Desktop/Footer/ShortcutHint.tsx +0 -0
  291. /package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{DefaultContent.tsx → TopicItem/DefaultContent.tsx} +0 -0
  292. /package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{TopicContent.tsx → TopicItem/TopicContent.tsx} +0 -0
  293. /package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/PluginResultJSON.tsx +0 -0
  294. /package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/Settings.tsx +0 -0
  295. /package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/style.ts +0 -0
@@ -0,0 +1,62 @@
1
+ import React, { memo, useMemo } from 'react';
2
+
3
+ import { ChatItem } from '@/features/Conversation';
4
+ import ActionsBar from '@/features/Conversation/components/ChatItem/ActionsBar';
5
+ import { useAgentStore } from '@/store/agent';
6
+ import { agentSelectors } from '@/store/agent/selectors';
7
+ import { useChatStore } from '@/store/chat';
8
+ import { threadSelectors } from '@/store/chat/selectors';
9
+
10
+ import ThreadDivider from './ThreadDivider';
11
+
12
+ export interface ThreadChatItemProps {
13
+ id: string;
14
+ index: number;
15
+ }
16
+
17
+ const ThreadChatItem = memo<ThreadChatItemProps>(({ id, index }) => {
18
+ const [threadMessageId, threadStartMessageIndex, historyLength] = useChatStore((s) => [
19
+ threadSelectors.threadSourceMessageId(s),
20
+ threadSelectors.threadSourceMessageIndex(s),
21
+ threadSelectors.portalDisplayChatsLength(s),
22
+ ]);
23
+
24
+ const enableThreadDivider = threadMessageId === id;
25
+
26
+ const endRender = useMemo(
27
+ () => enableThreadDivider && <ThreadDivider />,
28
+ [enableThreadDivider, id],
29
+ );
30
+
31
+ const isParentMessage = index <= threadStartMessageIndex;
32
+
33
+ const actionBar = useMemo(
34
+ () => !isParentMessage && <ActionsBar id={id} inPortalThread />,
35
+ [id, isParentMessage],
36
+ );
37
+
38
+ const enableHistoryDivider = useAgentStore((s) => {
39
+ const config = agentSelectors.currentAgentChatConfig(s);
40
+ return (
41
+ config.enableHistoryCount &&
42
+ historyLength > (config.historyCount ?? 0) &&
43
+ config.historyCount === historyLength - index
44
+ );
45
+ });
46
+
47
+ return (
48
+ <ChatItem
49
+ actionBar={actionBar}
50
+ disableEditing={isParentMessage}
51
+ enableHistoryDivider={enableHistoryDivider}
52
+ endRender={endRender}
53
+ id={id}
54
+ inPortalThread
55
+ index={index}
56
+ />
57
+ );
58
+ });
59
+
60
+ ThreadChatItem.displayName = 'ThreadChatItem';
61
+
62
+ export default ThreadChatItem;
@@ -0,0 +1,49 @@
1
+ import React, { memo, useCallback } from 'react';
2
+ import { Flexbox } from 'react-layout-kit';
3
+
4
+ import { SkeletonList, VirtualizedList } from '@/features/Conversation';
5
+ import { useChatStore } from '@/store/chat';
6
+ import { threadSelectors } from '@/store/chat/selectors';
7
+
8
+ import ThreadChatItem from './ChatItem';
9
+
10
+ interface ChatListProps {
11
+ mobile?: boolean;
12
+ }
13
+
14
+ const ChatList = memo(({ mobile }: ChatListProps) => {
15
+ const data = useChatStore(threadSelectors.portalDisplayChatIDs);
16
+ const isInit = useChatStore((s) => s.threadsInit);
17
+
18
+ const useFetchThreads = useChatStore((s) => s.useFetchThreads);
19
+
20
+ useFetchThreads();
21
+
22
+ const itemContent = useCallback(
23
+ (index: number, id: string) => <ThreadChatItem id={id} index={index} />,
24
+ [mobile],
25
+ );
26
+
27
+ if (!isInit)
28
+ return (
29
+ <Flexbox flex={1} height={'100%'}>
30
+ <SkeletonList mobile={mobile} />
31
+ </Flexbox>
32
+ );
33
+
34
+ return (
35
+ <Flexbox
36
+ flex={1}
37
+ style={{
38
+ overflowX: 'hidden',
39
+ overflowY: 'auto',
40
+ position: 'relative',
41
+ }}
42
+ width={'100%'}
43
+ >
44
+ <VirtualizedList dataSource={data} itemContent={itemContent} mobile={mobile} />
45
+ </Flexbox>
46
+ );
47
+ });
48
+
49
+ export default ChatList;
@@ -0,0 +1,19 @@
1
+ import { Icon, Tag } from '@lobehub/ui';
2
+ import { Divider } from 'antd';
3
+ import { GitBranch } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ const ThreadDivider = memo(() => {
8
+ const { t } = useTranslation('chat');
9
+
10
+ return (
11
+ <div style={{ padding: '0 20px' }}>
12
+ <Divider style={{ margin: 0, padding: '20px 0' }}>
13
+ <Tag icon={<Icon icon={GitBranch} />}>{t('thread.divider')}</Tag>
14
+ </Divider>
15
+ </div>
16
+ );
17
+ });
18
+
19
+ export default ThreadDivider;
@@ -0,0 +1,28 @@
1
+ import { Suspense, memo } from 'react';
2
+ import { Flexbox } from 'react-layout-kit';
3
+
4
+ import { SkeletonList } from '@/features/Conversation';
5
+
6
+ import ChatInput from './ChatInput';
7
+ import ChatList from './ChatList';
8
+
9
+ interface ConversationProps {
10
+ mobile?: boolean;
11
+ }
12
+
13
+ const Conversation = memo<ConversationProps>(({ mobile }) => (
14
+ <Flexbox height={'100%'}>
15
+ <Suspense
16
+ fallback={
17
+ <Flexbox flex={1} height={'100%'}>
18
+ <SkeletonList mobile={mobile} />
19
+ </Flexbox>
20
+ }
21
+ >
22
+ <ChatList mobile={mobile} />
23
+ </Suspense>
24
+ <ChatInput />
25
+ </Flexbox>
26
+ ));
27
+
28
+ export default Conversation;
@@ -0,0 +1,35 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { Typography } from 'antd';
3
+ import isEqual from 'fast-deep-equal';
4
+ import { ListTree } from 'lucide-react';
5
+ import { Flexbox } from 'react-layout-kit';
6
+
7
+ import BubblesLoading from '@/components/BubblesLoading';
8
+ import { LOADING_FLAT } from '@/const/message';
9
+ import { useChatStore } from '@/store/chat';
10
+ import { portalThreadSelectors } from '@/store/chat/selectors';
11
+ import { oneLineEllipsis } from '@/styles';
12
+
13
+ const Active = () => {
14
+ const currentThread = useChatStore(portalThreadSelectors.portalCurrentThread, isEqual);
15
+
16
+ return (
17
+ currentThread && (
18
+ <Flexbox align={'center'} gap={8} horizontal style={{ marginInlineStart: 8 }}>
19
+ <Icon icon={ListTree} size={{ fontSize: 20 }} />
20
+
21
+ <Typography.Text className={oneLineEllipsis} style={{ fontSize: 16, fontWeight: 'bold' }}>
22
+ {currentThread?.title === LOADING_FLAT ? (
23
+ <Flexbox flex={1} height={30} justify={'center'}>
24
+ <BubblesLoading />
25
+ </Flexbox>
26
+ ) : (
27
+ currentThread?.title
28
+ )}
29
+ </Typography.Text>
30
+ </Flexbox>
31
+ )
32
+ );
33
+ };
34
+
35
+ export default Active;
@@ -0,0 +1,37 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { Checkbox, Typography } from 'antd';
3
+ import { GitBranch } from 'lucide-react';
4
+ import { Flexbox } from 'react-layout-kit';
5
+
6
+ import { useChatStore } from '@/store/chat';
7
+ import { portalThreadSelectors } from '@/store/chat/selectors';
8
+ import { oneLineEllipsis } from '@/styles';
9
+ import { ThreadType } from '@/types/topic';
10
+
11
+ const NewThreadHeader = () => {
12
+ const [newThreadMode] = useChatStore((s) => [portalThreadSelectors.newThreadMode(s)]);
13
+
14
+ return (
15
+ <Flexbox>
16
+ <Flexbox align={'center'} gap={8} horizontal style={{ marginInlineStart: 8 }}>
17
+ <Icon icon={GitBranch} size={{ fontSize: 20 }} />
18
+ <Typography.Text className={oneLineEllipsis} style={{ fontSize: 16, fontWeight: 'bold' }}>
19
+ 开启新的子话题
20
+ </Typography.Text>
21
+ <Checkbox
22
+ checked={newThreadMode === ThreadType.Continuation}
23
+ onChange={(e) => {
24
+ useChatStore.setState({
25
+ newThreadMode: e.target.checked ? ThreadType.Continuation : ThreadType.Standalone,
26
+ });
27
+ }}
28
+ style={{ marginInlineStart: 12 }}
29
+ >
30
+ 包含话题上下文
31
+ </Checkbox>
32
+ </Flexbox>
33
+ </Flexbox>
34
+ );
35
+ };
36
+
37
+ export default NewThreadHeader;
@@ -0,0 +1,18 @@
1
+ import { Skeleton } from 'antd';
2
+
3
+ import { useChatStore } from '@/store/chat';
4
+
5
+ import ActiveThread from './Active';
6
+ import NewThread from './New';
7
+
8
+ const Header = () => {
9
+ const isInNew = useChatStore((s) => s.startToForkThread);
10
+
11
+ const isInit = useChatStore((s) => s.threadsInit);
12
+
13
+ if (!isInit) return <Skeleton.Button active size={'small'} style={{ height: 22, width: 200 }} />;
14
+
15
+ return isInNew ? <NewThread /> : <ActiveThread />;
16
+ };
17
+
18
+ export default Header;
@@ -0,0 +1,20 @@
1
+ import { ActionIcon } from '@lobehub/ui';
2
+ import { XIcon } from 'lucide-react';
3
+
4
+ import SidebarHeader from '@/components/SidebarHeader';
5
+ import { useChatStore } from '@/store/chat';
6
+
7
+ import Title from './Title';
8
+
9
+ const Header = () => {
10
+ const closeThreadPortal = useChatStore((s) => s.closeThreadPortal);
11
+ return (
12
+ <SidebarHeader
13
+ actions={<ActionIcon icon={XIcon} onClick={closeThreadPortal} />}
14
+ style={{ paddingBlock: 8, paddingInline: 8 }}
15
+ title={<Title />}
16
+ />
17
+ );
18
+ };
19
+
20
+ export default Header;
@@ -0,0 +1,8 @@
1
+ import { useChatStore } from '@/store/chat';
2
+ import { portalThreadSelectors } from '@/store/chat/selectors';
3
+
4
+ export const useEnable = () => useChatStore(portalThreadSelectors.showThread);
5
+
6
+ export const onClose = () => {
7
+ useChatStore.setState({ portalThreadId: undefined });
8
+ };
@@ -0,0 +1,12 @@
1
+ import { PortalImpl } from '../type';
2
+ import Chat from './Chat';
3
+ import Header from './Header';
4
+ import { onClose, useEnable } from './hook';
5
+
6
+ export const Thread: PortalImpl = {
7
+ Body: Chat,
8
+ Header,
9
+ Title: () => null,
10
+ onClose,
11
+ useEnable,
12
+ };
@@ -7,10 +7,11 @@ import { FilePreview } from './FilePreview';
7
7
  import { HomeBody, HomeTitle } from './Home';
8
8
  import { MessageDetail } from './MessageDetail';
9
9
  import { Plugins } from './Plugins';
10
+ import { Thread } from './Thread';
10
11
  import Header from './components/Header';
11
12
  import { PortalImpl } from './type';
12
13
 
13
- const items: PortalImpl[] = [MessageDetail, Artifacts, Plugins, FilePreview];
14
+ const items: PortalImpl[] = [Thread, MessageDetail, Artifacts, Plugins, FilePreview];
14
15
 
15
16
  export const PortalTitle = memo(() => {
16
17
  const enabledList: boolean[] = [];
@@ -6,6 +6,12 @@ import { useSessionStore } from '@/store/session';
6
6
  */
7
7
  export const useFetchTopics = () => {
8
8
  const [sessionId] = useSessionStore((s) => [s.activeId]);
9
- const [useFetchTopics] = useChatStore((s) => [s.useFetchTopics]);
9
+ const [activeTopicId, useFetchTopics, useFetchThreads] = useChatStore((s) => [
10
+ s.activeTopicId,
11
+ s.useFetchTopics,
12
+ s.useFetchThreads,
13
+ ]);
10
14
  useFetchTopics(sessionId);
15
+
16
+ useFetchThreads(activeTopicId);
11
17
  };
@@ -9,6 +9,7 @@ export default {
9
9
  agents: '助手',
10
10
  artifact: {
11
11
  generating: '生成中',
12
+ inThread: '子话题中无法查看,请切换到主对话区打开',
12
13
  thinking: '思考中',
13
14
  thought: '思考过程',
14
15
  unknownTitle: '未命名作品',
@@ -67,6 +68,7 @@ export default {
67
68
  },
68
69
  messageAction: {
69
70
  delAndRegenerate: '删除并重新生成',
71
+ deleteDisabledByThreads: '存在子话题,不能删除',
70
72
  regenerate: '重新生成',
71
73
  },
72
74
  newAgent: '新建助手',
@@ -123,6 +125,11 @@ export default {
123
125
  loading: '识别中...',
124
126
  prettifying: '润色中...',
125
127
  },
128
+ thread: {
129
+ divider: '子话题',
130
+ threadMessageCount: '{{messageCount}} 条消息',
131
+ title: '子话题',
132
+ },
126
133
  tokenDetails: {
127
134
  chats: '会话消息',
128
135
  historySummary: '历史总结',
@@ -152,8 +159,8 @@ export default {
152
159
  action: '语音朗读',
153
160
  clear: '删除语音',
154
161
  },
155
- updateAgent: '更新助理信息',
156
162
 
163
+ updateAgent: '更新助理信息',
157
164
  upload: {
158
165
  action: {
159
166
  fileUpload: '上传文件',
@@ -17,6 +17,9 @@ export default {
17
17
  back: '返回',
18
18
  batchDelete: '批量删除',
19
19
  blog: '产品博客',
20
+ branching: '创建子话题',
21
+ branchingDisable:
22
+ '「子话题」功能仅服务端版本可用,如需该功能,请切换到服务端部署模式或使用 LobeChat Cloud',
20
23
  cancel: '取消',
21
24
  changelog: '更新日志',
22
25
  close: '关闭',
@@ -16,6 +16,7 @@ import portal from './portal';
16
16
  import providers from './providers';
17
17
  import ragEval from './ragEval';
18
18
  import setting from './setting';
19
+ import thread from './thread';
19
20
  import tool from './tool';
20
21
  import topic from './topic';
21
22
  import welcome from './welcome';
@@ -39,6 +40,7 @@ const resources = {
39
40
  providers,
40
41
  ragEval,
41
42
  setting,
43
+ thread,
42
44
  tool,
43
45
  topic,
44
46
  welcome,
@@ -391,6 +391,11 @@ export default {
391
391
  modelDesc: '指定用于优化用户提问的模型',
392
392
  title: '知识库提问重写',
393
393
  },
394
+ thread: {
395
+ label: '子话题命名模型',
396
+ modelDesc: '指定用于子话题自动重命名的模型',
397
+ title: '子话题自动命名',
398
+ },
394
399
  title: '系统助手',
395
400
  topic: {
396
401
  label: '话题命名模型',
@@ -0,0 +1,5 @@
1
+ export default {
2
+ actions: {
3
+ confirmRemoveThread: '即将删除该子话题,删除后将不可恢复,请谨慎操作。',
4
+ },
5
+ };
@@ -13,6 +13,7 @@ import { pluginRouter } from './plugin';
13
13
  import { ragEvalRouter } from './ragEval';
14
14
  import { sessionRouter } from './session';
15
15
  import { sessionGroupRouter } from './sessionGroup';
16
+ import { threadRouter } from './thread';
16
17
  import { topicRouter } from './topic';
17
18
  import { userRouter } from './user';
18
19
 
@@ -28,6 +29,7 @@ export const lambdaRouter = router({
28
29
  ragEval: ragEvalRouter,
29
30
  session: sessionRouter,
30
31
  sessionGroup: sessionGroupRouter,
32
+ thread: threadRouter,
31
33
  topic: topicRouter,
32
34
  user: userRouter,
33
35
  });
@@ -0,0 +1,83 @@
1
+ import { z } from 'zod';
2
+
3
+ import { MessageModel } from '@/database/server/models/message';
4
+ import { ThreadModel } from '@/database/server/models/thread';
5
+ import { insertThreadSchema } from '@/database/server/schemas/lobechat';
6
+ import { authedProcedure, router } from '@/libs/trpc';
7
+ import { ThreadItem, createThreadSchema } from '@/types/topic/thread';
8
+
9
+ const threadProcedure = authedProcedure.use(async (opts) => {
10
+ const { ctx } = opts;
11
+
12
+ return opts.next({
13
+ ctx: {
14
+ messageModel: new MessageModel(ctx.userId),
15
+ threadModel: new ThreadModel(ctx.userId),
16
+ },
17
+ });
18
+ });
19
+
20
+ export const threadRouter = router({
21
+ createThread: threadProcedure.input(createThreadSchema).mutation(async ({ input, ctx }) => {
22
+ const thread = await ctx.threadModel.create({
23
+ parentThreadId: input.parentThreadId,
24
+ sourceMessageId: input.sourceMessageId,
25
+ title: input.title,
26
+ topicId: input.topicId,
27
+ type: input.type,
28
+ });
29
+
30
+ return thread?.id;
31
+ }),
32
+ createThreadWithMessage: threadProcedure
33
+ .input(
34
+ createThreadSchema.extend({
35
+ message: z.any(),
36
+ }),
37
+ )
38
+ .mutation(async ({ input, ctx }) => {
39
+ const thread = await ctx.threadModel.create({
40
+ parentThreadId: input.parentThreadId,
41
+ sourceMessageId: input.sourceMessageId,
42
+ title: input.message.content.slice(0, 20),
43
+ topicId: input.topicId,
44
+ type: input.type,
45
+ });
46
+
47
+ const message = await ctx.messageModel.create({ ...input.message, threadId: thread?.id });
48
+
49
+ return { messageId: message?.id, threadId: thread?.id };
50
+ }),
51
+ getThread: threadProcedure.query(async ({ ctx }): Promise<ThreadItem[]> => {
52
+ return ctx.threadModel.query() as any;
53
+ }),
54
+
55
+ getThreads: threadProcedure
56
+ .input(z.object({ topicId: z.string() }))
57
+ .query(async ({ input, ctx }) => {
58
+ return ctx.threadModel.queryByTopicId(input.topicId);
59
+ }),
60
+
61
+ removeAllThreads: threadProcedure.mutation(async ({ ctx }) => {
62
+ return ctx.threadModel.deleteAll();
63
+ }),
64
+
65
+ removeThread: threadProcedure
66
+ .input(z.object({ id: z.string(), removeChildren: z.boolean().optional() }))
67
+ .mutation(async ({ input, ctx }) => {
68
+ return ctx.threadModel.delete(input.id);
69
+ }),
70
+
71
+ updateThread: threadProcedure
72
+ .input(
73
+ z.object({
74
+ id: z.string(),
75
+ value: insertThreadSchema.partial(),
76
+ }),
77
+ )
78
+ .mutation(async ({ input, ctx }) => {
79
+ return ctx.threadModel.update(input.id, input.value);
80
+ }),
81
+ });
82
+
83
+ export type ThreadRouter = typeof threadRouter;
@@ -0,0 +1,54 @@
1
+ import { INBOX_SESSION_ID } from '@/const/session';
2
+ import { lambdaClient } from '@/libs/trpc/client';
3
+ import { CreateMessageParams } from '@/types/message';
4
+ import { CreateThreadParams, ThreadItem } from '@/types/topic';
5
+
6
+ interface CreateThreadWithMessageParams extends CreateThreadParams {
7
+ message: CreateMessageParams;
8
+ }
9
+ export class ThreadService {
10
+ getThreads = (topicId: string): Promise<ThreadItem[]> => {
11
+ return lambdaClient.thread.getThreads.query({ topicId });
12
+ };
13
+
14
+ createThreadWithMessage({
15
+ message,
16
+ ...params
17
+ }: CreateThreadWithMessageParams): Promise<{ messageId: string; threadId: string }> {
18
+ return lambdaClient.thread.createThreadWithMessage.mutate({
19
+ ...params,
20
+ message: { ...message, sessionId: this.toDbSessionId(message.sessionId) },
21
+ });
22
+ }
23
+
24
+ // createThread(params: CreateThreadParams): Promise<string> {
25
+ // return lambdaClient.thread.createThread.mutate(params);
26
+ // }
27
+
28
+ updateThread(id: string, data: Partial<ThreadItem>): Promise<any> {
29
+ return lambdaClient.thread.updateThread.mutate({ id, value: data });
30
+ }
31
+
32
+ //
33
+ removeThread(id: string): Promise<any> {
34
+ return lambdaClient.thread.removeThread.mutate({ id });
35
+ }
36
+ //
37
+ // removeThreads(sessionId: string): Promise<any> {
38
+ // return lambdaClient.thread.batchDeleteBySessionId.mutate({ id: this.toDbSessionId(sessionId) });
39
+ // }
40
+ //
41
+ // batchRemoveThreads(topics: string[]): Promise<any> {
42
+ // return lambdaClient.thread.batchDelete.mutate({ ids: topics });
43
+ // }
44
+ //
45
+ // removeAllThread(): Promise<any> {
46
+ // return lambdaClient.thread.removeAllThreads.mutate();
47
+ // }
48
+
49
+ private toDbSessionId(sessionId: string | undefined) {
50
+ return sessionId === INBOX_SESSION_ID ? null : sessionId;
51
+ }
52
+ }
53
+
54
+ export const threadService = new ThreadService();
@@ -5,12 +5,14 @@ import { ChatMessageState, initialMessageState } from './slices/message/initialS
5
5
  import { ChatShareState, initialShareState } from './slices/share/initialState';
6
6
  import { ChatTopicState, initialTopicState } from './slices/topic/initialState';
7
7
  import { ChatAIChatState, initialAiChatState } from './slices/aiChat/initialState';
8
+ import { ChatThreadState, initialThreadState } from './slices/thread/initialState';
8
9
 
9
10
  export type ChatStoreState = ChatTopicState &
10
11
  ChatMessageState &
11
12
  ChatAIChatState &
12
13
  ChatToolState &
13
14
  ChatShareState &
15
+ ChatThreadState &
14
16
  ChatPortalState;
15
17
 
16
18
  export const initialState: ChatStoreState = {
@@ -19,6 +21,7 @@ export const initialState: ChatStoreState = {
19
21
  ...initialTopicState,
20
22
  ...initialToolState,
21
23
  ...initialShareState,
24
+ ...initialThreadState,
22
25
  ...initialChatPortalState,
23
26
 
24
27
  // cloud
@@ -1,4 +1,5 @@
1
1
  export { chatToolSelectors } from './slices/builtinTool/selectors';
2
2
  export { chatSelectors } from './slices/message/selectors';
3
- export { chatPortalSelectors } from './slices/portal/selectors';
3
+ export * from './slices/portal/selectors';
4
+ export { threadSelectors } from './slices/thread/selectors';
4
5
  export { topicSelectors } from './slices/topic/selectors';
@@ -527,7 +527,7 @@ describe('chatMessage actions', () => {
527
527
  await result.current.regenerateMessage(messageId);
528
528
  });
529
529
 
530
- expect(resendMessageSpy).toHaveBeenCalledWith(messageId, 'abc');
530
+ expect(resendMessageSpy).toHaveBeenCalledWith(messageId, { traceId: 'abc' });
531
531
  });
532
532
  });
533
533
 
@@ -243,7 +243,7 @@ describe('chatRAG actions', () => {
243
243
  }) as ChatMessage,
244
244
  );
245
245
 
246
- vi.spyOn(chatSelectors, 'currentChatsWithHistoryConfig').mockReturnValue([
246
+ vi.spyOn(chatSelectors, 'mainAIChatsWithHistoryConfig').mockReturnValue([
247
247
  { content: 'history' },
248
248
  ] as ChatMessage[]);
249
249