@lobehub/chat 0.94.4 → 0.95.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/.github/workflows/pr-welcome.yml +1 -1
- package/CHANGELOG.md +60 -0
- package/CONTRIBUTING.md +88 -0
- package/package.json +4 -4
- package/src/app/chat/(desktop)/features/ChatInput.tsx +0 -1
- package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +1 -1
- package/src/app/chat/features/Conversation/ChatList/Messages/Assistant.tsx +12 -15
- package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +26 -15
- package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/Settings.tsx +36 -0
- package/src/app/chat/features/Conversation/ChatList/Plugins/{FunctionCall.tsx → Inspector/index.tsx} +53 -25
- package/src/app/chat/features/Conversation/ChatList/Plugins/{IFrameRender → Render/DefaultType/IFrameRender}/index.tsx +5 -16
- package/src/app/chat/features/Conversation/ChatList/Plugins/{PluginMessage.tsx → Render/DefaultType/index.tsx} +3 -3
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +136 -0
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/index.tsx +28 -0
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/index.tsx +28 -0
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/iframeOnReady.ts +23 -0
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/listenToPlugin.ts +65 -0
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/pluginSettings.ts +17 -0
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/pluginState.ts +20 -0
- package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/postMessage.ts +17 -0
- package/src/app/market/page.tsx +2 -22
- package/src/chains/summaryAgentName.ts +13 -8
- package/src/chains/summaryDescription.ts +28 -5
- package/src/chains/summaryTags.ts +8 -7
- package/src/chains/summaryTitle.ts +5 -3
- package/src/const/plugin.ts +1 -1
- package/src/features/PluginSettings/index.tsx +5 -3
- package/src/features/PluginSettingsModal/index.tsx +45 -0
- package/src/services/agentMarket.ts +9 -7
- package/src/services/pluginMarket.ts +2 -2
- package/src/store/global/helpers.ts +5 -0
- package/src/store/global/selectors/settings.ts +1 -0
- package/src/store/plugin/selectors.ts +10 -4
- package/src/store/session/slices/chat/actions/index.ts +4 -1
- package/src/store/session/slices/chat/actions/message.ts +102 -117
- package/src/store/session/slices/chat/actions/plugin.ts +109 -0
- package/src/store/session/slices/chat/reducers/message.ts +31 -1
- package/src/store/session/slices/chat/selectors/chat.ts +17 -20
- package/src/store/session/slices/chat/selectors/index.ts +4 -2
- package/src/types/chatMessage.ts +1 -0
- package/src/utils/message.ts +8 -1
- package/src/app/chat/features/Conversation/ChatList/Plugins/IFrameRender/hooks.ts +0 -29
- package/src/app/chat/features/Conversation/ChatList/Plugins/IFrameRender/utils.ts +0 -17
- /package/src/app/chat/features/Conversation/ChatList/Plugins/{PluginResultRender.tsx → Inspector/PluginResultJSON.tsx} +0 -0
- /package/src/app/chat/features/Conversation/ChatList/Plugins/{style.ts → Inspector/style.ts} +0 -0
- /package/src/app/chat/features/Conversation/ChatList/Plugins/{SystemJsRender → Render/DefaultType/SystemJsRender}/index.tsx +0 -0
- /package/src/app/chat/features/Conversation/ChatList/Plugins/{SystemJsRender → Render/DefaultType/SystemJsRender}/utils.ts +0 -0
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
|
|
2
1
|
import { template } from 'lodash-es';
|
|
3
2
|
import { StateCreator } from 'zustand/vanilla';
|
|
4
3
|
|
|
5
4
|
import { LOADING_FLAT } from '@/const/message';
|
|
6
|
-
import { PLUGIN_SCHEMA_SEPARATOR } from '@/const/plugin';
|
|
7
5
|
import { fetchChatModel } from '@/services/chatModel';
|
|
8
|
-
import { fetchPlugin } from '@/services/plugin';
|
|
9
6
|
import { SessionStore } from '@/store/session';
|
|
10
|
-
import { ChatMessage
|
|
7
|
+
import { ChatMessage } from '@/types/chatMessage';
|
|
11
8
|
import { fetchSSE } from '@/utils/fetch';
|
|
12
|
-
import {
|
|
9
|
+
import { isFunctionMessageAtStart, testFunctionMessageAtEnd } from '@/utils/message';
|
|
13
10
|
import { setNamespace } from '@/utils/storeDebug';
|
|
14
11
|
import { nanoid } from '@/utils/uuid';
|
|
15
12
|
|
|
@@ -29,6 +26,12 @@ export interface ChatMessageAction {
|
|
|
29
26
|
* 清除消息
|
|
30
27
|
*/
|
|
31
28
|
clearMessage: () => void;
|
|
29
|
+
/**
|
|
30
|
+
* 处理 ai 消息的核心逻辑(包含前处理与后处理)
|
|
31
|
+
* @param messages - 聊天消息数组
|
|
32
|
+
* @param parentId - 父消息 ID,可选
|
|
33
|
+
*/
|
|
34
|
+
coreProcessMessage: (messages: ChatMessage[], parentId: string) => Promise<void>;
|
|
32
35
|
/**
|
|
33
36
|
* 删除消息
|
|
34
37
|
* @param id - 消息 ID
|
|
@@ -40,21 +43,19 @@ export interface ChatMessageAction {
|
|
|
40
43
|
*/
|
|
41
44
|
dispatchMessage: (payload: MessageDispatch) => void;
|
|
42
45
|
/**
|
|
43
|
-
*
|
|
46
|
+
* 实际获取 AI 响应
|
|
44
47
|
* @param messages - 聊天消息数组
|
|
45
48
|
* @param options - 获取 SSE 选项
|
|
46
49
|
*/
|
|
47
|
-
|
|
50
|
+
fetchAIChatMessage: (
|
|
48
51
|
messages: ChatMessage[],
|
|
49
52
|
assistantMessageId: string,
|
|
50
|
-
) => Promise<{
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
*/
|
|
57
|
-
realFetchAIResponse: (messages: ChatMessage[], parentId: string) => Promise<void>;
|
|
53
|
+
) => Promise<{
|
|
54
|
+
content: string;
|
|
55
|
+
functionCallAtEnd: boolean;
|
|
56
|
+
functionCallContent: string;
|
|
57
|
+
isFunctionCall: boolean;
|
|
58
|
+
}>;
|
|
58
59
|
|
|
59
60
|
/**
|
|
60
61
|
* 重新发送消息
|
|
@@ -73,8 +74,6 @@ export interface ChatMessageAction {
|
|
|
73
74
|
id?: string,
|
|
74
75
|
action?: string,
|
|
75
76
|
) => AbortController | undefined;
|
|
76
|
-
|
|
77
|
-
triggerFunctionCall: (id: string) => Promise<void>;
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
export const chatMessage: StateCreator<
|
|
@@ -93,10 +92,74 @@ export const chatMessage: StateCreator<
|
|
|
93
92
|
}
|
|
94
93
|
},
|
|
95
94
|
|
|
95
|
+
coreProcessMessage: async (messages, userMessageId) => {
|
|
96
|
+
const { dispatchMessage, fetchAIChatMessage, triggerFunctionCall, activeTopicId } = get();
|
|
97
|
+
|
|
98
|
+
const { model } = agentSelectors.currentAgentConfig(get());
|
|
99
|
+
|
|
100
|
+
// 添加一个空的信息用于放置 ai 响应,注意顺序不能反
|
|
101
|
+
// 因为如果顺序反了,messages 中将包含新增的 ai message
|
|
102
|
+
const mid = nanoid();
|
|
103
|
+
|
|
104
|
+
dispatchMessage({
|
|
105
|
+
id: mid,
|
|
106
|
+
message: LOADING_FLAT,
|
|
107
|
+
parentId: userMessageId,
|
|
108
|
+
role: 'assistant',
|
|
109
|
+
type: 'addMessage',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// 如果有 activeTopicId,则添加 topicId
|
|
113
|
+
if (activeTopicId) {
|
|
114
|
+
dispatchMessage({ id: mid, key: 'topicId', type: 'updateMessage', value: activeTopicId });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 为模型添加 fromModel 的额外信息
|
|
118
|
+
dispatchMessage({ id: mid, key: 'fromModel', type: 'updateMessageExtra', value: model });
|
|
119
|
+
|
|
120
|
+
// 生成 ai message
|
|
121
|
+
const { isFunctionCall, content, functionCallAtEnd, functionCallContent } =
|
|
122
|
+
await fetchAIChatMessage(messages, mid);
|
|
123
|
+
|
|
124
|
+
// 如果是 function,则发送函数调用方法
|
|
125
|
+
if (isFunctionCall) {
|
|
126
|
+
let functionId = mid;
|
|
127
|
+
|
|
128
|
+
if (functionCallAtEnd) {
|
|
129
|
+
// create a new separate message and remove the function call from the prev message
|
|
130
|
+
dispatchMessage({
|
|
131
|
+
id: mid,
|
|
132
|
+
key: 'content',
|
|
133
|
+
type: 'updateMessage',
|
|
134
|
+
value: content.replace(functionCallContent, ''),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
functionId = nanoid();
|
|
138
|
+
dispatchMessage({
|
|
139
|
+
id: functionId,
|
|
140
|
+
message: functionCallContent,
|
|
141
|
+
parentId: userMessageId,
|
|
142
|
+
role: 'assistant',
|
|
143
|
+
type: 'addMessage',
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// also add activeTopicId
|
|
147
|
+
if (activeTopicId)
|
|
148
|
+
dispatchMessage({
|
|
149
|
+
id: functionId,
|
|
150
|
+
key: 'topicId',
|
|
151
|
+
type: 'updateMessage',
|
|
152
|
+
value: activeTopicId,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
triggerFunctionCall(functionId);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
|
|
96
160
|
deleteMessage: (id) => {
|
|
97
161
|
get().dispatchMessage({ id, type: 'deleteMessage' });
|
|
98
162
|
},
|
|
99
|
-
|
|
100
163
|
dispatchMessage: (payload) => {
|
|
101
164
|
const { activeId } = get();
|
|
102
165
|
const session = sessionSelectors.currentSession(get());
|
|
@@ -106,7 +169,8 @@ export const chatMessage: StateCreator<
|
|
|
106
169
|
|
|
107
170
|
get().dispatchSession({ chats, id: activeId, type: 'updateSessionChat' });
|
|
108
171
|
},
|
|
109
|
-
|
|
172
|
+
|
|
173
|
+
fetchAIChatMessage: async (messages, assistantId) => {
|
|
110
174
|
const { dispatchMessage, toggleChatLoading } = get();
|
|
111
175
|
|
|
112
176
|
const abortController = toggleChatLoading(
|
|
@@ -160,6 +224,8 @@ export const chatMessage: StateCreator<
|
|
|
160
224
|
|
|
161
225
|
let output = '';
|
|
162
226
|
let isFunctionCall = false;
|
|
227
|
+
let functionCallAtEnd = false;
|
|
228
|
+
let functionCallContent = '';
|
|
163
229
|
|
|
164
230
|
await fetchSSE(fetcher, {
|
|
165
231
|
onErrorHandle: (error) => {
|
|
@@ -168,15 +234,10 @@ export const chatMessage: StateCreator<
|
|
|
168
234
|
onMessageHandle: (text) => {
|
|
169
235
|
output += text;
|
|
170
236
|
|
|
171
|
-
dispatchMessage({
|
|
172
|
-
id: assistantId,
|
|
173
|
-
key: 'content',
|
|
174
|
-
type: 'updateMessage',
|
|
175
|
-
value: output,
|
|
176
|
-
});
|
|
237
|
+
dispatchMessage({ id: assistantId, key: 'content', type: 'updateMessage', value: output });
|
|
177
238
|
|
|
178
|
-
//
|
|
179
|
-
if (
|
|
239
|
+
// is this message is just a function call
|
|
240
|
+
if (isFunctionMessageAtStart(output)) {
|
|
180
241
|
isFunctionCall = true;
|
|
181
242
|
}
|
|
182
243
|
},
|
|
@@ -184,41 +245,19 @@ export const chatMessage: StateCreator<
|
|
|
184
245
|
|
|
185
246
|
toggleChatLoading(false, undefined, t('generateMessage(end)') as string);
|
|
186
247
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
realFetchAIResponse: async (messages, userMessageId) => {
|
|
191
|
-
const { dispatchMessage, generateMessage, triggerFunctionCall, activeTopicId } = get();
|
|
192
|
-
|
|
193
|
-
const { model } = agentSelectors.currentAgentConfig(get());
|
|
248
|
+
// also exist message like this: 请稍等,我帮您查询一下。{"function_call": {"name": "plugin-identifier____recommendClothes____standalone", "arguments": "{\n "mood": "",\n "gender": "man"\n}"}}
|
|
249
|
+
if (!isFunctionCall) {
|
|
250
|
+
const { content, valid } = testFunctionMessageAtEnd(output);
|
|
194
251
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
message: LOADING_FLAT,
|
|
202
|
-
parentId: userMessageId,
|
|
203
|
-
role: 'assistant',
|
|
204
|
-
type: 'addMessage',
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// 如果有 activeTopicId,则添加 topicId
|
|
208
|
-
if (activeTopicId) {
|
|
209
|
-
dispatchMessage({ id: mid, key: 'topicId', type: 'updateMessage', value: activeTopicId });
|
|
252
|
+
// if fc at end, replace the message
|
|
253
|
+
if (valid) {
|
|
254
|
+
isFunctionCall = true;
|
|
255
|
+
functionCallAtEnd = true;
|
|
256
|
+
functionCallContent = content;
|
|
257
|
+
}
|
|
210
258
|
}
|
|
211
259
|
|
|
212
|
-
|
|
213
|
-
dispatchMessage({ id: mid, key: 'fromModel', type: 'updateMessageExtra', value: model });
|
|
214
|
-
|
|
215
|
-
// 生成 ai message
|
|
216
|
-
const { isFunctionCall } = await generateMessage(messages, mid);
|
|
217
|
-
|
|
218
|
-
// 如果是 function,则发送函数调用方法
|
|
219
|
-
if (isFunctionCall) {
|
|
220
|
-
triggerFunctionCall(mid);
|
|
221
|
-
}
|
|
260
|
+
return { content: output, functionCallAtEnd, functionCallContent, isFunctionCall };
|
|
222
261
|
},
|
|
223
262
|
|
|
224
263
|
resendMessage: async (messageId) => {
|
|
@@ -254,17 +293,17 @@ export const chatMessage: StateCreator<
|
|
|
254
293
|
|
|
255
294
|
if (contextMessages.length <= 0) return;
|
|
256
295
|
|
|
257
|
-
const {
|
|
296
|
+
const { coreProcessMessage } = get();
|
|
258
297
|
|
|
259
298
|
const latestMsg = contextMessages.filter((s) => s.role === 'user').at(-1);
|
|
260
299
|
|
|
261
300
|
if (!latestMsg) return;
|
|
262
301
|
|
|
263
|
-
await
|
|
302
|
+
await coreProcessMessage(contextMessages, latestMsg.id);
|
|
264
303
|
},
|
|
265
304
|
|
|
266
305
|
sendMessage: async (message) => {
|
|
267
|
-
const { dispatchMessage,
|
|
306
|
+
const { dispatchMessage, coreProcessMessage, activeTopicId } = get();
|
|
268
307
|
const session = sessionSelectors.currentSession(get());
|
|
269
308
|
if (!session || !message) return;
|
|
270
309
|
|
|
@@ -279,7 +318,7 @@ export const chatMessage: StateCreator<
|
|
|
279
318
|
// Get the current messages to generate AI response
|
|
280
319
|
const messages = chatSelectors.currentChats(get());
|
|
281
320
|
|
|
282
|
-
await
|
|
321
|
+
await coreProcessMessage(messages, userId);
|
|
283
322
|
|
|
284
323
|
// check activeTopic and then auto create topic
|
|
285
324
|
const chats = chatSelectors.currentChats(get());
|
|
@@ -308,58 +347,4 @@ export const chatMessage: StateCreator<
|
|
|
308
347
|
set({ abortController: undefined, chatLoadingId: undefined }, false, action);
|
|
309
348
|
}
|
|
310
349
|
},
|
|
311
|
-
|
|
312
|
-
triggerFunctionCall: async (id) => {
|
|
313
|
-
const { dispatchMessage, realFetchAIResponse, toggleChatLoading } = get();
|
|
314
|
-
const session = sessionSelectors.currentSession(get());
|
|
315
|
-
|
|
316
|
-
if (!session) return;
|
|
317
|
-
|
|
318
|
-
const message = session.chats[id];
|
|
319
|
-
if (!message) return;
|
|
320
|
-
|
|
321
|
-
let payload: PluginRequestPayload = { apiName: '', identifier: '' };
|
|
322
|
-
// 识别到内容是 function_call 的情况下
|
|
323
|
-
// 将 function_call 转换为 plugin request payload
|
|
324
|
-
if (message.content) {
|
|
325
|
-
const { function_call } = JSON.parse(message.content) as {
|
|
326
|
-
function_call: OpenAIFunctionCall;
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
const [identifier, apiName] = function_call.name.split(PLUGIN_SCHEMA_SEPARATOR);
|
|
330
|
-
payload = { apiName, arguments: function_call.arguments, identifier };
|
|
331
|
-
|
|
332
|
-
dispatchMessage({ id, key: 'plugin', type: 'updateMessage', value: payload });
|
|
333
|
-
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: '' });
|
|
334
|
-
} else {
|
|
335
|
-
if (message.plugin) {
|
|
336
|
-
payload = message.plugin;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (!payload.apiName) return;
|
|
341
|
-
|
|
342
|
-
dispatchMessage({ id, key: 'role', type: 'updateMessage', value: 'function' });
|
|
343
|
-
dispatchMessage({ id, key: 'name', type: 'updateMessage', value: payload.identifier });
|
|
344
|
-
dispatchMessage({ id, key: 'plugin', type: 'updateMessage', value: payload });
|
|
345
|
-
|
|
346
|
-
let data: string;
|
|
347
|
-
try {
|
|
348
|
-
const abortController = toggleChatLoading(true, id);
|
|
349
|
-
data = await fetchPlugin(payload, { signal: abortController?.signal });
|
|
350
|
-
} catch (error) {
|
|
351
|
-
dispatchMessage({ id, key: 'error', type: 'updateMessage', value: error });
|
|
352
|
-
|
|
353
|
-
data = '';
|
|
354
|
-
}
|
|
355
|
-
toggleChatLoading(false);
|
|
356
|
-
// 如果报错则结束了
|
|
357
|
-
if (!data) return;
|
|
358
|
-
|
|
359
|
-
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: data });
|
|
360
|
-
|
|
361
|
-
const chats = chatSelectors.currentChats(get());
|
|
362
|
-
|
|
363
|
-
await realFetchAIResponse(chats, message.id);
|
|
364
|
-
},
|
|
365
350
|
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
|
|
2
|
+
import { StateCreator } from 'zustand/vanilla';
|
|
3
|
+
|
|
4
|
+
import { PLUGIN_SCHEMA_SEPARATOR } from '@/const/plugin';
|
|
5
|
+
import { fetchPlugin } from '@/services/plugin';
|
|
6
|
+
import { SessionStore } from '@/store/session';
|
|
7
|
+
import { OpenAIFunctionCall } from '@/types/chatMessage';
|
|
8
|
+
import { setNamespace } from '@/utils/storeDebug';
|
|
9
|
+
|
|
10
|
+
import { sessionSelectors } from '../../session/selectors';
|
|
11
|
+
import { chatSelectors } from '../selectors';
|
|
12
|
+
|
|
13
|
+
const t = setNamespace('chat/plugin');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 插件方法
|
|
17
|
+
*/
|
|
18
|
+
export interface ChatPluginAction {
|
|
19
|
+
fillPluginMessageContent: (id: string, content: string) => Promise<void>;
|
|
20
|
+
runPluginDefaultType: (id: string, payload: any) => Promise<void>;
|
|
21
|
+
triggerFunctionCall: (id: string) => Promise<void>;
|
|
22
|
+
updatePluginState: (id: string, key: string, value: any) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const chatPlugin: StateCreator<
|
|
26
|
+
SessionStore,
|
|
27
|
+
[['zustand/devtools', never]],
|
|
28
|
+
[],
|
|
29
|
+
ChatPluginAction
|
|
30
|
+
> = (set, get) => ({
|
|
31
|
+
fillPluginMessageContent: async (id, content) => {
|
|
32
|
+
const { dispatchMessage, coreProcessMessage } = get();
|
|
33
|
+
|
|
34
|
+
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: content });
|
|
35
|
+
|
|
36
|
+
const chats = chatSelectors.currentChats(get());
|
|
37
|
+
await coreProcessMessage(chats, id);
|
|
38
|
+
},
|
|
39
|
+
runPluginDefaultType: async (id, payload) => {
|
|
40
|
+
const { dispatchMessage, coreProcessMessage, toggleChatLoading } = get();
|
|
41
|
+
let data: string;
|
|
42
|
+
try {
|
|
43
|
+
const abortController = toggleChatLoading(true, id, t('fetchPlugin') as string);
|
|
44
|
+
data = await fetchPlugin(payload, { signal: abortController?.signal });
|
|
45
|
+
} catch (error) {
|
|
46
|
+
dispatchMessage({ id, key: 'error', type: 'updateMessage', value: error });
|
|
47
|
+
|
|
48
|
+
data = '';
|
|
49
|
+
}
|
|
50
|
+
toggleChatLoading(false);
|
|
51
|
+
// 如果报错则结束了
|
|
52
|
+
if (!data) return;
|
|
53
|
+
|
|
54
|
+
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: data });
|
|
55
|
+
|
|
56
|
+
const chats = chatSelectors.currentChats(get());
|
|
57
|
+
await coreProcessMessage(chats, id);
|
|
58
|
+
},
|
|
59
|
+
triggerFunctionCall: async (id) => {
|
|
60
|
+
const { dispatchMessage, runPluginDefaultType } = get();
|
|
61
|
+
const session = sessionSelectors.currentSession(get());
|
|
62
|
+
|
|
63
|
+
if (!session) return;
|
|
64
|
+
|
|
65
|
+
const message = session.chats[id];
|
|
66
|
+
if (!message) return;
|
|
67
|
+
|
|
68
|
+
let payload: PluginRequestPayload = { apiName: '', identifier: '' };
|
|
69
|
+
|
|
70
|
+
// 识别到内容是 function_call 的情况下
|
|
71
|
+
// 将 function_call 转换为 plugin request payload
|
|
72
|
+
if (message.content) {
|
|
73
|
+
const { function_call } = JSON.parse(message.content) as {
|
|
74
|
+
function_call: OpenAIFunctionCall;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const [identifier, apiName, type] = function_call.name.split(PLUGIN_SCHEMA_SEPARATOR);
|
|
78
|
+
|
|
79
|
+
payload = {
|
|
80
|
+
apiName,
|
|
81
|
+
arguments: function_call.arguments,
|
|
82
|
+
identifier,
|
|
83
|
+
type: type ?? 'default',
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
dispatchMessage({ id, key: 'plugin', type: 'updateMessage', value: payload });
|
|
87
|
+
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: '' });
|
|
88
|
+
} else {
|
|
89
|
+
if (message.plugin) {
|
|
90
|
+
payload = message.plugin;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!payload.apiName) return;
|
|
95
|
+
|
|
96
|
+
dispatchMessage({ id, key: 'role', type: 'updateMessage', value: 'function' });
|
|
97
|
+
dispatchMessage({ id, key: 'name', type: 'updateMessage', value: payload.identifier });
|
|
98
|
+
dispatchMessage({ id, key: 'plugin', type: 'updateMessage', value: payload });
|
|
99
|
+
|
|
100
|
+
if (payload.type === 'standalone') {
|
|
101
|
+
// nothing to do
|
|
102
|
+
} else runPluginDefaultType(id, payload);
|
|
103
|
+
},
|
|
104
|
+
updatePluginState: (id, key, value) => {
|
|
105
|
+
const { dispatchMessage } = get();
|
|
106
|
+
|
|
107
|
+
dispatchMessage({ id, key, type: 'updatePluginState', value });
|
|
108
|
+
},
|
|
109
|
+
});
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import isEqual from 'fast-deep-equal';
|
|
1
2
|
import { produce } from 'immer';
|
|
2
3
|
|
|
3
4
|
import { ChatMessage, ChatMessageMap } from '@/types/chatMessage';
|
|
4
5
|
import { LLMRoleType } from '@/types/llm';
|
|
5
6
|
import { MetaData } from '@/types/meta';
|
|
7
|
+
import { merge } from '@/utils/merge';
|
|
6
8
|
import { nanoid } from '@/utils/uuid';
|
|
7
9
|
|
|
8
10
|
interface AddMessage {
|
|
@@ -38,12 +40,20 @@ interface UpdateMessageExtra {
|
|
|
38
40
|
value: any;
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
interface UpdatePluginState {
|
|
44
|
+
id: string;
|
|
45
|
+
key: string;
|
|
46
|
+
type: 'updatePluginState';
|
|
47
|
+
value: any;
|
|
48
|
+
}
|
|
49
|
+
|
|
41
50
|
export type MessageDispatch =
|
|
42
51
|
| AddMessage
|
|
43
52
|
| DeleteMessage
|
|
44
53
|
| ResetMessages
|
|
45
54
|
| UpdateMessage
|
|
46
|
-
| UpdateMessageExtra
|
|
55
|
+
| UpdateMessageExtra
|
|
56
|
+
| UpdatePluginState;
|
|
47
57
|
|
|
48
58
|
export const messagesReducer = (
|
|
49
59
|
state: ChatMessageMap,
|
|
@@ -101,6 +111,26 @@ export const messagesReducer = (
|
|
|
101
111
|
});
|
|
102
112
|
}
|
|
103
113
|
|
|
114
|
+
case 'updatePluginState': {
|
|
115
|
+
return produce(state, (draftState) => {
|
|
116
|
+
const { id, key, value } = payload;
|
|
117
|
+
const message = draftState[id];
|
|
118
|
+
if (!message) return;
|
|
119
|
+
|
|
120
|
+
let newState;
|
|
121
|
+
if (!message.pluginState) {
|
|
122
|
+
newState = { [key]: value } as any;
|
|
123
|
+
} else {
|
|
124
|
+
newState = merge(message.pluginState, { [key]: value });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (isEqual(message.pluginState, newState)) return;
|
|
128
|
+
|
|
129
|
+
message.pluginState = newState;
|
|
130
|
+
message.updateAt = Date.now();
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
104
134
|
case 'resetMessages': {
|
|
105
135
|
return produce(state, (draftState) => {
|
|
106
136
|
const { topicId } = payload;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { LobePluginType } from '@lobehub/chat-plugin-sdk';
|
|
1
2
|
import { t } from 'i18next';
|
|
2
3
|
|
|
3
|
-
import { FunctionCallProps } from '@/app/chat/features/Conversation/ChatList/Plugins/FunctionCall';
|
|
4
4
|
import { DEFAULT_INBOX_AVATAR, DEFAULT_USER_AVATAR } from '@/const/meta';
|
|
5
5
|
import { INBOX_SESSION_ID } from '@/const/session';
|
|
6
6
|
import { useGlobalStore } from '@/store/global';
|
|
@@ -92,22 +92,19 @@ export const chatsMessageString = (s: SessionStore): string => {
|
|
|
92
92
|
return chats.map((m) => m.content).join('');
|
|
93
93
|
};
|
|
94
94
|
|
|
95
|
-
export const
|
|
96
|
-
(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
loading: id === s.chatLoadingId,
|
|
112
|
-
};
|
|
113
|
-
};
|
|
95
|
+
export const getFunctionMessageProps =
|
|
96
|
+
({ plugin, content, id }: Pick<ChatMessage, 'plugin' | 'content' | 'id'>) =>
|
|
97
|
+
(s: SessionStore) => ({
|
|
98
|
+
arguments: plugin?.arguments,
|
|
99
|
+
command: plugin,
|
|
100
|
+
content,
|
|
101
|
+
id: plugin?.identifier,
|
|
102
|
+
loading: id === s.chatLoadingId,
|
|
103
|
+
type: plugin?.type as LobePluginType,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
export const getMessageById = (id: string) => (s: SessionStore) => {
|
|
107
|
+
for (const e of Object.values(s.sessions)) {
|
|
108
|
+
if (e.chats[id]) return e.chats[id];
|
|
109
|
+
}
|
|
110
|
+
};
|
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
currentChatsWithGuideMessage,
|
|
5
5
|
currentChatsWithHistoryConfig,
|
|
6
6
|
getChatsById,
|
|
7
|
-
|
|
7
|
+
getFunctionMessageProps,
|
|
8
|
+
getMessageById,
|
|
8
9
|
} from './chat';
|
|
9
10
|
import { currentTopics, getTopicMessages } from './topic';
|
|
10
11
|
|
|
@@ -14,7 +15,8 @@ export const chatSelectors = {
|
|
|
14
15
|
currentChatsWithGuideMessage,
|
|
15
16
|
currentChatsWithHistoryConfig,
|
|
16
17
|
getChatsById,
|
|
17
|
-
|
|
18
|
+
getFunctionMessageProps,
|
|
19
|
+
getMessageById,
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
export const topicSelectors = {
|
package/src/types/chatMessage.ts
CHANGED
package/src/utils/message.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { FUNCTION_MESSAGE_FLAG } from '@/const/message';
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const isFunctionMessageAtStart = (content: string) => {
|
|
4
4
|
return content.startsWith(FUNCTION_MESSAGE_FLAG);
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
export const testFunctionMessageAtEnd = (content: string) => {
|
|
8
|
+
const regExp = /{"function_call":.*?}}/;
|
|
9
|
+
const match = content.match(regExp);
|
|
10
|
+
|
|
11
|
+
return { content: match ? match[0] : '', valid: match };
|
|
12
|
+
};
|
|
13
|
+
|
|
7
14
|
// export const createFunctionCallMessage = () => {
|
|
8
15
|
// return [
|
|
9
16
|
// {
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
import { onPluginFetchMessage, onPluginReady } from './utils';
|
|
4
|
-
|
|
5
|
-
export const useOnPluginReady = (onReady: () => void) => {
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
const fn = (e: MessageEvent) => {
|
|
8
|
-
onPluginReady(e, onReady);
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
window.addEventListener('message', fn);
|
|
12
|
-
return () => {
|
|
13
|
-
window.removeEventListener('message', fn);
|
|
14
|
-
};
|
|
15
|
-
}, []);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const useOnPluginFetchMessage = (onRequest: (data: any) => void, deps: any[] = []) => {
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
const fn = (e: MessageEvent) => {
|
|
21
|
-
onPluginFetchMessage(e, onRequest);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
window.addEventListener('message', fn);
|
|
25
|
-
return () => {
|
|
26
|
-
window.removeEventListener('message', fn);
|
|
27
|
-
};
|
|
28
|
-
}, deps);
|
|
29
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { PluginChannel } from '@lobehub/chat-plugin-sdk/client';
|
|
2
|
-
|
|
3
|
-
export const onPluginReady = (e: MessageEvent, onReady: () => void) => {
|
|
4
|
-
if (e.data.type === PluginChannel.pluginReadyForRender) {
|
|
5
|
-
onReady();
|
|
6
|
-
}
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export const onPluginFetchMessage = (e: MessageEvent, onRequest: (data: any) => void) => {
|
|
10
|
-
if (e.data.type === PluginChannel.fetchPluginMessage) {
|
|
11
|
-
onRequest(e.data);
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const sendMessageToPlugin = (window: Window, props: any) => {
|
|
16
|
-
window.postMessage({ props, type: PluginChannel.renderPlugin }, '*');
|
|
17
|
-
};
|
|
File without changes
|
/package/src/app/chat/features/Conversation/ChatList/Plugins/{style.ts → Inspector/style.ts}
RENAMED
|
File without changes
|
|
File without changes
|