@lobehub/chat 0.94.5 → 0.95.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.github/workflows/pr-welcome.yml +1 -1
  2. package/CHANGELOG.md +60 -0
  3. package/README.md +4 -4
  4. package/docs/Lighthouse.md +8 -8
  5. package/docs/Lighthouse.zh-CN.md +8 -8
  6. package/package.json +4 -4
  7. package/src/app/chat/(desktop)/features/ChatInput.tsx +0 -1
  8. package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +1 -1
  9. package/src/app/chat/features/Conversation/ChatList/Messages/Assistant.tsx +12 -15
  10. package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +26 -15
  11. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/Settings.tsx +36 -0
  12. package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/index.tsx +126 -0
  13. package/src/app/chat/features/Conversation/ChatList/Plugins/{style.ts → Inspector/style.ts} +1 -0
  14. package/src/app/chat/features/Conversation/ChatList/Plugins/{IFrameRender → Render/DefaultType/IFrameRender}/index.tsx +5 -16
  15. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/DefaultType/index.tsx +117 -0
  16. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +136 -0
  17. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/index.tsx +28 -0
  18. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/index.tsx +28 -0
  19. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/iframeOnReady.ts +23 -0
  20. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/listenToPlugin.ts +65 -0
  21. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/pluginSettings.ts +17 -0
  22. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/pluginState.ts +20 -0
  23. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/utils/postMessage.ts +17 -0
  24. package/src/const/plugin.ts +1 -1
  25. package/src/features/PluginSettings/index.tsx +5 -3
  26. package/src/features/PluginSettingsModal/index.tsx +45 -0
  27. package/src/locales/default/plugin.ts +3 -2
  28. package/src/store/plugin/selectors.ts +17 -4
  29. package/src/store/session/slices/chat/actions/index.ts +4 -1
  30. package/src/store/session/slices/chat/actions/message.ts +102 -117
  31. package/src/store/session/slices/chat/actions/plugin.ts +109 -0
  32. package/src/store/session/slices/chat/reducers/message.ts +31 -1
  33. package/src/store/session/slices/chat/selectors/chat.ts +17 -20
  34. package/src/store/session/slices/chat/selectors/index.ts +4 -2
  35. package/src/types/chatMessage.ts +1 -0
  36. package/src/utils/message.ts +8 -1
  37. package/src/app/chat/features/Conversation/ChatList/Plugins/FunctionCall.tsx +0 -90
  38. package/src/app/chat/features/Conversation/ChatList/Plugins/IFrameRender/hooks.ts +0 -29
  39. package/src/app/chat/features/Conversation/ChatList/Plugins/IFrameRender/utils.ts +0 -17
  40. package/src/app/chat/features/Conversation/ChatList/Plugins/PluginMessage.tsx +0 -68
  41. /package/src/app/chat/features/Conversation/ChatList/Plugins/{PluginResultRender.tsx → Inspector/PluginResultJSON.tsx} +0 -0
  42. /package/src/app/chat/features/Conversation/ChatList/Plugins/{SystemJsRender → Render/DefaultType/SystemJsRender}/index.tsx +0 -0
  43. /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, OpenAIFunctionCall } from '@/types/chatMessage';
7
+ import { ChatMessage } from '@/types/chatMessage';
11
8
  import { fetchSSE } from '@/utils/fetch';
