@lobehub/chat 0.105.2 → 0.107.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.
- package/CHANGELOG.md +50 -0
- package/README.md +18 -0
- package/README.zh-CN.md +18 -0
- package/docs/Environment-Variable.md +4 -2
- package/docs/Environment-Variable.zh-CN.md +4 -2
- package/locales/en_US/common.json +19 -0
- package/locales/en_US/migration.json +45 -0
- package/locales/en_US/setting.json +7 -7
- package/locales/ja_JP/common.json +19 -0
- package/locales/ja_JP/migration.json +45 -0
- package/locales/ja_JP/setting.json +2 -2
- package/locales/ko_KR/common.json +19 -0
- package/locales/ko_KR/migration.json +45 -0
- package/locales/ko_KR/setting.json +2 -2
- package/locales/ru_RU/common.json +19 -0
- package/locales/ru_RU/migration.json +45 -0
- package/locales/ru_RU/setting.json +2 -2
- package/locales/zh_CN/common.json +19 -0
- package/locales/zh_CN/migration.json +45 -0
- package/locales/zh_CN/setting.json +1 -1
- package/locales/zh_TW/common.json +19 -0
- package/locales/zh_TW/migration.json +45 -0
- package/locales/zh_TW/setting.json +2 -2
- package/package.json +2 -1
- package/src/app/api/openai/chat/createChatCompletion.test.ts +4 -4
- package/src/app/api/openai/chat/createChatCompletion.ts +4 -10
- package/src/app/api/openai/createBizOpenAI/index.ts +1 -1
- package/src/app/api/openai/errorResponse.test.ts +1 -1
- package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +2 -1
- package/src/app/chat/(mobile)/features/ChatInput/SendButton.tsx +2 -2
- package/src/app/chat/features/ChatHeader/ShareButton/Inner.tsx +2 -5
- package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +2 -2
- package/src/app/chat/features/ChatInput/ActionBar/Clear.tsx +2 -2
- package/src/app/chat/features/ChatInput/ActionBar/ModelSwitch.tsx +1 -1
- package/src/app/chat/features/ChatInput/ActionBar/Token/TokenTag.tsx +8 -2
- package/src/app/chat/features/ChatInput/InputAreaInner/index.tsx +2 -2
- package/src/app/chat/features/ChatInput/STT/index.tsx +2 -2
- package/src/app/chat/features/ChatInput/Topic/index.tsx +2 -2
- package/src/app/chat/features/ChatInput/useSend.ts +3 -3
- package/src/app/chat/features/Conversation/ChatList/Actions/index.ts +2 -2
- package/src/app/chat/features/Conversation/ChatList/Error/ApiKeyForm.tsx +2 -2
- package/src/app/chat/features/Conversation/ChatList/Error/InvalidAccess.tsx +2 -2
- package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +2 -2
- package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.test.tsx +4 -4
- package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.tsx +30 -31
- package/src/app/chat/features/Conversation/ChatList/Extras/TTS.tsx +2 -2
- package/src/app/chat/features/Conversation/ChatList/Extras/Translate.tsx +2 -2
- package/src/app/chat/features/Conversation/ChatList/Extras/User.tsx +8 -5
- package/src/app/chat/features/Conversation/ChatList/Extras/index.ts +3 -3
- package/src/app/chat/features/Conversation/ChatList/Extras/type.ts +5 -0
- package/src/app/chat/features/Conversation/ChatList/Messages/Assistant.tsx +10 -6
- package/src/app/chat/features/Conversation/ChatList/Messages/Default.tsx +7 -3
- package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +7 -6
- package/src/app/chat/features/Conversation/ChatList/Messages/index.ts +7 -7
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +6 -6
- package/src/app/chat/features/Conversation/ChatList/index.tsx +16 -24
- package/src/app/chat/features/Conversation/ScrollAnchor/index.tsx +2 -2
- package/src/app/chat/features/Conversation/index.tsx +3 -3
- package/src/app/chat/features/Conversation/useInitConversation.ts +43 -0
- package/src/app/chat/features/Migration/ExportConfigButton.tsx +33 -0
- package/src/app/chat/features/Migration/Failed.tsx +115 -0
- package/src/app/chat/features/Migration/Modal.tsx +80 -0
- package/src/app/chat/features/Migration/Start.tsx +106 -0
- package/src/app/chat/features/Migration/UpgradeButton.tsx +74 -0
- package/src/app/chat/features/Migration/const.ts +15 -0
- package/src/app/chat/features/Migration/index.tsx +44 -0
- package/src/app/chat/features/SessionListContent/DefaultMode.tsx +54 -0
- package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +5 -4
- package/src/app/chat/features/SessionListContent/List/Item/index.tsx +22 -28
- package/src/app/chat/features/SessionListContent/SearchMode.tsx +15 -0
- package/src/app/chat/features/SessionListContent/index.tsx +4 -44
- package/src/app/chat/features/SessionSearchBar/index.tsx +11 -3
- package/src/app/chat/features/TopicListContent/Header.tsx +4 -4
- package/src/app/chat/features/TopicListContent/Topic/TopicContent.tsx +11 -9
- package/src/app/chat/features/TopicListContent/Topic/TopicItem.tsx +15 -13
- package/src/app/chat/features/TopicListContent/Topic/index.tsx +9 -16
- package/src/app/chat/features/TopicListContent/TopicSearchBar/index.tsx +13 -4
- package/src/app/chat/features/TopicListContent/index.tsx +1 -2
- package/src/app/chat/page.tsx +6 -1
- package/src/app/chat/settings/features/HeaderContent.tsx +3 -3
- package/src/app/home/Redirect.tsx +13 -7
- package/src/app/settings/(mobile)/features/ExtraList.tsx +12 -4
- package/src/app/settings/common/Common.tsx +16 -4
- package/src/app/welcome/features/Banner/index.tsx +9 -10
- package/src/chains/summaryTitle.ts +5 -2
- package/src/components/DataStyleModal/index.tsx +64 -0
- package/src/config/client.ts +5 -0
- package/src/const/session.ts +21 -0
- package/src/const/settings.ts +1 -1
- package/src/database/core/__tests__/db.test.ts +8 -5
- package/src/database/core/__tests__/model.test.ts +3 -3
- package/src/database/core/db.ts +38 -10
- package/src/database/core/index.ts +1 -0
- package/src/database/core/model.ts +121 -10
- package/src/database/core/schemas.ts +22 -0
- package/src/database/core/types/db.ts +13 -0
- package/src/database/models/__DEBUG.ts +119 -0
- package/src/database/models/{file.test.ts → __tests__/file.test.ts} +19 -6
- package/src/database/models/__tests__/message.test.ts +314 -0
- package/src/database/models/__tests__/session.test.ts +117 -0
- package/src/database/models/__tests__/topic.test.ts +250 -0
- package/src/database/models/file.ts +9 -5
- package/src/database/models/message.ts +186 -0
- package/src/database/models/session.ts +160 -0
- package/src/database/models/topic.ts +196 -0
- package/src/{types/database → database/schemas}/files.ts +2 -2
- package/src/database/schemas/message.ts +37 -0
- package/src/database/schemas/session.ts +56 -0
- package/src/database/schemas/topic.ts +12 -0
- package/src/features/AgentSetting/AgentConfig/index.tsx +8 -1
- package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +1 -1
- package/src/features/AgentSetting/store/action.ts +1 -1
- package/src/features/AgentSetting/store/initialState.ts +1 -1
- package/src/features/AgentSetting/store/reducers/config.ts +1 -1
- package/src/features/DataImporter/index.tsx +240 -0
- package/src/features/DebugUI/index.tsx +34 -0
- package/src/features/SideBar/BottomActions.tsx +8 -14
- package/src/hooks/useImportConfig.ts +13 -30
- package/src/hooks/useTTS.ts +1 -1
- package/src/layout/GlobalLayout/index.tsx +16 -1
- package/src/locales/default/common.ts +19 -0
- package/src/locales/default/migration.ts +47 -0
- package/src/locales/default/setting.ts +1 -1
- package/src/locales/resources/en_US.ts +2 -0
- package/src/locales/resources/ja_JP.ts +2 -0
- package/src/locales/resources/ko_KR.ts +2 -0
- package/src/locales/resources/ru_RU.ts +2 -0
- package/src/locales/resources/zh_CN.ts +2 -0
- package/src/locales/resources/zh_TW.ts +2 -0
- package/src/migrations/FromV0ToV1.ts +2 -4
- package/src/migrations/FromV1ToV2/fixtures/input-v1-session.json +191 -0
- package/src/migrations/FromV1ToV2/fixtures/output-v2.json +202 -0
- package/src/migrations/FromV1ToV2/index.ts +82 -0
- package/src/migrations/FromV1ToV2/migrations.test.ts +224 -0
- package/src/migrations/FromV1ToV2/types/v1.ts +78 -0
- package/src/migrations/FromV1ToV2/types/v2.ts +52 -0
- package/src/{utils → migrations}/VersionController.test.ts +1 -3
- package/src/{utils → migrations}/VersionController.ts +5 -2
- package/src/migrations/index.ts +17 -3
- package/src/services/__tests__/file.test.ts +3 -3
- package/src/services/chat.ts +66 -5
- package/src/services/config.ts +184 -0
- package/src/services/debug.ts +13 -0
- package/src/services/file.ts +6 -2
- package/src/services/github.ts +25 -0
- package/src/services/message.ts +79 -0
- package/src/services/session.ts +63 -0
- package/src/services/topic.ts +56 -0
- package/src/store/chat/actions/enhance.ts +95 -0
- package/src/store/{session/slices/chat → chat}/actions/message.ts +225 -247
- package/src/store/{session/slices/chat → chat}/actions/plugin.ts +29 -34
- package/src/store/{session/slices/chat → chat}/actions/share.ts +9 -13
- package/src/store/chat/actions/topic.ts +197 -0
- package/src/store/chat/helpers.ts +23 -0
- package/src/store/chat/index.ts +3 -0
- package/src/store/chat/initialState.ts +48 -0
- package/src/store/chat/reducers/message.test.ts +83 -0
- package/src/store/chat/reducers/message.ts +83 -0
- package/src/store/chat/selectors/__tests__/topic.test.ts +43 -0
- package/src/store/chat/selectors/chat.ts +119 -0
- package/src/store/chat/selectors/index.ts +2 -0
- package/src/store/chat/selectors/topic.ts +28 -0
- package/src/store/chat/store.ts +63 -0
- package/src/store/files/slices/images/action.ts +6 -4
- package/src/store/global/selectors/__snapshots__/settings.test.ts.snap +99 -0
- package/src/store/global/selectors/settings.test.ts +51 -0
- package/src/store/global/selectors/settings.ts +36 -5
- package/src/store/global/store.ts +1 -1
- package/src/store/middleware/createHyperStorage/index.ts +40 -24
- package/src/store/middleware/createHyperStorage/keyMapper.ts +8 -2
- package/src/store/middleware/createHyperStorage/type.ts +10 -8
- package/src/store/plugin/slices/plugin/action.ts +1 -1
- package/src/store/plugin/store.ts +2 -4
- package/src/store/session/helpers.ts +1 -1
- package/src/store/session/hooks/useSessionHydrated.ts +6 -10
- package/src/store/session/initialState.ts +26 -7
- package/src/store/session/selectors.ts +1 -2
- package/src/store/session/slices/agent/action.ts +26 -35
- package/src/store/session/slices/agent/selectors.ts +32 -37
- package/src/store/session/slices/session/action.ts +81 -88
- package/src/store/session/slices/session/helpers.ts +19 -0
- package/src/store/session/slices/session/initialState.ts +7 -10
- package/src/store/session/slices/session/selectors/export.ts +12 -9
- package/src/store/session/slices/session/selectors/index.ts +2 -35
- package/src/store/session/slices/session/selectors/list.test.ts +25 -203
- package/src/store/session/slices/session/selectors/list.ts +40 -78
- package/src/store/session/store.ts +11 -17
- package/src/types/agent/index.ts +61 -0
- package/src/types/chatMessage.ts +11 -20
- package/src/types/exportConfig.ts +7 -2
- package/src/types/files.ts +2 -2
- package/src/types/llm.ts +1 -1
- package/src/types/meta.ts +24 -12
- package/src/types/openai/chat.ts +9 -1
- package/src/types/openai/functionCall.ts +4 -0
- package/src/types/session.ts +10 -94
- package/src/types/settings.ts +2 -0
- package/src/types/translate.ts +2 -1
- package/src/utils/config.ts +5 -11
- package/src/utils/fetch.ts +8 -2
- package/src/app/chat/features/Conversation/usePluginsInit.ts +0 -14
- package/src/database/core/schema.ts +0 -9
- package/src/helpers/export.ts +0 -69
- package/src/hooks/useExportConfig.ts +0 -6
- package/src/store/session/slices/chat/actions/index.ts +0 -32
- package/src/store/session/slices/chat/actions/topic.ts +0 -171
- package/src/store/session/slices/chat/actions/translate.ts +0 -113
- package/src/store/session/slices/chat/helpers.ts +0 -9
- package/src/store/session/slices/chat/initialState.ts +0 -13
- package/src/store/session/slices/chat/reducers/message.test.ts +0 -319
- package/src/store/session/slices/chat/reducers/message.ts +0 -156
- package/src/store/session/slices/chat/reducers/topic.test.ts +0 -179
- package/src/store/session/slices/chat/reducers/topic.ts +0 -81
- package/src/store/session/slices/chat/selectors/chat.ts +0 -111
- package/src/store/session/slices/chat/selectors/index.ts +0 -26
- package/src/store/session/slices/chat/selectors/topic.ts +0 -30
- package/src/store/session/slices/chat/selectors/utils.test.ts +0 -385
- package/src/store/session/slices/chat/selectors/utils.ts +0 -91
- package/src/store/session/slices/chat/utils.ts +0 -13
- package/src/store/session/slices/session/reducers/session.test.ts +0 -577
- package/src/store/session/slices/session/reducers/session.ts +0 -182
- package/src/types/database/db.ts +0 -4
- /package/src/store/{session/slices/chat → chat}/reducers/files.test.ts +0 -0
- /package/src/store/{session/slices/chat → chat}/reducers/files.ts +0 -0
|
@@ -1,56 +1,59 @@
|
|
|
1
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
|
|
2
|
+
// Note: DON'T REMOVE THE FIRST LINE
|
|
3
|
+
// Disable the auto sort key eslint rule to make the code more logic and readable
|
|
1
4
|
import { template } from 'lodash-es';
|
|
5
|
+
import useSWR, { SWRResponse, mutate } from 'swr';
|
|
2
6
|
import { StateCreator } from 'zustand/vanilla';
|
|
3
7
|
|
|
4
8
|
import { VISION_MODEL_WHITE_LIST } from '@/const/llm';
|
|
5
9
|
import { LOADING_FLAT } from '@/const/message';
|
|
6
10
|
import { VISION_MODEL_DEFAULT_MAX_TOKENS } from '@/const/settings';
|
|
11
|
+
import { CreateMessageParams } from '@/database/models/message';
|
|
12
|
+
import { DB_Message } from '@/database/schemas/message';
|
|
7
13
|
import { chatService } from '@/services/chat';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
14
|
+
import { messageService } from '@/services/message';
|
|
15
|
+
import { topicService } from '@/services/topic';
|
|
16
|
+
import { chatHelpers } from '@/store/chat/helpers';
|
|
17
|
+
import { ChatStore } from '@/store/chat/store';
|
|
18
|
+
import { useSessionStore } from '@/store/session';
|
|
19
|
+
import { agentSelectors } from '@/store/session/selectors';
|
|
10
20
|
import { ChatMessage } from '@/types/chatMessage';
|
|
11
|
-
import { OpenAIChatMessage, UserMessageContentPart } from '@/types/openai/chat';
|
|
12
21
|
import { fetchSSE } from '@/utils/fetch';
|
|
13
22
|
import { isFunctionMessageAtStart, testFunctionMessageAtEnd } from '@/utils/message';
|
|
14
23
|
import { setNamespace } from '@/utils/storeDebug';
|
|
15
|
-
import { nanoid } from '@/utils/uuid';
|
|
16
24
|
|
|
17
|
-
import { agentSelectors } from '../../agent/selectors';
|
|
18
|
-
import { sessionSelectors } from '../../session/selectors';
|
|
19
|
-
import { FileDispatch, filesReducer } from '../reducers/files';
|
|
20
25
|
import { MessageDispatch, messagesReducer } from '../reducers/message';
|
|
21
26
|
import { chatSelectors } from '../selectors';
|
|
22
|
-
import { getSlicedMessagesWithConfig } from '../utils';
|
|
23
27
|
|
|
24
|
-
const t = setNamespace('
|
|
28
|
+
const t = setNamespace('message');
|
|
25
29
|
|
|
26
|
-
/**
|
|
27
|
-
* 聊天操作
|
|
28
|
-
*/
|
|
29
30
|
export interface ChatMessageAction {
|
|
31
|
+
// create
|
|
32
|
+
resendMessage: (id: string) => Promise<void>;
|
|
33
|
+
sendMessage: (text: string, images?: { id: string; url: string }[]) => Promise<void>;
|
|
34
|
+
// delete
|
|
30
35
|
/**
|
|
31
|
-
*
|
|
32
|
-
*/
|
|
33
|
-
clearMessage: () => void;
|
|
34
|
-
/**
|
|
35
|
-
* 处理 ai 消息的核心逻辑(包含前处理与后处理)
|
|
36
|
-
* @param messages - 聊天消息数组
|
|
37
|
-
* @param parentId - 父消息 ID,可选
|
|
38
|
-
*/
|
|
39
|
-
coreProcessMessage: (messages: ChatMessage[], parentId: string) => Promise<void>;
|
|
40
|
-
/**
|
|
41
|
-
* 删除消息
|
|
42
|
-
* @param id - 消息 ID
|
|
36
|
+
* clear message on the active session
|
|
43
37
|
*/
|
|
44
|
-
|
|
38
|
+
clearMessage: () => Promise<void>;
|
|
39
|
+
deleteMessage: (id: string) => Promise<void>;
|
|
40
|
+
clearAllMessages: () => Promise<void>;
|
|
41
|
+
// update
|
|
42
|
+
updateInputMessage: (message: string) => void;
|
|
43
|
+
updateMessageContent: (id: string, content: string) => Promise<void>;
|
|
44
|
+
// query
|
|
45
|
+
useFetchMessages: (sessionId: string, topicId?: string) => SWRResponse<ChatMessage[]>;
|
|
46
|
+
stopGenerateMessage: () => void;
|
|
47
|
+
|
|
45
48
|
/**
|
|
46
|
-
*
|
|
49
|
+
* update message at the frontend point
|
|
50
|
+
* this method will not update messages to database
|
|
47
51
|
*/
|
|
48
|
-
|
|
52
|
+
dispatchMessage: (payload: MessageDispatch) => void;
|
|
49
53
|
/**
|
|
50
|
-
*
|
|
51
|
-
* @param payload - 消息分发参数
|
|
54
|
+
* core process of the AI message (include preprocess and postprocess)
|
|
52
55
|
*/
|
|
53
|
-
|
|
56
|
+
coreProcessMessage: (messages: ChatMessage[], parentId: string) => Promise<void>;
|
|
54
57
|
/**
|
|
55
58
|
* 实际获取 AI 响应
|
|
56
59
|
* @param messages - 聊天消息数组
|
|
@@ -65,135 +68,225 @@ export interface ChatMessageAction {
|
|
|
65
68
|
functionCallContent: string;
|
|
66
69
|
isFunctionCall: boolean;
|
|
67
70
|
}>;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 重新发送消息
|
|
71
|
-
* @param id - 消息 ID
|
|
72
|
-
*/
|
|
73
|
-
resendMessage: (id: string) => Promise<void>;
|
|
74
|
-
/**
|
|
75
|
-
* 发送消息
|
|
76
|
-
* @param text - 消息文本
|
|
77
|
-
*/
|
|
78
|
-
sendMessage: (text: string, images?: { id: string; url: string }[]) => Promise<void>;
|
|
79
|
-
stopGenerateMessage: () => void;
|
|
80
71
|
toggleChatLoading: (
|
|
81
72
|
loading: boolean,
|
|
82
73
|
id?: string,
|
|
83
74
|
action?: string,
|
|
84
75
|
) => AbortController | undefined;
|
|
85
|
-
|
|
86
|
-
updateInputMessage: (message: string) => void;
|
|
76
|
+
refreshMessages: () => Promise<void>;
|
|
87
77
|
}
|
|
88
78
|
|
|
79
|
+
const getAgentConfig = () => agentSelectors.currentAgentConfig(useSessionStore.getState());
|
|
80
|
+
|
|
89
81
|
export const chatMessage: StateCreator<
|
|
90
|
-
|
|
82
|
+
ChatStore,
|
|
91
83
|
[['zustand/devtools', never]],
|
|
92
84
|
[],
|
|
93
85
|
ChatMessageAction
|
|
94
86
|
> = (set, get) => ({
|
|
95
|
-
|
|
96
|
-
|
|
87
|
+
deleteMessage: async (id) => {
|
|
88
|
+
await messageService.removeMessage(id);
|
|
89
|
+
await get().refreshMessages();
|
|
90
|
+
},
|
|
91
|
+
clearMessage: async () => {
|
|
92
|
+
const { activeId, activeTopicId, refreshMessages, refreshTopic, switchTopic } = get();
|
|
97
93
|
|
|
98
|
-
|
|
94
|
+
await messageService.removeMessages(activeId, activeTopicId);
|
|
99
95
|
|
|
100
96
|
if (activeTopicId) {
|
|
101
|
-
|
|
97
|
+
await topicService.removeTopics(activeId);
|
|
102
98
|
}
|
|
99
|
+
await refreshTopic();
|
|
100
|
+
await refreshMessages();
|
|
103
101
|
|
|
104
102
|
// after remove topic , go back to default topic
|
|
105
|
-
|
|
103
|
+
switchTopic();
|
|
106
104
|
},
|
|
107
|
-
|
|
108
|
-
const {
|
|
105
|
+
clearAllMessages: async () => {
|
|
106
|
+
const { refreshMessages } = get();
|
|
107
|
+
await messageService.clearAllMessage();
|
|
108
|
+
await refreshMessages();
|
|
109
|
+
},
|
|
110
|
+
resendMessage: async (messageId) => {
|
|
111
|
+
// 1. 构造所有相关的历史记录
|
|
112
|
+
const chats = chatSelectors.currentChats(get());
|
|
109
113
|
|
|
110
|
-
const
|
|
114
|
+
const currentIndex = chats.findIndex((c) => c.id === messageId);
|
|
115
|
+
if (currentIndex < 0) return;
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
// 因为如果顺序反了,messages 中将包含新增的 ai message
|
|
114
|
-
const mid = nanoid();
|
|
117
|
+
const currentMessage = chats[currentIndex];
|
|
115
118
|
|
|
116
|
-
|
|
117
|
-
id: mid,
|
|
118
|
-
message: LOADING_FLAT,
|
|
119
|
-
parentId: userMessageId,
|
|
120
|
-
role: 'assistant',
|
|
121
|
-
type: 'addMessage',
|
|
122
|
-
});
|
|
119
|
+
let contextMessages: ChatMessage[] = [];
|
|
123
120
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
121
|
+
switch (currentMessage.role) {
|
|
122
|
+
case 'function':
|
|
123
|
+
case 'user': {
|
|
124
|
+
contextMessages = chats.slice(0, currentIndex + 1);
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case 'assistant': {
|
|
128
|
+
// 消息是 AI 发出的因此需要找到它的 user 消息
|
|
129
|
+
const userId = currentMessage.parentId;
|
|
130
|
+
const userIndex = chats.findIndex((c) => c.id === userId);
|
|
131
|
+
// 如果消息没有 parentId,那么同 user/function 模式
|
|
132
|
+
contextMessages = chats.slice(0, userIndex < 0 ? currentIndex + 1 : userIndex + 1);
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
127
135
|
}
|
|
128
136
|
|
|
129
|
-
|
|
130
|
-
|
|
137
|
+
if (contextMessages.length <= 0) return;
|
|
138
|
+
|
|
139
|
+
const { coreProcessMessage } = get();
|
|
140
|
+
|
|
141
|
+
const latestMsg = contextMessages.filter((s) => s.role === 'user').at(-1);
|
|
142
|
+
|
|
143
|
+
if (!latestMsg) return;
|
|
144
|
+
|
|
145
|
+
await coreProcessMessage(contextMessages, latestMsg.id);
|
|
146
|
+
},
|
|
147
|
+
sendMessage: async (message, files) => {
|
|
148
|
+
const { coreProcessMessage, activeTopicId, activeId } = get();
|
|
149
|
+
if (!message || !activeId) return;
|
|
150
|
+
|
|
151
|
+
const fileIdList = files?.map((f) => f.id);
|
|
152
|
+
|
|
153
|
+
let newMessage: CreateMessageParams = {
|
|
154
|
+
content: message,
|
|
155
|
+
// if message has attached with files, then add files to message and the agent
|
|
156
|
+
files: fileIdList,
|
|
157
|
+
role: 'user',
|
|
158
|
+
sessionId: activeId,
|
|
159
|
+
// if there is activeTopicId,then add topicId to message
|
|
160
|
+
topicId: activeTopicId,
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const id = await messageService.create(newMessage);
|
|
164
|
+
await get().refreshMessages();
|
|
165
|
+
|
|
166
|
+
// Get the current messages to generate AI response
|
|
167
|
+
const messages = chatSelectors.currentChats(get());
|
|
168
|
+
|
|
169
|
+
await coreProcessMessage(messages, id);
|
|
170
|
+
|
|
171
|
+
// check activeTopic and then auto create topic
|
|
172
|
+
const chats = chatSelectors.currentChats(get());
|
|
173
|
+
|
|
174
|
+
const agentConfig = getAgentConfig();
|
|
175
|
+
// if autoCreateTopic is false, then stop
|
|
176
|
+
if (!agentConfig.enableAutoCreateTopic) return;
|
|
177
|
+
|
|
178
|
+
if (!activeTopicId && chats.length >= agentConfig.autoCreateTopicThreshold) {
|
|
179
|
+
const { saveToTopic, switchTopic } = get();
|
|
180
|
+
const id = await saveToTopic();
|
|
181
|
+
if (id) switchTopic(id);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
stopGenerateMessage: () => {
|
|
186
|
+
const { abortController, toggleChatLoading } = get();
|
|
187
|
+
if (!abortController) return;
|
|
188
|
+
|
|
189
|
+
abortController.abort();
|
|
190
|
+
|
|
191
|
+
toggleChatLoading(false, undefined, t('stopGenerateMessage') as string);
|
|
192
|
+
},
|
|
193
|
+
updateInputMessage: (message) => {
|
|
194
|
+
set({ inputMessage: message }, false, t('updateInputMessage', message));
|
|
195
|
+
},
|
|
196
|
+
updateMessageContent: async (id, content) => {
|
|
197
|
+
const { dispatchMessage, refreshMessages } = get();
|
|
198
|
+
|
|
199
|
+
// Due to the async update method and refresh need about 100ms
|
|
200
|
+
// we need to update the message content at the frontend to avoid the update flick
|
|
201
|
+
// refs: https://medium.com/@kyledeguzmanx/what-are-optimistic-updates-483662c3e171
|
|
202
|
+
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: content });
|
|
203
|
+
|
|
204
|
+
await messageService.updateMessageContent(id, content);
|
|
205
|
+
await refreshMessages();
|
|
206
|
+
},
|
|
207
|
+
useFetchMessages: (sessionId, activeTopicId) =>
|
|
208
|
+
useSWR<ChatMessage[]>(
|
|
209
|
+
[sessionId, activeTopicId],
|
|
210
|
+
async ([sessionId, topicId]: [string, string | undefined]) =>
|
|
211
|
+
messageService.getMessages(sessionId, topicId),
|
|
212
|
+
{
|
|
213
|
+
onSuccess: (messages, key) => {
|
|
214
|
+
set(
|
|
215
|
+
{ activeId: sessionId, messages, messagesInit: true },
|
|
216
|
+
false,
|
|
217
|
+
t('useFetchMessages', {
|
|
218
|
+
messages,
|
|
219
|
+
queryKey: key,
|
|
220
|
+
}),
|
|
221
|
+
);
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
),
|
|
225
|
+
refreshMessages: async () => {
|
|
226
|
+
await mutate([get().activeId, get().activeTopicId]);
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
// the internal process method of the AI message
|
|
230
|
+
coreProcessMessage: async (messages, userMessageId) => {
|
|
231
|
+
const { fetchAIChatMessage, triggerFunctionCall, refreshMessages, activeTopicId } = get();
|
|
232
|
+
|
|
233
|
+
const { model } = getAgentConfig();
|
|
131
234
|
|
|
132
|
-
//
|
|
235
|
+
// 1. Add an empty message to place the AI response
|
|
236
|
+
const assistantMessage: DB_Message = {
|
|
237
|
+
role: 'assistant',
|
|
238
|
+
content: LOADING_FLAT,
|
|
239
|
+
fromModel: model,
|
|
240
|
+
|
|
241
|
+
parentId: userMessageId,
|
|
242
|
+
sessionId: get().activeId,
|
|
243
|
+
topicId: activeTopicId, // if there is activeTopicId,then add it to topicId
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const mid = await messageService.create(assistantMessage);
|
|
247
|
+
await refreshMessages();
|
|
248
|
+
|
|
249
|
+
// 2. fetch the AI response
|
|
133
250
|
const { isFunctionCall, content, functionCallAtEnd, functionCallContent } =
|
|
134
251
|
await fetchAIChatMessage(messages, mid);
|
|
135
252
|
|
|
136
|
-
//
|
|
253
|
+
// 3. if it's the function call message, trigger the function method
|
|
137
254
|
if (isFunctionCall) {
|
|
138
255
|
let functionId = mid;
|
|
139
256
|
|
|
257
|
+
// if the function call is at the end of the message, then create a new function message
|
|
140
258
|
if (functionCallAtEnd) {
|
|
141
259
|
// create a new separate message and remove the function call from the prev message
|
|
142
|
-
dispatchMessage({
|
|
143
|
-
id: mid,
|
|
144
|
-
key: 'content',
|
|
145
|
-
type: 'updateMessage',
|
|
146
|
-
value: content.replace(functionCallContent, ''),
|
|
147
|
-
});
|
|
148
260
|
|
|
149
|
-
|
|
150
|
-
dispatchMessage({
|
|
151
|
-
id: functionId,
|
|
152
|
-
message: functionCallContent,
|
|
153
|
-
parentId: userMessageId,
|
|
154
|
-
role: 'assistant',
|
|
155
|
-
type: 'addMessage',
|
|
156
|
-
});
|
|
261
|
+
await messageService.updateMessageContent(mid, content.replace(functionCallContent, ''));
|
|
157
262
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
263
|
+
const functionMessage: CreateMessageParams = {
|
|
264
|
+
role: 'function',
|
|
265
|
+
content: functionCallContent,
|
|
266
|
+
extra: {
|
|
267
|
+
fromModel: model,
|
|
268
|
+
},
|
|
269
|
+
parentId: userMessageId,
|
|
270
|
+
sessionId: get().activeId,
|
|
271
|
+
topicId: activeTopicId,
|
|
272
|
+
};
|
|
273
|
+
functionId = await messageService.create(functionMessage);
|
|
166
274
|
}
|
|
167
275
|
|
|
168
276
|
triggerFunctionCall(functionId);
|
|
169
277
|
}
|
|
170
278
|
},
|
|
171
|
-
|
|
172
|
-
deleteMessage: (id) => {
|
|
173
|
-
get().dispatchMessage({ id, type: 'deleteMessage' });
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
dispatchAgentFile: (payload) => {
|
|
177
|
-
const { activeId } = get();
|
|
178
|
-
const session = sessionSelectors.currentSession(get());
|
|
179
|
-
if (!activeId || !session) return;
|
|
180
|
-
|
|
181
|
-
const files = filesReducer(session.files || [], payload);
|
|
182
|
-
|
|
183
|
-
get().dispatchSession({ files, id: activeId, type: 'updateSessionFiles' });
|
|
184
|
-
},
|
|
185
279
|
dispatchMessage: (payload) => {
|
|
186
280
|
const { activeId } = get();
|
|
187
|
-
const session = sessionSelectors.currentSession(get());
|
|
188
|
-
if (!activeId || !session) return;
|
|
189
281
|
|
|
190
|
-
|
|
282
|
+
if (!activeId) return;
|
|
191
283
|
|
|
192
|
-
get().
|
|
193
|
-
},
|
|
284
|
+
const messages = messagesReducer(get().messages, payload);
|
|
194
285
|
|
|
286
|
+
set({ messages }, false, t(`dispatchMessage/${payload.type}`, payload));
|
|
287
|
+
},
|
|
195
288
|
fetchAIChatMessage: async (messages, assistantId) => {
|
|
196
|
-
const { dispatchMessage, toggleChatLoading } = get();
|
|
289
|
+
const { dispatchMessage, toggleChatLoading, refreshMessages } = get();
|
|
197
290
|
|
|
198
291
|
const abortController = toggleChatLoading(
|
|
199
292
|
true,
|
|
@@ -201,7 +294,7 @@ export const chatMessage: StateCreator<
|
|
|
201
294
|
t('generateMessage(start)', { assistantId, messages }) as string,
|
|
202
295
|
);
|
|
203
296
|
|
|
204
|
-
const config =
|
|
297
|
+
const config = getAgentConfig();
|
|
205
298
|
|
|
206
299
|
const compiler = template(config.inputTemplate, { interpolate: /{{([\S\s]+?)}}/g });
|
|
207
300
|
|
|
@@ -210,7 +303,7 @@ export const chatMessage: StateCreator<
|
|
|
210
303
|
// ================================== //
|
|
211
304
|
|
|
212
305
|
// 1. slice messages with config
|
|
213
|
-
let preprocessMsgs = getSlicedMessagesWithConfig(messages, config);
|
|
306
|
+
let preprocessMsgs = chatHelpers.getSlicedMessagesWithConfig(messages, config);
|
|
214
307
|
|
|
215
308
|
// 2. replace inputMessage template
|
|
216
309
|
preprocessMsgs = !config.inputTemplate
|
|
@@ -234,38 +327,18 @@ export const chatMessage: StateCreator<
|
|
|
234
327
|
preprocessMsgs.unshift({ content: config.systemRole, role: 'system' } as ChatMessage);
|
|
235
328
|
}
|
|
236
329
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
//
|
|
240
|
-
// for the models with visual ability, add image url to content
|
|
241
|
-
// refs: https://platform.openai.com/docs/guides/vision/quick-start
|
|
330
|
+
// 4. handle config for the vision model
|
|
331
|
+
// Due to vision model's default max_tokens is very small
|
|
332
|
+
// we need to set the max_tokens a larger one.
|
|
242
333
|
if (VISION_MODEL_WHITE_LIST.includes(config.model)) {
|
|
243
|
-
|
|
244
|
-
if (!m.files) return m;
|
|
245
|
-
|
|
246
|
-
const imageList = filesSelectors.getImageUrlOrBase64ByList(m.files)(
|
|
247
|
-
useFileStore.getState(),
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
if (imageList.length === 0) return m;
|
|
251
|
-
|
|
252
|
-
const content: UserMessageContentPart[] = [
|
|
253
|
-
{ text: m.content, type: 'text' },
|
|
254
|
-
...imageList.map(
|
|
255
|
-
(i) => ({ image_url: { detail: 'auto', url: i.url }, type: 'image_url' }) as const,
|
|
256
|
-
),
|
|
257
|
-
];
|
|
258
|
-
return { ...m, content };
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
// due to vision model's default max_tokens is very small, we need to set the max_tokens a larger one.
|
|
334
|
+
/* eslint-disable unicorn/no-lonely-if */
|
|
262
335
|
if (!config.params.max_tokens) config.params.max_tokens = VISION_MODEL_DEFAULT_MAX_TOKENS;
|
|
263
336
|
}
|
|
264
337
|
|
|
265
338
|
const fetcher = () =>
|
|
266
|
-
chatService.
|
|
339
|
+
chatService.createAssistantMessage(
|
|
267
340
|
{
|
|
268
|
-
messages:
|
|
341
|
+
messages: preprocessMsgs,
|
|
269
342
|
model: config.model,
|
|
270
343
|
...config.params,
|
|
271
344
|
plugins: config.plugins,
|
|
@@ -279,24 +352,29 @@ export const chatMessage: StateCreator<
|
|
|
279
352
|
let functionCallContent = '';
|
|
280
353
|
|
|
281
354
|
await fetchSSE(fetcher, {
|
|
282
|
-
onErrorHandle: (error) => {
|
|
283
|
-
|
|
355
|
+
onErrorHandle: async (error) => {
|
|
356
|
+
await messageService.updateMessageError(assistantId, error);
|
|
357
|
+
await refreshMessages();
|
|
358
|
+
},
|
|
359
|
+
onFinish: async (content) => {
|
|
360
|
+
// update the content after fetch result
|
|
361
|
+
await messageService.updateMessageContent(assistantId, content);
|
|
284
362
|
},
|
|
285
363
|
onMessageHandle: (text) => {
|
|
286
364
|
output += text;
|
|
287
|
-
|
|
365
|
+
// Note: Don't need change this to `messageService.updateMessageContent`
|
|
366
|
+
// because we don't need to update the message content to the database in the middle of the process
|
|
288
367
|
dispatchMessage({ id: assistantId, key: 'content', type: 'updateMessage', value: output });
|
|
289
368
|
|
|
290
369
|
// is this message is just a function call
|
|
291
|
-
if (isFunctionMessageAtStart(output))
|
|
292
|
-
isFunctionCall = true;
|
|
293
|
-
}
|
|
370
|
+
if (isFunctionMessageAtStart(output)) isFunctionCall = true;
|
|
294
371
|
},
|
|
295
372
|
});
|
|
296
373
|
|
|
297
374
|
toggleChatLoading(false, undefined, t('generateMessage(end)') as string);
|
|
298
375
|
|
|
299
|
-
// also exist message like this:
|
|
376
|
+
// also exist message like this:
|
|
377
|
+
// 请稍等,我帮您查询一下。{"function_call": {"name": "plugin-identifier____recommendClothes____standalone", "arguments": "{\n "mood": "",\n "gender": "man"\n}"}}
|
|
300
378
|
if (!isFunctionCall) {
|
|
301
379
|
const { content, valid } = testFunctionMessageAtEnd(output);
|
|
302
380
|
|
|
@@ -310,103 +388,6 @@ export const chatMessage: StateCreator<
|
|
|
310
388
|
|
|
311
389
|
return { content: output, functionCallAtEnd, functionCallContent, isFunctionCall };
|
|
312
390
|
},
|
|
313
|
-
|
|
314
|
-
resendMessage: async (messageId) => {
|
|
315
|
-
const session = sessionSelectors.currentSession(get());
|
|
316
|
-
|
|
317
|
-
if (!session) return;
|
|
318
|
-
|
|
319
|
-
// 1. 构造所有相关的历史记录
|
|
320
|
-
const chats = chatSelectors.currentChats(get());
|
|
321
|
-
|
|
322
|
-
const currentIndex = chats.findIndex((c) => c.id === messageId);
|
|
323
|
-
if (currentIndex < 0) return;
|
|
324
|
-
|
|
325
|
-
const currentMessage = chats[currentIndex];
|
|
326
|
-
|
|
327
|
-
let contextMessages: ChatMessage[] = [];
|
|
328
|
-
|
|
329
|
-
switch (currentMessage.role) {
|
|
330
|
-
case 'function':
|
|
331
|
-
case 'user': {
|
|
332
|
-
contextMessages = chats.slice(0, currentIndex + 1);
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
case 'assistant': {
|
|
336
|
-
// 消息是 AI 发出的因此需要找到它的 user 消息
|
|
337
|
-
const userId = currentMessage.parentId;
|
|
338
|
-
const userIndex = chats.findIndex((c) => c.id === userId);
|
|
339
|
-
// 如果消息没有 parentId,那么同 user/function 模式
|
|
340
|
-
contextMessages = chats.slice(0, userIndex < 0 ? currentIndex + 1 : userIndex + 1);
|
|
341
|
-
break;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (contextMessages.length <= 0) return;
|
|
346
|
-
|
|
347
|
-
const { coreProcessMessage } = get();
|
|
348
|
-
|
|
349
|
-
const latestMsg = contextMessages.filter((s) => s.role === 'user').at(-1);
|
|
350
|
-
|
|
351
|
-
if (!latestMsg) return;
|
|
352
|
-
|
|
353
|
-
await coreProcessMessage(contextMessages, latestMsg.id);
|
|
354
|
-
},
|
|
355
|
-
|
|
356
|
-
sendMessage: async (message, files) => {
|
|
357
|
-
const { dispatchMessage, dispatchAgentFile, coreProcessMessage, activeTopicId } = get();
|
|
358
|
-
const session = sessionSelectors.currentSession(get());
|
|
359
|
-
if (!session || !message) return;
|
|
360
|
-
|
|
361
|
-
const userId = nanoid();
|
|
362
|
-
|
|
363
|
-
dispatchMessage({
|
|
364
|
-
id: userId,
|
|
365
|
-
message: message,
|
|
366
|
-
role: 'user',
|
|
367
|
-
type: 'addMessage',
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
// if message has attached with files, then add files to message and the agent
|
|
371
|
-
if (files && files.length > 0) {
|
|
372
|
-
const fileIdList = files.map((f) => f.id);
|
|
373
|
-
dispatchMessage({ id: userId, key: 'files', type: 'updateMessage', value: fileIdList });
|
|
374
|
-
|
|
375
|
-
dispatchAgentFile({ files: fileIdList, type: 'addFiles' });
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// if there is activeTopicId,then add topicId to message
|
|
379
|
-
if (activeTopicId) {
|
|
380
|
-
dispatchMessage({ id: userId, key: 'topicId', type: 'updateMessage', value: activeTopicId });
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Get the current messages to generate AI response
|
|
384
|
-
const messages = chatSelectors.currentChats(get());
|
|
385
|
-
|
|
386
|
-
await coreProcessMessage(messages, userId);
|
|
387
|
-
|
|
388
|
-
// check activeTopic and then auto create topic
|
|
389
|
-
const chats = chatSelectors.currentChats(get());
|
|
390
|
-
|
|
391
|
-
const agentConfig = agentSelectors.currentAgentConfig(get());
|
|
392
|
-
// if autoCreateTopic is false, then stop
|
|
393
|
-
if (!agentConfig.enableAutoCreateTopic) return;
|
|
394
|
-
|
|
395
|
-
if (!activeTopicId && chats.length >= agentConfig.autoCreateTopicThreshold) {
|
|
396
|
-
const { saveToTopic, toggleTopic } = get();
|
|
397
|
-
const id = await saveToTopic();
|
|
398
|
-
if (id) toggleTopic(id);
|
|
399
|
-
}
|
|
400
|
-
},
|
|
401
|
-
|
|
402
|
-
stopGenerateMessage: () => {
|
|
403
|
-
const { abortController, toggleChatLoading } = get();
|
|
404
|
-
if (!abortController) return;
|
|
405
|
-
|
|
406
|
-
abortController.abort();
|
|
407
|
-
|
|
408
|
-
toggleChatLoading(false);
|
|
409
|
-
},
|
|
410
391
|
toggleChatLoading: (loading, id, action) => {
|
|
411
392
|
if (loading) {
|
|
412
393
|
const abortController = new AbortController();
|
|
@@ -416,7 +397,4 @@ export const chatMessage: StateCreator<
|
|
|
416
397
|
set({ abortController: undefined, chatLoadingId: undefined }, false, action);
|
|
417
398
|
}
|
|
418
399
|
},
|
|
419
|
-
updateInputMessage: (message) => {
|
|
420
|
-
set({ inputMessage: message }, false, t('updateInputMessage'));
|
|
421
|
-
},
|
|
422
400
|
});
|