@lobehub/chat 1.33.5 → 1.34.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.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/chat.json +7 -0
- package/locales/ar/common.json +2 -0
- package/locales/ar/models.json +24 -0
- package/locales/ar/setting.json +5 -0
- package/locales/ar/thread.json +5 -0
- package/locales/bg-BG/chat.json +7 -0
- package/locales/bg-BG/common.json +2 -0
- package/locales/bg-BG/models.json +24 -0
- package/locales/bg-BG/setting.json +5 -0
- package/locales/bg-BG/thread.json +5 -0
- package/locales/de-DE/chat.json +7 -0
- package/locales/de-DE/common.json +2 -0
- package/locales/de-DE/models.json +24 -0
- package/locales/de-DE/setting.json +5 -0
- package/locales/de-DE/thread.json +5 -0
- package/locales/en-US/chat.json +7 -0
- package/locales/en-US/common.json +2 -0
- package/locales/en-US/models.json +24 -0
- package/locales/en-US/setting.json +5 -0
- package/locales/en-US/thread.json +5 -0
- package/locales/es-ES/chat.json +7 -0
- package/locales/es-ES/common.json +2 -0
- package/locales/es-ES/models.json +24 -0
- package/locales/es-ES/setting.json +5 -0
- package/locales/es-ES/thread.json +5 -0
- package/locales/fa-IR/chat.json +7 -0
- package/locales/fa-IR/common.json +2 -0
- package/locales/fa-IR/models.json +24 -0
- package/locales/fa-IR/setting.json +5 -0
- package/locales/fa-IR/thread.json +5 -0
- package/locales/fr-FR/chat.json +7 -0
- package/locales/fr-FR/common.json +2 -0
- package/locales/fr-FR/models.json +24 -0
- package/locales/fr-FR/setting.json +5 -0
- package/locales/fr-FR/thread.json +5 -0
- package/locales/it-IT/chat.json +7 -0
- package/locales/it-IT/common.json +2 -0
- package/locales/it-IT/models.json +24 -0
- package/locales/it-IT/setting.json +5 -0
- package/locales/it-IT/thread.json +5 -0
- package/locales/ja-JP/chat.json +7 -0
- package/locales/ja-JP/common.json +2 -0
- package/locales/ja-JP/models.json +24 -0
- package/locales/ja-JP/setting.json +5 -0
- package/locales/ja-JP/thread.json +5 -0
- package/locales/ko-KR/chat.json +7 -0
- package/locales/ko-KR/common.json +2 -0
- package/locales/ko-KR/models.json +24 -0
- package/locales/ko-KR/setting.json +5 -0
- package/locales/ko-KR/thread.json +5 -0
- package/locales/nl-NL/chat.json +7 -0
- package/locales/nl-NL/common.json +2 -0
- package/locales/nl-NL/models.json +24 -0
- package/locales/nl-NL/setting.json +5 -0
- package/locales/nl-NL/thread.json +5 -0
- package/locales/pl-PL/chat.json +7 -0
- package/locales/pl-PL/common.json +2 -0
- package/locales/pl-PL/models.json +24 -0
- package/locales/pl-PL/setting.json +5 -0
- package/locales/pl-PL/thread.json +5 -0
- package/locales/pt-BR/chat.json +7 -0
- package/locales/pt-BR/common.json +2 -0
- package/locales/pt-BR/models.json +24 -0
- package/locales/pt-BR/setting.json +5 -0
- package/locales/pt-BR/thread.json +5 -0
- package/locales/ru-RU/chat.json +7 -0
- package/locales/ru-RU/common.json +2 -0
- package/locales/ru-RU/models.json +24 -0
- package/locales/ru-RU/setting.json +5 -0
- package/locales/ru-RU/thread.json +5 -0
- package/locales/tr-TR/chat.json +7 -0
- package/locales/tr-TR/common.json +2 -0
- package/locales/tr-TR/models.json +24 -0
- package/locales/tr-TR/setting.json +5 -0
- package/locales/tr-TR/thread.json +5 -0
- package/locales/vi-VN/chat.json +7 -0
- package/locales/vi-VN/common.json +2 -0
- package/locales/vi-VN/models.json +24 -0
- package/locales/vi-VN/setting.json +5 -0
- package/locales/vi-VN/thread.json +5 -0
- package/locales/zh-CN/chat.json +7 -0
- package/locales/zh-CN/common.json +2 -0
- package/locales/zh-CN/models.json +24 -0
- package/locales/zh-CN/setting.json +5 -0
- package/locales/zh-CN/thread.json +5 -0
- package/locales/zh-TW/chat.json +7 -0
- package/locales/zh-TW/common.json +2 -0
- package/locales/zh-TW/models.json +24 -0
- package/locales/zh-TW/setting.json +5 -0
- package/locales/zh-TW/thread.json +5 -0
- package/package.json +1 -1
- package/src/app/(main)/chat/(workspace)/@conversation/default.tsx +2 -0
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatHydration/index.tsx +11 -2
- package/src/{features → app/(main)/chat/(workspace)/@conversation/features}/ChatInput/Desktop/Footer/index.tsx +7 -9
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +7 -2
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/Thread.tsx +62 -0
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/ThreadItem.tsx +68 -0
- package/src/app/(main)/chat/(workspace)/@conversation/features/ChatList/ChatItem/index.tsx +62 -2
- package/src/app/(main)/chat/(workspace)/@conversation/features/ThreadHydration.tsx +47 -0
- package/src/app/(main)/chat/(workspace)/@portal/_layout/Desktop.tsx +3 -2
- package/src/app/(main)/chat/(workspace)/@portal/_layout/Mobile.tsx +47 -6
- package/src/app/(main)/chat/(workspace)/@topic/features/SkeletonList.tsx +3 -2
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ByTimeMode/index.tsx +10 -3
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/FlatMode/index.tsx +1 -1
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ThreadItem/Content.tsx +164 -0
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/ThreadItem/index.tsx +98 -0
- package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{TopicItem.tsx → TopicItem/index.tsx} +33 -22
- package/src/app/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +12 -5
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/index.tsx +1 -2
- package/src/const/message.ts +2 -0
- package/src/const/settings/systemAgent.ts +1 -0
- package/src/database/server/migrations/0012_add_thread.sql +39 -0
- package/src/database/server/migrations/meta/0012_snapshot.json +3671 -0
- package/src/database/server/migrations/meta/_journal.json +7 -0
- package/src/database/server/models/_template.ts +2 -2
- package/src/database/server/models/message.ts +1 -0
- package/src/database/server/models/thread.ts +79 -0
- package/src/database/server/schemas/lobechat/message.ts +2 -1
- package/src/database/server/schemas/lobechat/relations.ts +13 -1
- package/src/database/server/schemas/lobechat/topic.ts +30 -1
- package/src/database/server/utils/idGenerator.ts +1 -0
- package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +6 -4
- package/src/features/ChatInput/ActionBar/Token/index.tsx +24 -5
- package/src/features/ChatInput/ActionBar/config.ts +3 -2
- package/src/features/ChatInput/Desktop/index.tsx +15 -7
- package/src/features/ChatInput/Mobile/index.tsx +4 -4
- package/src/features/Conversation/Actions/Assistant.tsx +24 -5
- package/src/features/Conversation/Actions/User.tsx +21 -4
- package/src/features/Conversation/Actions/index.ts +1 -66
- package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/index.tsx +3 -1
- package/src/features/Conversation/Messages/{Tool/index.tsx → Assistant/ToolCallItem/Tool.tsx} +10 -11
- package/src/features/Conversation/Messages/Assistant/ToolCallItem/index.tsx +5 -3
- package/src/features/Conversation/Messages/Assistant/index.tsx +22 -14
- package/src/features/Conversation/Messages/index.ts +0 -2
- package/src/features/Conversation/components/AutoScroll.tsx +1 -1
- package/src/features/Conversation/components/ChatItem/ActionsBar.tsx +79 -5
- package/src/features/Conversation/components/ChatItem/InPortalThreadContext.ts +3 -0
- package/src/features/Conversation/components/ChatItem/index.tsx +16 -5
- package/src/features/Conversation/components/MarkdownElements/LobeArtifact/Render/index.tsx +9 -1
- package/src/features/Conversation/components/ThreadDivider/index.tsx +19 -0
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +19 -4
- package/src/features/Portal/Thread/Chat/ChatInput/Footer.tsx +90 -0
- package/src/features/Portal/Thread/Chat/ChatInput/TextArea.tsx +30 -0
- package/src/features/Portal/Thread/Chat/ChatInput/index.tsx +66 -0
- package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +50 -0
- package/src/features/Portal/Thread/Chat/ChatItem.tsx +62 -0
- package/src/features/Portal/Thread/Chat/ChatList.tsx +49 -0
- package/src/features/Portal/Thread/Chat/ThreadDivider/index.tsx +19 -0
- package/src/features/Portal/Thread/Chat/index.tsx +28 -0
- package/src/features/Portal/Thread/Header/Active.tsx +35 -0
- package/src/features/Portal/Thread/Header/New.tsx +37 -0
- package/src/features/Portal/Thread/Header/Title.tsx +18 -0
- package/src/features/Portal/Thread/Header/index.tsx +20 -0
- package/src/features/Portal/Thread/hook.ts +8 -0
- package/src/features/Portal/Thread/index.ts +12 -0
- package/src/features/Portal/router.tsx +2 -1
- package/src/hooks/useFetchTopics.ts +7 -1
- package/src/locales/default/chat.ts +8 -1
- package/src/locales/default/common.ts +3 -0
- package/src/locales/default/index.ts +2 -0
- package/src/locales/default/setting.ts +5 -0
- package/src/locales/default/thread.ts +5 -0
- package/src/server/modules/AgentRuntime/index.test.ts +10 -0
- package/src/server/modules/AgentRuntime/index.ts +1 -1
- package/src/server/routers/lambda/index.ts +2 -0
- package/src/server/routers/lambda/thread.ts +83 -0
- package/src/services/thread.ts +54 -0
- package/src/store/chat/initialState.ts +3 -0
- package/src/store/chat/selectors.ts +2 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +1 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +1 -1
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +31 -8
- package/src/store/chat/slices/aiChat/actions/rag.ts +1 -1
- package/src/store/chat/slices/message/selectors.test.ts +3 -3
- package/src/store/chat/slices/message/selectors.ts +50 -29
- package/src/store/chat/slices/plugin/action.ts +26 -8
- package/src/store/chat/slices/portal/action.ts +1 -0
- package/src/store/chat/slices/portal/initialState.ts +1 -0
- package/src/store/chat/slices/portal/selectors/thread.ts +17 -0
- package/src/store/chat/slices/portal/selectors.ts +2 -0
- package/src/store/chat/slices/thread/action.ts +326 -0
- package/src/store/chat/slices/thread/initialState.ts +34 -0
- package/src/store/chat/slices/thread/reducer.ts +48 -0
- package/src/store/chat/slices/thread/selectors/index.ts +202 -0
- package/src/store/chat/slices/thread/selectors/util.ts +22 -0
- package/src/store/chat/slices/topic/action.ts +5 -1
- package/src/store/chat/store.ts +5 -2
- package/src/store/global/initialState.ts +4 -0
- package/src/store/global/selectors.ts +4 -0
- package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
- package/src/types/message/index.ts +17 -1
- package/src/types/topic/index.ts +1 -0
- package/src/types/topic/thread.ts +42 -0
- package/src/types/user/settings/systemAgent.ts +1 -0
- package/src/app/(main)/chat/(workspace)/@portal/features/Header.tsx +0 -11
- package/src/app/(main)/chat/(workspace)/_layout/Mobile/PortalModal.tsx +0 -35
- /package/src/{features → app/(main)/chat/(workspace)/@conversation/features}/ChatInput/Desktop/Footer/SendMore.tsx +0 -0
- /package/src/{features → app/(main)/chat/(workspace)/@conversation/features}/ChatInput/Desktop/Footer/ShortcutHint.tsx +0 -0
- /package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{DefaultContent.tsx → TopicItem/DefaultContent.tsx} +0 -0
- /package/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/{TopicContent.tsx → TopicItem/TopicContent.tsx} +0 -0
- /package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/PluginResultJSON.tsx +0 -0
- /package/src/features/Conversation/Messages/{Tool → Assistant/ToolCallItem}/Inspector/Settings.tsx +0 -0
- /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) => [
|
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: '上传文件',
|
@@ -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,
|
@@ -273,6 +273,16 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
273
273
|
expect(runtime['_runtime']).toBeInstanceOf(LobeQwenAI);
|
274
274
|
});
|
275
275
|
|
276
|
+
it('Qwen AI provider: without endpoint', async () => {
|
277
|
+
const jwtPayload: JWTPayload = { apiKey: 'user-qwen-key' };
|
278
|
+
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
279
|
+
|
280
|
+
// 假设 LobeQwenAI 是 Qwen 提供者的实现类
|
281
|
+
expect(runtime['_runtime']).toBeInstanceOf(LobeQwenAI);
|
282
|
+
// endpoint 不存在,应返回 DEFAULT_BASE_URL
|
283
|
+
expect(runtime['_runtime'].baseURL).toBe('https://dashscope.aliyuncs.com/compatible-mode/v1');
|
284
|
+
});
|
285
|
+
|
276
286
|
it('Bedrock AI provider: without apikey', async () => {
|
277
287
|
const jwtPayload = {};
|
278
288
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Bedrock, jwtPayload);
|
@@ -40,7 +40,7 @@ const getLlmOptionsFromPayload = (provider: string, payload: JWTPayload) => {
|
|
40
40
|
const apiKey = apiKeyManager.pick(payload?.apiKey || llmConfig[`${upperProvider}_API_KEY`]);
|
41
41
|
const baseURL = payload?.endpoint || process.env[`${upperProvider}_PROXY_URL`];
|
42
42
|
|
43
|
-
return { apiKey, baseURL };
|
43
|
+
return baseURL ? { apiKey, baseURL } : { apiKey };
|
44
44
|
}
|
45
45
|
|
46
46
|
case ModelProvider.Azure: {
|
@@ -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
|
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, '
|
246
|
+
vi.spyOn(chatSelectors, 'mainAIChatsWithHistoryConfig').mockReturnValue([
|
247
247
|
{ content: 'history' },
|
248
248
|
] as ChatMessage[]);
|
249
249
|
|