12
- import { isFunctionMessage } from '@/utils/message';
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
- generateMessage: (
50
+ fetchAIChatMessage: (
48
51
  messages: ChatMessage[],
49
52
  assistantMessageId: string,
50
- ) => Promise<{ isFunctionCall: boolean }>;
51
- /**
52
- * 实际获取 AI 响应
53
- *
54
- * @param messages - 聊天消息数组
55
- * @param parentId - 父消息 ID,可选
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
- generateMessage: async (messages, assistantId) => {
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
- // 如果是 function call
179
- if (isFunctionMessage(output)) {
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
- return { isFunctionCall };
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
- // 添加一个空的信息用于放置 ai 响应,注意顺序不能反
196
- // 因为如果顺序反了,messages 中将包含新增的 ai message
197
- const mid = nanoid();
198
-
199
- dispatchMessage({
200
- id: mid,
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
- // 为模型添加 fromModel 的额外信息
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 { realFetchAIResponse } = get();
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 realFetchAIResponse(contextMessages, latestMsg.id);
302
+ await coreProcessMessage(contextMessages, latestMsg.id);
264
303
  },
265
304
 
266
305
  sendMessage: async (message) => {
267
- const { dispatchMessage, realFetchAIResponse, activeTopicId } = get();
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 realFetchAIResponse(messages, userId);
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 getFunctionMessageParams =
96
- (
97
- s: SessionStore,
98
- ): ((
99
- props: Pick<ChatMessage, 'plugin' | 'function_call' | 'content' | 'id'>,
100
- ) => FunctionCallProps) =>
101
- ({ plugin, function_call, content, id }) => {
102
- const itemId = plugin?.identifier || function_call?.name;
103
- const command = plugin ?? function_call;
104
- const args = command?.arguments;
105
-
106
- return {
107
- arguments: args,
108
- command,
109
- content,
110
- id: itemId,
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
- getFunctionMessageParams,
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
- getFunctionMessageParams,
18
+ getFunctionMessageProps,
19
+ getMessageById,
18
20
  };
19
21
 
20
22
  export const topicSelectors = {
@@ -46,6 +46,7 @@ export interface ChatMessage extends BaseDataModel {
46
46
  parentId?: string;
47
47
 
48
48
  plugin?: PluginRequestPayload;
49
+ pluginState?: any;
49
50
 
50
51
  // 引用
51
52
  quotaId?: string;
@@ -1,9 +1,16 @@
1
1
  import { FUNCTION_MESSAGE_FLAG } from '@/const/message';
2
2
 
3
- export const isFunctionMessage = (content: string) => {
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,90 +0,0 @@
1
- import { LoadingOutlined } from '@ant-design/icons';
2
- import { Avatar, Highlighter, Icon } from '@lobehub/ui';
3
- import { Tabs } from 'antd';
4
- import { LucideChevronDown, LucideChevronUp, LucideToyBrick } from 'lucide-react';
5
- import { memo, useState } from 'react';
6
- import { useTranslation } from 'react-i18next';
7
- import { Flexbox } from 'react-layout-kit';
8
-
9
- import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
10
-
11
- import PluginResult from './PluginResultRender';
12
- import { useStyles } from './style';
13
-
14
- export interface FunctionCallProps {
15
- arguments?: string;
16
- command?: any;
17
- content: string;
18
- id?: string;
19
- loading?: boolean;
20
- }
21
-
22
- const FunctionCall = memo<FunctionCallProps>(
23
- ({ arguments: requestArgs = '{}', command, loading, content, id = 'unknown' }) => {
24
- const { t } = useTranslation('plugin');
25
- const { styles } = useStyles();
26
- const [open, setOpen] = useState(false);
27
-
28
- const item = usePluginStore(pluginSelectors.getPluginMetaById(id));
29
-
30
- const pluginAvatar = pluginHelpers.getPluginAvatar(item?.meta);
31
- const pluginTitle = pluginHelpers.getPluginTitle(item?.meta);
32
-
33
- const avatar = pluginAvatar ? (
34
- <Avatar avatar={pluginAvatar} size={32} />
35
- ) : (
36
- <Icon icon={LucideToyBrick} />
37
- );
38
-
39
- const args = JSON.stringify(command, null, 2);
40
- const params = JSON.stringify(JSON.parse(requestArgs), null, 2);
41
-
42
- return (
43
- <Flexbox gap={8}>
44
- <Flexbox
45
- align={'center'}
46
- className={styles.container}
47
- gap={8}
48
- horizontal
49
- onClick={() => {
50
- setOpen(!open);
51
- }}
52
- >
53
- {loading ? (
54
- <div>
55
- <LoadingOutlined />
56
- </div>
57
- ) : (
58
- avatar
59
- )}
60
- {pluginTitle ?? t('plugins.unknown')}
61
- <Icon icon={open ? LucideChevronUp : LucideChevronDown} />
62
- </Flexbox>
63
- {open && (
64
- <Tabs
65
- items={[
66
- {
67
- children: <Highlighter language={'json'}>{args}</Highlighter>,
68
- key: 'function_call',
69
- label: t('debug.function_call'),
70
- },
71
- {
72
- children: <Highlighter language={'json'}>{params}</Highlighter>,
73
- key: 'arguments',
74
- label: t('debug.arguments'),
75
- },
76
- {
77
- children: <PluginResult content={content} />,
78
- key: 'response',
79
- label: t('debug.response'),
80
- },
81
- ]}
82
- style={{ maxWidth: 800 }}
83
- />
84
- )}
85
- </Flexbox>
86
- );
87
- },
88
- );
89
-
90
- export default FunctionCall;