@lobehub/lobehub 2.0.0-next.35 → 2.0.0-next.37
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/next.config.ts +5 -6
- package/package.json +2 -2
- package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +112 -77
- package/packages/agent-runtime/src/core/runtime.ts +63 -18
- package/packages/agent-runtime/src/types/generalAgent.ts +55 -0
- package/packages/agent-runtime/src/types/index.ts +1 -0
- package/packages/agent-runtime/src/types/instruction.ts +10 -3
- package/packages/const/src/user.ts +0 -1
- package/packages/context-engine/src/processors/GroupMessageFlatten.ts +8 -6
- package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +12 -12
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/assistant-group-branches.json +249 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/index.ts +4 -0
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/multi-assistant-group.json +260 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/active-index-1.json +4 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/assistant-group-branches.json +481 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/conversation.json +5 -1
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/index.ts +4 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/multi-assistant-group.json +407 -0
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/nested.json +18 -2
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/complex-scenario.json +25 -3
- package/packages/conversation-flow/src/__tests__/parse.test.ts +12 -0
- package/packages/conversation-flow/src/index.ts +1 -1
- package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +112 -34
- package/packages/conversation-flow/src/types/flatMessageList.ts +0 -12
- package/packages/conversation-flow/src/{types.ts → types/index.ts} +3 -14
- package/packages/database/src/models/__tests__/apiKey.test.ts +444 -0
- package/packages/database/src/models/message.ts +18 -19
- package/packages/types/src/aiChat.ts +2 -0
- package/packages/types/src/importer.ts +2 -2
- package/packages/types/src/message/ui/chat.ts +17 -1
- package/packages/types/src/message/ui/extra.ts +2 -2
- package/packages/types/src/message/ui/params.ts +2 -2
- package/packages/types/src/user/preference.ts +0 -4
- package/packages/utils/src/tokenizer/index.ts +3 -11
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/Desktop/MessageFromUrl.tsx +3 -3
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/index.tsx +1 -1
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/V1Mobile/useSend.ts +3 -3
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatInput/useSend.ts +6 -6
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/Content.tsx +5 -3
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/AgentWelcome/OpeningQuestions.tsx +2 -2
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/WelcomeChatItem/GroupWelcome/GroupUsageSuggest.tsx +2 -2
- package/src/app/[variants]/(main)/labs/page.tsx +0 -9
- package/src/features/ChatInput/ActionBar/STT/browser.tsx +3 -3
- package/src/features/ChatInput/ActionBar/STT/openai.tsx +3 -3
- package/src/features/Conversation/Error/AccessCodeForm.tsx +1 -1
- package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +1 -1
- package/src/features/Conversation/Error/ClerkLogin/index.tsx +1 -1
- package/src/features/Conversation/Error/OAuthForm.tsx +1 -1
- package/src/features/Conversation/Error/index.tsx +0 -5
- package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +13 -10
- package/src/features/Conversation/Messages/Assistant/Extra/index.test.tsx +3 -8
- package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -6
- package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +7 -9
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResult.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Render/PluginSettings.tsx +4 -1
- package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -3
- package/src/features/Conversation/Messages/Assistant/index.tsx +57 -60
- package/src/features/Conversation/Messages/Default.tsx +1 -0
- package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +38 -10
- package/src/features/Conversation/Messages/Group/Actions/index.tsx +1 -1
- package/src/features/Conversation/Messages/Group/ContentBlock.tsx +1 -3
- package/src/features/Conversation/Messages/Group/GroupChildren.tsx +12 -12
- package/src/features/Conversation/Messages/Group/MessageContent.tsx +7 -1
- package/src/features/Conversation/Messages/Group/Tool/Render/PluginSettings.tsx +1 -1
- package/src/features/Conversation/Messages/Group/index.tsx +2 -1
- package/src/features/Conversation/Messages/Supervisor/index.tsx +2 -2
- package/src/features/Conversation/Messages/User/{Actions.tsx → Actions/ActionsBar.tsx} +26 -25
- package/src/features/Conversation/Messages/User/Actions/MessageBranch.tsx +107 -0
- package/src/features/Conversation/Messages/User/Actions/index.tsx +42 -0
- package/src/features/Conversation/Messages/User/index.tsx +43 -44
- package/src/features/Conversation/Messages/index.tsx +3 -3
- package/src/features/Conversation/components/AutoScroll.tsx +3 -3
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/AnimatedNumber.tsx +55 -0
- package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +5 -2
- package/src/features/Conversation/components/VirtualizedList/index.tsx +29 -20
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +8 -10
- package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +3 -3
- package/src/hooks/useHotkeys/chatScope.ts +15 -7
- package/src/libs/trpc/client/lambda.ts +4 -3
- package/src/server/routers/lambda/__tests__/aiChat.test.ts +1 -1
- package/src/server/routers/lambda/__tests__/integration/message.integration.test.ts +0 -26
- package/src/server/routers/lambda/aiChat.ts +3 -2
- package/src/server/routers/lambda/message.ts +8 -16
- package/src/server/services/message/__tests__/index.test.ts +29 -39
- package/src/server/services/message/index.ts +41 -36
- package/src/services/electron/desktopNotification.ts +6 -6
- package/src/services/electron/file.ts +6 -6
- package/src/services/file/ClientS3/index.ts +8 -8
- package/src/services/message/__tests__/metadata-race-condition.test.ts +157 -0
- package/src/services/message/index.ts +21 -15
- package/src/services/upload.ts +11 -11
- package/src/services/utils/abortableRequest.test.ts +161 -0
- package/src/services/utils/abortableRequest.ts +67 -0
- package/src/store/chat/agents/GeneralChatAgent.ts +137 -0
- package/src/store/chat/agents/createAgentExecutors.ts +395 -0
- package/src/store/chat/helpers.test.ts +0 -99
- package/src/store/chat/helpers.ts +0 -11
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationControl.test.ts +332 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/conversationLifecycle.test.ts +257 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +11 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/rag.test.ts +6 -6
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingExecutor.test.ts +391 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/streamingStates.test.ts +179 -0
- package/src/store/chat/slices/aiChat/actions/conversationControl.ts +157 -0
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +329 -0
- package/src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts +14 -14
- package/src/store/chat/slices/aiChat/actions/index.ts +12 -6
- package/src/store/chat/slices/aiChat/actions/rag.ts +9 -6
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +604 -0
- package/src/store/chat/slices/aiChat/actions/streamingStates.ts +84 -0
- package/src/store/chat/slices/builtinTool/actions/__tests__/localSystem.test.ts +4 -4
- package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +11 -11
- package/src/store/chat/slices/builtinTool/actions/interpreter.ts +8 -8
- package/src/store/chat/slices/builtinTool/actions/localSystem.ts +2 -2
- package/src/store/chat/slices/builtinTool/actions/search.ts +8 -8
- package/src/store/chat/slices/message/action.test.ts +79 -68
- package/src/store/chat/slices/message/actions/index.ts +39 -0
- package/src/store/chat/slices/message/actions/internals.ts +77 -0
- package/src/store/chat/slices/message/actions/optimisticUpdate.ts +260 -0
- package/src/store/chat/slices/message/actions/publicApi.ts +224 -0
- package/src/store/chat/slices/message/actions/query.ts +120 -0
- package/src/store/chat/slices/message/actions/runtimeState.ts +108 -0
- package/src/store/chat/slices/message/initialState.ts +13 -0
- package/src/store/chat/slices/message/reducer.test.ts +48 -370
- package/src/store/chat/slices/message/reducer.ts +17 -81
- package/src/store/chat/slices/message/selectors/chat.test.ts +13 -50
- package/src/store/chat/slices/message/selectors/chat.ts +78 -242
- package/src/store/chat/slices/message/selectors/dbMessage.ts +140 -0
- package/src/store/chat/slices/message/selectors/displayMessage.ts +301 -0
- package/src/store/chat/slices/message/selectors/messageState.ts +5 -2
- package/src/store/chat/slices/plugin/action.test.ts +62 -64
- package/src/store/chat/slices/plugin/action.ts +34 -28
- package/src/store/chat/slices/thread/action.test.ts +28 -31
- package/src/store/chat/slices/thread/action.ts +13 -10
- package/src/store/chat/slices/thread/selectors/index.ts +8 -6
- package/src/store/chat/slices/topic/reducer.ts +11 -3
- package/src/store/chat/store.ts +1 -1
- package/src/store/user/slices/preference/selectors/labPrefer.ts +0 -3
- package/packages/database/src/models/__tests__/message.grouping.test.ts +0 -812
- package/packages/database/src/utils/__tests__/groupMessages.test.ts +0 -1132
- package/packages/database/src/utils/groupMessages.ts +0 -361
- package/packages/utils/src/tokenizer/client.ts +0 -35
- package/packages/utils/src/tokenizer/estimated.ts +0 -4
- package/packages/utils/src/tokenizer/server.ts +0 -11
- package/packages/utils/src/tokenizer/tokenizer.worker.ts +0 -12
- package/src/app/(backend)/webapi/tokenizer/index.test.ts +0 -32
- package/src/app/(backend)/webapi/tokenizer/route.ts +0 -8
- package/src/features/Conversation/Error/InvalidAccessCode.tsx +0 -79
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +0 -975
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +0 -1050
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +0 -720
- package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +0 -849
- package/src/store/chat/slices/message/action.ts +0 -629
|
@@ -1,849 +0,0 @@
|
|
|
1
|
-
/* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
|
|
2
|
-
// Disable the auto sort key eslint rule to make the code more logic and readable
|
|
3
|
-
import {
|
|
4
|
-
DEFAULT_AGENT_CHAT_CONFIG,
|
|
5
|
-
INBOX_SESSION_ID,
|
|
6
|
-
LOADING_FLAT,
|
|
7
|
-
isDesktop,
|
|
8
|
-
} from '@lobechat/const';
|
|
9
|
-
import { knowledgeBaseQAPrompts } from '@lobechat/prompts';
|
|
10
|
-
import {
|
|
11
|
-
ChatImageItem,
|
|
12
|
-
ChatTopic,
|
|
13
|
-
ChatVideoItem,
|
|
14
|
-
CreateNewMessageParams,
|
|
15
|
-
MessageSemanticSearchChunk,
|
|
16
|
-
SendMessageParams,
|
|
17
|
-
SendMessageServerResponse,
|
|
18
|
-
TraceNameMap,
|
|
19
|
-
UIChatMessage,
|
|
20
|
-
} from '@lobechat/types';
|
|
21
|
-
import { TRPCClientError } from '@trpc/client';
|
|
22
|
-
import debug from 'debug';
|
|
23
|
-
import { t } from 'i18next';
|
|
24
|
-
import { produce } from 'immer';
|
|
25
|
-
import pMap from 'p-map';
|
|
26
|
-
import { StateCreator } from 'zustand/vanilla';
|
|
27
|
-
|
|
28
|
-
import { aiChatService } from '@/services/aiChat';
|
|
29
|
-
import { chatService } from '@/services/chat';
|
|
30
|
-
import { messageService } from '@/services/message';
|
|
31
|
-
import { getAgentStoreState } from '@/store/agent';
|
|
32
|
-
import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/slices/chat';
|
|
33
|
-
import { aiModelSelectors, aiProviderSelectors, getAiInfraStoreState } from '@/store/aiInfra';
|
|
34
|
-
import { MainSendMessageOperation } from '@/store/chat/slices/aiChat/initialState';
|
|
35
|
-
import type { ChatStore } from '@/store/chat/store';
|
|
36
|
-
import { getFileStoreState } from '@/store/file/store';
|
|
37
|
-
import { getSessionStoreState } from '@/store/session';
|
|
38
|
-
import { WebBrowsingManifest } from '@/tools/web-browsing';
|
|
39
|
-
import { setNamespace } from '@/utils/storeDebug';
|
|
40
|
-
|
|
41
|
-
import { chatSelectors, threadSelectors, topicSelectors } from '../../../selectors';
|
|
42
|
-
import { messageMapKey } from '../../../utils/messageMapKey';
|
|
43
|
-
|
|
44
|
-
const n = setNamespace('ai');
|
|
45
|
-
const log = debug('lobe-store:ai-chat-v2');
|
|
46
|
-
|
|
47
|
-
export interface AIGenerateV2Action {
|
|
48
|
-
/**
|
|
49
|
-
* Sends a new message to the AI chat system
|
|
50
|
-
*/
|
|
51
|
-
sendMessage: (params: SendMessageParams) => Promise<void>;
|
|
52
|
-
/**
|
|
53
|
-
* Cancels sendMessage operation for a specific topic/session
|
|
54
|
-
*/
|
|
55
|
-
cancelSendMessageInServer: (topicId?: string) => void;
|
|
56
|
-
clearSendMessageError: () => void;
|
|
57
|
-
/**
|
|
58
|
-
*/
|
|
59
|
-
triggerToolsCalling: (
|
|
60
|
-
id: string,
|
|
61
|
-
params?: { threadId?: string; inPortalThread?: boolean; inSearchWorkflow?: boolean },
|
|
62
|
-
) => Promise<void>;
|
|
63
|
-
callToolFollowAssistantMessage: (params: {
|
|
64
|
-
parentId: string;
|
|
65
|
-
traceId?: string;
|
|
66
|
-
threadId?: string;
|
|
67
|
-
inPortalThread?: boolean;
|
|
68
|
-
inSearchWorkflow?: boolean;
|
|
69
|
-
}) => Promise<void>;
|
|
70
|
-
|
|
71
|
-
internal_refreshAiChat: (params: {
|
|
72
|
-
topics?: ChatTopic[];
|
|
73
|
-
messages: UIChatMessage[];
|
|
74
|
-
sessionId: string;
|
|
75
|
-
topicId?: string;
|
|
76
|
-
}) => void;
|
|
77
|
-
/**
|
|
78
|
-
* Executes the core processing logic for AI messages
|
|
79
|
-
* including preprocessing and postprocessing steps
|
|
80
|
-
*/
|
|
81
|
-
internal_execAgentRuntime: (params: {
|
|
82
|
-
messages: UIChatMessage[];
|
|
83
|
-
userMessageId?: string;
|
|
84
|
-
assistantMessageId: string;
|
|
85
|
-
isWelcomeQuestion?: boolean;
|
|
86
|
-
inSearchWorkflow?: boolean;
|
|
87
|
-
/**
|
|
88
|
-
* the RAG query content, should be embedding and used in the semantic search
|
|
89
|
-
*/
|
|
90
|
-
ragQuery?: string;
|
|
91
|
-
threadId?: string;
|
|
92
|
-
inPortalThread?: boolean;
|
|
93
|
-
traceId?: string;
|
|
94
|
-
}) => Promise<void>;
|
|
95
|
-
/**
|
|
96
|
-
* Toggle sendMessage operation state
|
|
97
|
-
*/
|
|
98
|
-
internal_toggleSendMessageOperation: (
|
|
99
|
-
key: string | { sessionId: string; topicId?: string | null },
|
|
100
|
-
loading: boolean,
|
|
101
|
-
cancelReason?: string,
|
|
102
|
-
) => AbortController | undefined;
|
|
103
|
-
internal_updateSendMessageOperation: (
|
|
104
|
-
key: string | { sessionId: string; topicId?: string | null },
|
|
105
|
-
value: Partial<MainSendMessageOperation> | null,
|
|
106
|
-
actionName?: any,
|
|
107
|
-
) => void;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export const generateAIChatV2: StateCreator<
|
|
111
|
-
ChatStore,
|
|
112
|
-
[['zustand/devtools', never]],
|
|
113
|
-
[],
|
|
114
|
-
AIGenerateV2Action
|
|
115
|
-
> = (set, get) => ({
|
|
116
|
-
sendMessage: async ({ message, files, onlyAddUserMessage, isWelcomeQuestion }) => {
|
|
117
|
-
const { activeTopicId, activeId, activeThreadId, internal_execAgentRuntime, mainInputEditor } =
|
|
118
|
-
get();
|
|
119
|
-
if (!activeId) return;
|
|
120
|
-
|
|
121
|
-
const fileIdList = files?.map((f) => f.id);
|
|
122
|
-
|
|
123
|
-
const hasFile = !!fileIdList && fileIdList.length > 0;
|
|
124
|
-
|
|
125
|
-
// if message is empty or no files, then stop
|
|
126
|
-
if (!message && !hasFile) return;
|
|
127
|
-
|
|
128
|
-
if (onlyAddUserMessage) {
|
|
129
|
-
await get().addUserMessage({ message, fileList: fileIdList });
|
|
130
|
-
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const messages = chatSelectors.activeBaseChats(get());
|
|
135
|
-
const chatConfig = agentChatConfigSelectors.currentChatConfig(getAgentStoreState());
|
|
136
|
-
const autoCreateThreshold =
|
|
137
|
-
chatConfig.autoCreateTopicThreshold ?? DEFAULT_AGENT_CHAT_CONFIG.autoCreateTopicThreshold;
|
|
138
|
-
const shouldCreateNewTopic =
|
|
139
|
-
!activeTopicId &&
|
|
140
|
-
!!chatConfig.enableAutoCreateTopic &&
|
|
141
|
-
messages.length + 2 >= autoCreateThreshold;
|
|
142
|
-
|
|
143
|
-
// 构造服务端模式临时消息的本地媒体预览(优先使用 S3 URL)
|
|
144
|
-
const filesInStore = getFileStoreState().chatUploadFileList;
|
|
145
|
-
const tempImages: ChatImageItem[] = filesInStore
|
|
146
|
-
.filter((f) => f.file?.type?.startsWith('image'))
|
|
147
|
-
.map((f) => ({
|
|
148
|
-
id: f.id,
|
|
149
|
-
url: f.fileUrl || f.base64Url || f.previewUrl || '',
|
|
150
|
-
alt: f.file?.name || f.id,
|
|
151
|
-
}));
|
|
152
|
-
const tempVideos: ChatVideoItem[] = filesInStore
|
|
153
|
-
.filter((f) => f.file?.type?.startsWith('video'))
|
|
154
|
-
.map((f) => ({
|
|
155
|
-
id: f.id,
|
|
156
|
-
url: f.fileUrl || f.base64Url || f.previewUrl || '',
|
|
157
|
-
alt: f.file?.name || f.id,
|
|
158
|
-
}));
|
|
159
|
-
|
|
160
|
-
// use optimistic update to avoid the slow waiting
|
|
161
|
-
const tempId = get().internal_createTmpMessage({
|
|
162
|
-
content: message,
|
|
163
|
-
// if message has attached with files, then add files to message and the agent
|
|
164
|
-
files: fileIdList,
|
|
165
|
-
role: 'user',
|
|
166
|
-
sessionId: activeId,
|
|
167
|
-
// if there is activeTopicId,then add topicId to message
|
|
168
|
-
topicId: activeTopicId,
|
|
169
|
-
threadId: activeThreadId,
|
|
170
|
-
imageList: tempImages.length > 0 ? tempImages : undefined,
|
|
171
|
-
videoList: tempVideos.length > 0 ? tempVideos : undefined,
|
|
172
|
-
});
|
|
173
|
-
get().internal_toggleMessageLoading(true, tempId);
|
|
174
|
-
|
|
175
|
-
const operationKey = messageMapKey(activeId, activeTopicId);
|
|
176
|
-
|
|
177
|
-
// Start tracking sendMessage operation with AbortController
|
|
178
|
-
const abortController = get().internal_toggleSendMessageOperation(operationKey, true)!;
|
|
179
|
-
|
|
180
|
-
const jsonState = mainInputEditor?.getJSONState();
|
|
181
|
-
get().internal_updateSendMessageOperation(
|
|
182
|
-
operationKey,
|
|
183
|
-
{ inputSendErrorMsg: undefined, inputEditorTempState: jsonState },
|
|
184
|
-
'creatingMessage/start',
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
let data: SendMessageServerResponse | undefined;
|
|
188
|
-
try {
|
|
189
|
-
const { model, provider } = agentSelectors.currentAgentConfig(getAgentStoreState());
|
|
190
|
-
data = await aiChatService.sendMessageInServer(
|
|
191
|
-
{
|
|
192
|
-
newUserMessage: {
|
|
193
|
-
content: message,
|
|
194
|
-
files: fileIdList,
|
|
195
|
-
},
|
|
196
|
-
// if there is activeTopicId,then add topicId to message
|
|
197
|
-
topicId: activeTopicId,
|
|
198
|
-
threadId: activeThreadId,
|
|
199
|
-
newTopic: shouldCreateNewTopic
|
|
200
|
-
? {
|
|
201
|
-
topicMessageIds: messages.map((m) => m.id),
|
|
202
|
-
title: t('defaultTitle', { ns: 'topic' }),
|
|
203
|
-
}
|
|
204
|
-
: undefined,
|
|
205
|
-
sessionId: activeId === INBOX_SESSION_ID ? undefined : activeId,
|
|
206
|
-
newAssistantMessage: { model, provider: provider! },
|
|
207
|
-
},
|
|
208
|
-
abortController,
|
|
209
|
-
);
|
|
210
|
-
// refresh the total data
|
|
211
|
-
get().internal_refreshAiChat({
|
|
212
|
-
messages: data.messages,
|
|
213
|
-
topics: data.topics,
|
|
214
|
-
sessionId: activeId,
|
|
215
|
-
topicId: data.topicId,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
if (data.isCreateNewTopic && data.topicId) {
|
|
219
|
-
await get().switchTopic(data.topicId, true);
|
|
220
|
-
}
|
|
221
|
-
} catch (e) {
|
|
222
|
-
if (e instanceof TRPCClientError) {
|
|
223
|
-
const isAbort = e.message.includes('aborted') || e.name === 'AbortError';
|
|
224
|
-
// Check if error is due to cancellation
|
|
225
|
-
if (!isAbort) {
|
|
226
|
-
get().internal_updateSendMessageOperation(operationKey, { inputSendErrorMsg: e.message });
|
|
227
|
-
get().mainInputEditor?.setJSONState(jsonState);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
} finally {
|
|
231
|
-
// Stop tracking sendMessage operation
|
|
232
|
-
get().internal_toggleSendMessageOperation(operationKey, false);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// remove temporally message
|
|
236
|
-
if (data?.isCreateNewTopic) {
|
|
237
|
-
get().internal_dispatchMessage(
|
|
238
|
-
{ type: 'deleteMessage', id: tempId },
|
|
239
|
-
{ topicId: activeTopicId, sessionId: activeId },
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
get().internal_toggleMessageLoading(false, tempId);
|
|
244
|
-
get().internal_updateSendMessageOperation(
|
|
245
|
-
operationKey,
|
|
246
|
-
{ inputEditorTempState: null },
|
|
247
|
-
'creatingMessage/finished',
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
if (!data) return;
|
|
251
|
-
|
|
252
|
-
// update assistant update to make it rerank
|
|
253
|
-
getSessionStoreState().triggerSessionUpdate(get().activeId);
|
|
254
|
-
|
|
255
|
-
// Get the current messages to generate AI response
|
|
256
|
-
// remove the latest assistant message id
|
|
257
|
-
const baseMessages = chatSelectors
|
|
258
|
-
.activeBaseChats(get())
|
|
259
|
-
.filter((item) => item.id !== data.assistantMessageId);
|
|
260
|
-
|
|
261
|
-
if (data.topicId) get().internal_updateTopicLoading(data.topicId, true);
|
|
262
|
-
|
|
263
|
-
const summaryTitle = async () => {
|
|
264
|
-
// check activeTopic and then auto update topic title
|
|
265
|
-
if (data.isCreateNewTopic) {
|
|
266
|
-
await get().summaryTopicTitle(data.topicId, data.messages);
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (!data.topicId) return;
|
|
271
|
-
|
|
272
|
-
const topic = topicSelectors.getTopicById(data.topicId)(get());
|
|
273
|
-
|
|
274
|
-
if (topic && !topic.title) {
|
|
275
|
-
const chats = chatSelectors.getBaseChatsByKey(messageMapKey(activeId, topic.id))(get());
|
|
276
|
-
await get().summaryTopicTitle(topic.id, chats);
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
summaryTitle().catch(console.error);
|
|
281
|
-
|
|
282
|
-
try {
|
|
283
|
-
await internal_execAgentRuntime({
|
|
284
|
-
messages: baseMessages,
|
|
285
|
-
userMessageId: data.userMessageId,
|
|
286
|
-
assistantMessageId: data.assistantMessageId,
|
|
287
|
-
isWelcomeQuestion,
|
|
288
|
-
ragQuery: get().internal_shouldUseRAG() ? message : undefined,
|
|
289
|
-
threadId: activeThreadId,
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
//
|
|
293
|
-
// // if there is relative files, then add files to agent
|
|
294
|
-
// // only available in server mode
|
|
295
|
-
const userFiles = chatSelectors.currentUserFiles(get()).map((f) => f.id);
|
|
296
|
-
|
|
297
|
-
await getAgentStoreState().addFilesToAgent(userFiles, false);
|
|
298
|
-
} catch (e) {
|
|
299
|
-
console.error(e);
|
|
300
|
-
} finally {
|
|
301
|
-
if (data.topicId) get().internal_updateTopicLoading(data.topicId, false);
|
|
302
|
-
}
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
cancelSendMessageInServer: (topicId?: string) => {
|
|
306
|
-
const { activeId, activeTopicId } = get();
|
|
307
|
-
|
|
308
|
-
// Determine which operation to cancel
|
|
309
|
-
const targetTopicId = topicId ?? activeTopicId;
|
|
310
|
-
const operationKey = messageMapKey(activeId, targetTopicId);
|
|
311
|
-
|
|
312
|
-
// Cancel the specific operation
|
|
313
|
-
get().internal_toggleSendMessageOperation(
|
|
314
|
-
operationKey,
|
|
315
|
-
false,
|
|
316
|
-
'User cancelled sendMessage operation',
|
|
317
|
-
);
|
|
318
|
-
|
|
319
|
-
// Only clear creating message state if it's the active session
|
|
320
|
-
if (operationKey === messageMapKey(activeId, activeTopicId)) {
|
|
321
|
-
const editorTempState = get().mainSendMessageOperations[operationKey]?.inputEditorTempState;
|
|
322
|
-
|
|
323
|
-
if (editorTempState) get().mainInputEditor?.setJSONState(editorTempState);
|
|
324
|
-
}
|
|
325
|
-
},
|
|
326
|
-
clearSendMessageError: () => {
|
|
327
|
-
get().internal_updateSendMessageOperation(
|
|
328
|
-
{ sessionId: get().activeId, topicId: get().activeTopicId },
|
|
329
|
-
null,
|
|
330
|
-
'clearSendMessageError',
|
|
331
|
-
);
|
|
332
|
-
},
|
|
333
|
-
internal_refreshAiChat: ({ topics, messages, sessionId, topicId }) => {
|
|
334
|
-
set(
|
|
335
|
-
{
|
|
336
|
-
topicMaps: topics ? { ...get().topicMaps, [sessionId]: topics } : get().topicMaps,
|
|
337
|
-
messagesMap: { ...get().messagesMap, [messageMapKey(sessionId, topicId)]: messages },
|
|
338
|
-
},
|
|
339
|
-
false,
|
|
340
|
-
'refreshAiChat',
|
|
341
|
-
);
|
|
342
|
-
},
|
|
343
|
-
|
|
344
|
-
internal_execAgentRuntime: async (params) => {
|
|
345
|
-
const {
|
|
346
|
-
assistantMessageId: assistantId,
|
|
347
|
-
userMessageId,
|
|
348
|
-
ragQuery,
|
|
349
|
-
messages: originalMessages,
|
|
350
|
-
} = params;
|
|
351
|
-
|
|
352
|
-
log(
|
|
353
|
-
'[internal_execAgentRuntime] start, assistantId: %s, messages count: %d',
|
|
354
|
-
assistantId,
|
|
355
|
-
originalMessages.length,
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
const {
|
|
359
|
-
internal_fetchAIChatMessage,
|
|
360
|
-
triggerToolsCalling,
|
|
361
|
-
refreshMessages,
|
|
362
|
-
internal_updateMessageRAG,
|
|
363
|
-
} = get();
|
|
364
|
-
|
|
365
|
-
// create a new array to avoid the original messages array change
|
|
366
|
-
const messages = [...originalMessages];
|
|
367
|
-
|
|
368
|
-
const agentStoreState = getAgentStoreState();
|
|
369
|
-
const { model, provider, chatConfig } = agentSelectors.currentAgentConfig(agentStoreState);
|
|
370
|
-
|
|
371
|
-
log('[internal_execAgentRuntime] Agent config: model=%s, provider=%s', model, provider);
|
|
372
|
-
|
|
373
|
-
let fileChunks: MessageSemanticSearchChunk[] | undefined;
|
|
374
|
-
let ragQueryId;
|
|
375
|
-
|
|
376
|
-
// go into RAG flow if there is ragQuery flag
|
|
377
|
-
if (ragQuery && userMessageId) {
|
|
378
|
-
log('[internal_execAgentRuntime] Entering RAG flow with query: %s', ragQuery);
|
|
379
|
-
// 1. get the relative chunks from semantic search
|
|
380
|
-
const { chunks, queryId, rewriteQuery } = await get().internal_retrieveChunks(
|
|
381
|
-
userMessageId,
|
|
382
|
-
ragQuery,
|
|
383
|
-
// should skip the last content
|
|
384
|
-
messages.map((m) => m.content).slice(0, messages.length - 1),
|
|
385
|
-
);
|
|
386
|
-
|
|
387
|
-
ragQueryId = queryId;
|
|
388
|
-
|
|
389
|
-
const lastMsg = messages.pop() as UIChatMessage;
|
|
390
|
-
|
|
391
|
-
// 2. build the retrieve context messages
|
|
392
|
-
const knowledgeBaseQAContext = knowledgeBaseQAPrompts({
|
|
393
|
-
chunks,
|
|
394
|
-
userQuery: lastMsg.content,
|
|
395
|
-
rewriteQuery,
|
|
396
|
-
knowledge: agentSelectors.currentEnabledKnowledge(agentStoreState),
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
// 3. add the retrieve context messages to the messages history
|
|
400
|
-
messages.push({
|
|
401
|
-
...lastMsg,
|
|
402
|
-
content: (lastMsg.content + '\n\n' + knowledgeBaseQAContext).trim(),
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
fileChunks = chunks.map((c) => ({ id: c.id, similarity: c.similarity }));
|
|
406
|
-
|
|
407
|
-
if (fileChunks.length > 0) {
|
|
408
|
-
await internal_updateMessageRAG(assistantId, { ragQueryId, fileChunks });
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// 3. place a search with the search working model if this model is not support tool use
|
|
413
|
-
const aiInfraStoreState = getAiInfraStoreState();
|
|
414
|
-
const isModelSupportToolUse = aiModelSelectors.isModelSupportToolUse(
|
|
415
|
-
model,
|
|
416
|
-
provider!,
|
|
417
|
-
)(aiInfraStoreState);
|
|
418
|
-
const isProviderHasBuiltinSearch = aiProviderSelectors.isProviderHasBuiltinSearch(provider!)(
|
|
419
|
-
aiInfraStoreState,
|
|
420
|
-
);
|
|
421
|
-
const isModelHasBuiltinSearch = aiModelSelectors.isModelHasBuiltinSearch(
|
|
422
|
-
model,
|
|
423
|
-
provider!,
|
|
424
|
-
)(aiInfraStoreState);
|
|
425
|
-
const isModelBuiltinSearchInternal = aiModelSelectors.isModelBuiltinSearchInternal(
|
|
426
|
-
model,
|
|
427
|
-
provider!,
|
|
428
|
-
)(aiInfraStoreState);
|
|
429
|
-
const useModelBuiltinSearch = agentChatConfigSelectors.useModelBuiltinSearch(agentStoreState);
|
|
430
|
-
const useModelSearch =
|
|
431
|
-
((isProviderHasBuiltinSearch || isModelHasBuiltinSearch) && useModelBuiltinSearch) ||
|
|
432
|
-
isModelBuiltinSearchInternal;
|
|
433
|
-
const isAgentEnableSearch = agentChatConfigSelectors.isAgentEnableSearch(agentStoreState);
|
|
434
|
-
|
|
435
|
-
if (isAgentEnableSearch && !useModelSearch && !isModelSupportToolUse) {
|
|
436
|
-
const { model, provider } = agentChatConfigSelectors.searchFCModel(agentStoreState);
|
|
437
|
-
|
|
438
|
-
let isToolsCalling = false;
|
|
439
|
-
let isError = false;
|
|
440
|
-
|
|
441
|
-
const abortController = get().internal_toggleChatLoading(
|
|
442
|
-
true,
|
|
443
|
-
assistantId,
|
|
444
|
-
n('generateMessage(start)', { messageId: assistantId, messages }),
|
|
445
|
-
);
|
|
446
|
-
|
|
447
|
-
get().internal_toggleSearchWorkflow(true, assistantId);
|
|
448
|
-
await chatService.fetchPresetTaskResult({
|
|
449
|
-
params: { messages, model, provider, plugins: [WebBrowsingManifest.identifier] },
|
|
450
|
-
onFinish: async (_, { toolCalls, usage }) => {
|
|
451
|
-
if (toolCalls && toolCalls.length > 0) {
|
|
452
|
-
get().internal_toggleToolCallingStreaming(assistantId, undefined);
|
|
453
|
-
// update tools calling
|
|
454
|
-
await get().internal_updateMessageContent(assistantId, '', {
|
|
455
|
-
toolCalls,
|
|
456
|
-
metadata: usage,
|
|
457
|
-
model,
|
|
458
|
-
provider,
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
},
|
|
462
|
-
trace: {
|
|
463
|
-
traceId: params.traceId,
|
|
464
|
-
sessionId: get().activeId,
|
|
465
|
-
topicId: get().activeTopicId,
|
|
466
|
-
traceName: TraceNameMap.SearchIntentRecognition,
|
|
467
|
-
},
|
|
468
|
-
abortController,
|
|
469
|
-
onMessageHandle: async (chunk) => {
|
|
470
|
-
if (chunk.type === 'tool_calls') {
|
|
471
|
-
get().internal_toggleSearchWorkflow(false, assistantId);
|
|
472
|
-
get().internal_toggleToolCallingStreaming(assistantId, chunk.isAnimationActives);
|
|
473
|
-
get().internal_dispatchMessage({
|
|
474
|
-
id: assistantId,
|
|
475
|
-
type: 'updateMessage',
|
|
476
|
-
value: { tools: get().internal_transformToolCalls(chunk.tool_calls) },
|
|
477
|
-
});
|
|
478
|
-
isToolsCalling = true;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
if (chunk.type === 'text') {
|
|
482
|
-
abortController!.abort('not fc');
|
|
483
|
-
}
|
|
484
|
-
},
|
|
485
|
-
onErrorHandle: async (error) => {
|
|
486
|
-
isError = true;
|
|
487
|
-
await messageService.updateMessageError(assistantId, error);
|
|
488
|
-
await refreshMessages();
|
|
489
|
-
},
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
get().internal_toggleChatLoading(
|
|
493
|
-
false,
|
|
494
|
-
assistantId,
|
|
495
|
-
n('generateMessage(start)', { messageId: assistantId, messages }),
|
|
496
|
-
);
|
|
497
|
-
get().internal_toggleSearchWorkflow(false, assistantId);
|
|
498
|
-
|
|
499
|
-
// if there is error, then stop
|
|
500
|
-
if (isError) return;
|
|
501
|
-
|
|
502
|
-
// if it's the function call message, trigger the function method
|
|
503
|
-
if (isToolsCalling) {
|
|
504
|
-
get().internal_toggleMessageInToolsCalling(true, assistantId);
|
|
505
|
-
await refreshMessages();
|
|
506
|
-
await triggerToolsCalling(assistantId, {
|
|
507
|
-
threadId: params?.threadId,
|
|
508
|
-
inPortalThread: params?.inPortalThread,
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
// then story the workflow
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
// 4. fetch the AI response
|
|
517
|
-
log('[internal_execAgentRuntime] Fetching AI response for assistantId: %s', assistantId);
|
|
518
|
-
const { isFunctionCall, content } = await internal_fetchAIChatMessage({
|
|
519
|
-
messages,
|
|
520
|
-
messageId: assistantId,
|
|
521
|
-
params,
|
|
522
|
-
model,
|
|
523
|
-
provider: provider!,
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
// 5. if it's the function call message, trigger the function method
|
|
527
|
-
if (isFunctionCall) {
|
|
528
|
-
log('[internal_execAgentRuntime] AI response is function call, triggering tools calling');
|
|
529
|
-
get().internal_toggleMessageInToolsCalling(true, assistantId);
|
|
530
|
-
await refreshMessages();
|
|
531
|
-
await triggerToolsCalling(assistantId, {
|
|
532
|
-
threadId: params?.threadId,
|
|
533
|
-
inPortalThread: params?.inPortalThread,
|
|
534
|
-
});
|
|
535
|
-
} else {
|
|
536
|
-
log(
|
|
537
|
-
'[internal_execAgentRuntime] AI response completed, content length: %d',
|
|
538
|
-
content?.length || 0,
|
|
539
|
-
);
|
|
540
|
-
// 显示桌面通知(仅在桌面端且窗口隐藏时)
|
|
541
|
-
if (isDesktop) {
|
|
542
|
-
try {
|
|
543
|
-
// 动态导入桌面通知服务,避免在非桌面端环境中导入
|
|
544
|
-
const { desktopNotificationService } = await import(
|
|
545
|
-
'@/services/electron/desktopNotification'
|
|
546
|
-
);
|
|
547
|
-
|
|
548
|
-
await desktopNotificationService.showNotification({
|
|
549
|
-
body: content,
|
|
550
|
-
title: t('notification.finishChatGeneration', { ns: 'electron' }),
|
|
551
|
-
});
|
|
552
|
-
} catch (error) {
|
|
553
|
-
// 静默处理错误,不影响正常流程
|
|
554
|
-
console.error('Desktop notification error:', error);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// 6. summary history if context messages is larger than historyCount
|
|
560
|
-
const historyCount = agentChatConfigSelectors.historyCount(agentStoreState);
|
|
561
|
-
|
|
562
|
-
if (
|
|
563
|
-
agentChatConfigSelectors.enableHistoryCount(agentStoreState) &&
|
|
564
|
-
chatConfig.enableCompressHistory &&
|
|
565
|
-
originalMessages.length > historyCount
|
|
566
|
-
) {
|
|
567
|
-
// after generation: [u1,a1,u2,a2,u3,a3]
|
|
568
|
-
// but the `originalMessages` is still: [u1,a1,u2,a2,u3]
|
|
569
|
-
// So if historyCount=2, we need to summary [u1,a1,u2,a2]
|
|
570
|
-
// because user find UI is [u1,a1,u2,a2 | u3,a3]
|
|
571
|
-
const historyMessages = originalMessages.slice(0, -historyCount + 1);
|
|
572
|
-
|
|
573
|
-
await get().internal_summaryHistory(historyMessages);
|
|
574
|
-
}
|
|
575
|
-
},
|
|
576
|
-
triggerToolsCalling: async (assistantId, { threadId, inPortalThread, inSearchWorkflow } = {}) => {
|
|
577
|
-
log('[triggerToolsCalling] start, assistantId (block ID): %s', assistantId);
|
|
578
|
-
|
|
579
|
-
const foundMessage = chatSelectors.getMessageById(assistantId)(get());
|
|
580
|
-
if (!foundMessage) {
|
|
581
|
-
log('[triggerToolsCalling] Message not found, returning');
|
|
582
|
-
return;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Determine if this is a group message or a block
|
|
586
|
-
let groupMessage: UIChatMessage;
|
|
587
|
-
let latestBlock: UIChatMessage;
|
|
588
|
-
|
|
589
|
-
if (foundMessage.role === 'group') {
|
|
590
|
-
// Case 1: assistantId matches a group message ID directly
|
|
591
|
-
// Find the block within children that matches assistantId
|
|
592
|
-
groupMessage = foundMessage;
|
|
593
|
-
const block = foundMessage.children?.find((item) => item.id === assistantId);
|
|
594
|
-
|
|
595
|
-
if (!block) {
|
|
596
|
-
log(
|
|
597
|
-
'[triggerToolsCalling] Block with id %s not found in group message children, returning',
|
|
598
|
-
assistantId,
|
|
599
|
-
);
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
latestBlock = block as UIChatMessage;
|
|
603
|
-
} else if (foundMessage.parentId) {
|
|
604
|
-
// Case 2: assistantId is a block ID, need to get parent group message
|
|
605
|
-
const parentMsg = chatSelectors.getMessageById(foundMessage.parentId)(get());
|
|
606
|
-
if (!parentMsg || parentMsg.role !== 'group') {
|
|
607
|
-
log('[triggerToolsCalling] Parent group message not found, returning');
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
|
-
groupMessage = parentMsg;
|
|
611
|
-
latestBlock = foundMessage;
|
|
612
|
-
} else {
|
|
613
|
-
log(
|
|
614
|
-
'[triggerToolsCalling] Message is neither a group message nor a block with parentId, returning',
|
|
615
|
-
);
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
log('[triggerToolsCalling] Found group message: %O', {
|
|
620
|
-
id: groupMessage.id,
|
|
621
|
-
groupId: groupMessage.groupId,
|
|
622
|
-
childrenCount: groupMessage.children?.length,
|
|
623
|
-
latestBlockId: latestBlock.id,
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
if (!latestBlock.tools) {
|
|
627
|
-
log('[triggerToolsCalling] Latest block has no tools, returning');
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
log(
|
|
632
|
-
'[triggerToolsCalling] Latest block found with %d tools: %O',
|
|
633
|
-
latestBlock.tools.length,
|
|
634
|
-
latestBlock.tools.map((t) => ({ id: t.id, type: t.type, identifier: t.identifier })),
|
|
635
|
-
);
|
|
636
|
-
|
|
637
|
-
let shouldCreateMessage = false;
|
|
638
|
-
let latestToolId = '';
|
|
639
|
-
|
|
640
|
-
await pMap(
|
|
641
|
-
latestBlock.tools,
|
|
642
|
-
async (payload) => {
|
|
643
|
-
log(
|
|
644
|
-
'[triggerToolsCalling] Processing tool: %s (type: %s)',
|
|
645
|
-
payload.identifier,
|
|
646
|
-
payload.type,
|
|
647
|
-
);
|
|
648
|
-
|
|
649
|
-
// 2. 使用 createMessage 创建 tool 消息
|
|
650
|
-
const toolMessage: CreateNewMessageParams = {
|
|
651
|
-
content: '',
|
|
652
|
-
parentId: assistantId,
|
|
653
|
-
plugin: payload,
|
|
654
|
-
role: 'tool',
|
|
655
|
-
sessionId: get().activeId,
|
|
656
|
-
tool_call_id: payload.id,
|
|
657
|
-
threadId,
|
|
658
|
-
topicId: get().activeTopicId, // if there is activeTopicId,then add it to topicId
|
|
659
|
-
groupId: groupMessage.groupId, // Propagate groupId from parent message for group chat
|
|
660
|
-
};
|
|
661
|
-
|
|
662
|
-
const result = await get().internal_createMessage(toolMessage);
|
|
663
|
-
|
|
664
|
-
if (!result) {
|
|
665
|
-
log('[triggerToolsCalling] Failed to create tool message for %s', payload.identifier);
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
log('[triggerToolsCalling] Tool message created: %s', result.id);
|
|
670
|
-
|
|
671
|
-
// 3. 执行 tool(这时 tool 消息已经创建,且 UI 已更新)
|
|
672
|
-
const data = await get().internal_invokeDifferentTypePlugin(result.id, payload);
|
|
673
|
-
|
|
674
|
-
if (data && !['markdown', 'standalone'].includes(payload.type)) {
|
|
675
|
-
shouldCreateMessage = true;
|
|
676
|
-
latestToolId = result.id;
|
|
677
|
-
log(
|
|
678
|
-
'[triggerToolsCalling] Tool %s requires follow-up assistant message',
|
|
679
|
-
payload.identifier,
|
|
680
|
-
);
|
|
681
|
-
} else {
|
|
682
|
-
log('[triggerToolsCalling] Tool %s completed without follow-up', payload.identifier);
|
|
683
|
-
}
|
|
684
|
-
},
|
|
685
|
-
{ concurrency: 5 },
|
|
686
|
-
);
|
|
687
|
-
|
|
688
|
-
await get().internal_toggleMessageInToolsCalling(false, assistantId);
|
|
689
|
-
|
|
690
|
-
if (!shouldCreateMessage) {
|
|
691
|
-
log('[triggerToolsCalling] No follow-up message needed, completed');
|
|
692
|
-
return;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
const traceId = chatSelectors.getTraceIdByMessageId(latestToolId)(get());
|
|
696
|
-
log(
|
|
697
|
-
'[triggerToolsCalling] Calling follow-up assistant message with latestToolId: %s',
|
|
698
|
-
latestToolId,
|
|
699
|
-
);
|
|
700
|
-
|
|
701
|
-
await get().callToolFollowAssistantMessage({
|
|
702
|
-
traceId,
|
|
703
|
-
threadId,
|
|
704
|
-
inPortalThread,
|
|
705
|
-
inSearchWorkflow,
|
|
706
|
-
parentId: latestToolId,
|
|
707
|
-
});
|
|
708
|
-
log('[triggerToolsCalling] completed');
|
|
709
|
-
},
|
|
710
|
-
|
|
711
|
-
callToolFollowAssistantMessage: async ({
|
|
712
|
-
parentId,
|
|
713
|
-
traceId,
|
|
714
|
-
threadId,
|
|
715
|
-
inPortalThread,
|
|
716
|
-
inSearchWorkflow,
|
|
717
|
-
}) => {
|
|
718
|
-
log('[callToolFollowAssistantMessage] start, parentId: %s', parentId);
|
|
719
|
-
|
|
720
|
-
const chats = inPortalThread
|
|
721
|
-
? threadSelectors.portalAIChatsWithHistoryConfig(get())
|
|
722
|
-
: chatSelectors.mainAIChatsWithHistoryConfig(get());
|
|
723
|
-
|
|
724
|
-
let assistantMessageId: string;
|
|
725
|
-
|
|
726
|
-
// 获取 agent 配置
|
|
727
|
-
const agentStoreState = getAgentStoreState();
|
|
728
|
-
const { model, provider } = agentSelectors.currentAgentConfig(agentStoreState);
|
|
729
|
-
|
|
730
|
-
// 查找包含 parentId 的 group message
|
|
731
|
-
// parentId 是 tool result message 的 id,它存储在 assistant block 的 tools[].result_msg_id 中
|
|
732
|
-
let groupMessageId: string | undefined;
|
|
733
|
-
|
|
734
|
-
// 遍历所有 group messages,找到包含该 tool result 的那个
|
|
735
|
-
for (const msg of chats) {
|
|
736
|
-
if (msg.role === 'group' && msg.children) {
|
|
737
|
-
for (const child of msg.children) {
|
|
738
|
-
// 检查 child 的 tools 中是否有 result_msg_id === parentId
|
|
739
|
-
if (child.tools?.some((tool) => tool.result_msg_id === parentId)) {
|
|
740
|
-
groupMessageId = msg.id;
|
|
741
|
-
log('[callToolFollowAssistantMessage] Found group message: %s', groupMessageId);
|
|
742
|
-
break;
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
if (groupMessageId) break;
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
// 创建新的 assistant message,作为 group message 的新 block
|
|
750
|
-
const assistantMessage: CreateNewMessageParams = {
|
|
751
|
-
role: 'assistant',
|
|
752
|
-
content: LOADING_FLAT,
|
|
753
|
-
parentId,
|
|
754
|
-
sessionId: get().activeId,
|
|
755
|
-
topicId: get().activeTopicId,
|
|
756
|
-
threadId,
|
|
757
|
-
traceId,
|
|
758
|
-
model,
|
|
759
|
-
provider,
|
|
760
|
-
};
|
|
761
|
-
|
|
762
|
-
log('[callToolFollowAssistantMessage] Creating new assistant message block with params: %O', {
|
|
763
|
-
parentId,
|
|
764
|
-
groupMessageId,
|
|
765
|
-
model,
|
|
766
|
-
provider,
|
|
767
|
-
sessionId: get().activeId,
|
|
768
|
-
topicId: get().activeTopicId,
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
const result = await get().internal_createMessage(assistantMessage, { groupMessageId });
|
|
772
|
-
|
|
773
|
-
if (!result) {
|
|
774
|
-
log('[callToolFollowAssistantMessage] Failed to create assistant message');
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
assistantMessageId = result.id;
|
|
779
|
-
log(
|
|
780
|
-
'[callToolFollowAssistantMessage] Assistant message created successfully, id: %s',
|
|
781
|
-
assistantMessageId,
|
|
782
|
-
);
|
|
783
|
-
|
|
784
|
-
log('[callToolFollowAssistantMessage] Starting agent runtime with %d messages', chats.length);
|
|
785
|
-
await get().internal_execAgentRuntime({
|
|
786
|
-
messages: chats,
|
|
787
|
-
assistantMessageId,
|
|
788
|
-
traceId,
|
|
789
|
-
threadId,
|
|
790
|
-
inPortalThread,
|
|
791
|
-
inSearchWorkflow,
|
|
792
|
-
});
|
|
793
|
-
log('[callToolFollowAssistantMessage] completed');
|
|
794
|
-
},
|
|
795
|
-
|
|
796
|
-
internal_updateSendMessageOperation: (key, value, actionName) => {
|
|
797
|
-
const operationKey = typeof key === 'string' ? key : messageMapKey(key.sessionId, key.topicId);
|
|
798
|
-
|
|
799
|
-
set(
|
|
800
|
-
produce((draft) => {
|
|
801
|
-
if (!draft.mainSendMessageOperations[operationKey])
|
|
802
|
-
draft.mainSendMessageOperations[operationKey] = value;
|
|
803
|
-
else {
|
|
804
|
-
if (value === null) {
|
|
805
|
-
delete draft.mainSendMessageOperations[operationKey];
|
|
806
|
-
} else {
|
|
807
|
-
draft.mainSendMessageOperations[operationKey] = {
|
|
808
|
-
...draft.mainSendMessageOperations[operationKey],
|
|
809
|
-
...value,
|
|
810
|
-
};
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
}),
|
|
814
|
-
false,
|
|
815
|
-
actionName ?? n('updateSendMessageOperation', { operationKey, value }),
|
|
816
|
-
);
|
|
817
|
-
},
|
|
818
|
-
internal_toggleSendMessageOperation: (key, loading: boolean, cancelReason?: string) => {
|
|
819
|
-
if (loading) {
|
|
820
|
-
const abortController = new AbortController();
|
|
821
|
-
|
|
822
|
-
get().internal_updateSendMessageOperation(
|
|
823
|
-
key,
|
|
824
|
-
{ isLoading: true, abortController },
|
|
825
|
-
n('toggleSendMessageOperation(start)', { key }),
|
|
826
|
-
);
|
|
827
|
-
|
|
828
|
-
return abortController;
|
|
829
|
-
} else {
|
|
830
|
-
const operationKey =
|
|
831
|
-
typeof key === 'string' ? key : messageMapKey(key.sessionId, key.topicId);
|
|
832
|
-
|
|
833
|
-
const operation = get().mainSendMessageOperations[operationKey];
|
|
834
|
-
|
|
835
|
-
// If cancelReason is provided, abort the operation first
|
|
836
|
-
if (cancelReason && operation?.isLoading) {
|
|
837
|
-
operation.abortController?.abort(cancelReason);
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
get().internal_updateSendMessageOperation(
|
|
841
|
-
key,
|
|
842
|
-
{ isLoading: false, abortController: null },
|
|
843
|
-
n('toggleSendMessageOperation(stop)', { key, cancelReason }),
|
|
844
|
-
);
|
|
845
|
-
|
|
846
|
-
return undefined;
|
|
847
|
-
}
|
|
848
|
-
},
|
|
849
|
-
});
|