@lobehub/chat 1.106.4 → 1.106.5
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 +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/BuiltinPluginTitle.tsx +2 -9
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResultJSON.tsx +7 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +2 -2
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/index.tsx +5 -11
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/index.tsx +37 -12
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +43 -34
- package/src/features/Conversation/Messages/Assistant/Tool/index.tsx +23 -6
- package/src/features/Conversation/Messages/Assistant/index.tsx +1 -0
- package/src/features/Conversation/components/VirtualizedList/index.tsx +0 -1
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +10 -0
- package/src/store/chat/slices/aiChat/initialState.ts +2 -0
- package/src/store/chat/slices/message/selectors.ts +9 -0
- package/src/store/chat/slices/plugin/action.ts +2 -0
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.106.5](https://github.com/lobehub/lobe-chat/compare/v1.106.4...v1.106.5)
|
6
|
+
|
7
|
+
<sup>Released on **2025-07-30**</sup>
|
8
|
+
|
9
|
+
#### 💄 Styles
|
10
|
+
|
11
|
+
- **misc**: Improve mcp plugin calling and display.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### Styles
|
19
|
+
|
20
|
+
- **misc**: Improve mcp plugin calling and display, closes [#8619](https://github.com/lobehub/lobe-chat/issues/8619) ([14c41c4](https://github.com/lobehub/lobe-chat/commit/14c41c4))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.106.4](https://github.com/lobehub/lobe-chat/compare/v1.106.3...v1.106.4)
|
6
31
|
|
7
32
|
<sup>Released on **2025-07-30**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.106.
|
3
|
+
"version": "1.106.5",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
@@ -33,17 +33,10 @@ interface BuiltinPluginTitleProps {
|
|
33
33
|
}
|
34
34
|
|
35
35
|
const BuiltinPluginTitle = memo<BuiltinPluginTitleProps>(
|
36
|
-
({ messageId, index, apiName,
|
36
|
+
({ messageId, index, apiName, icon, title }) => {
|
37
37
|
const { styles } = useStyles();
|
38
38
|
|
39
|
-
const isLoading = useChatStore((
|
40
|
-
const toolMessageId = chatSelectors.getMessageByToolCallId(toolCallId)(s)?.id;
|
41
|
-
const isToolCallStreaming = chatSelectors.isToolCallStreaming(messageId, index)(s);
|
42
|
-
const isPluginApiInvoking = !toolMessageId
|
43
|
-
? true
|
44
|
-
: chatSelectors.isPluginApiInvoking(toolMessageId)(s);
|
45
|
-
return isToolCallStreaming || isPluginApiInvoking;
|
46
|
-
});
|
39
|
+
const isLoading = useChatStore(chatSelectors.isInToolsCalling(messageId, index));
|
47
40
|
|
48
41
|
return (
|
49
42
|
<Flexbox align={'center'} className={isLoading ? styles.shinyText : ''} gap={4} horizontal>
|
@@ -6,9 +6,10 @@ import { chatSelectors } from '@/store/chat/selectors';
|
|
6
6
|
|
7
7
|
export interface FunctionMessageProps {
|
8
8
|
toolCallId: string;
|
9
|
+
variant?: 'filled' | 'outlined' | 'borderless';
|
9
10
|
}
|
10
11
|
|
11
|
-
const PluginResult = memo<FunctionMessageProps>(({ toolCallId }) => {
|
12
|
+
const PluginResult = memo<FunctionMessageProps>(({ toolCallId, variant }) => {
|
12
13
|
const toolMessage = useChatStore(chatSelectors.getMessageByToolCallId(toolCallId));
|
13
14
|
|
14
15
|
const data = useMemo(() => {
|
@@ -20,7 +21,11 @@ const PluginResult = memo<FunctionMessageProps>(({ toolCallId }) => {
|
|
20
21
|
}, [toolMessage?.content]);
|
21
22
|
|
22
23
|
return (
|
23
|
-
<Highlighter
|
24
|
+
<Highlighter
|
25
|
+
language={'json'}
|
26
|
+
style={{ maxHeight: 200, overflow: 'scroll', width: '100%' }}
|
27
|
+
variant={variant}
|
28
|
+
>
|
24
29
|
{data}
|
25
30
|
</Highlighter>
|
26
31
|
);
|
@@ -91,8 +91,8 @@ const ToolTitle = memo<ToolTitleProps>(({ identifier, messageId, index, apiName,
|
|
91
91
|
const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
|
92
92
|
|
93
93
|
return (
|
94
|
-
<Flexbox align={'center'} className={isLoading ? styles.shinyText : ''} gap={
|
95
|
-
{isLoading ? <Loader /> : <PluginAvatar identifier={identifier} size={
|
94
|
+
<Flexbox align={'center'} className={isLoading ? styles.shinyText : ''} gap={6} horizontal>
|
95
|
+
{isLoading ? <Loader /> : <PluginAvatar identifier={identifier} size={18} />}
|
96
96
|
<div>{pluginTitle}</div>/<span className={styles.apiName}>{apiName}</span>
|
97
97
|
</Flexbox>
|
98
98
|
);
|
@@ -1,13 +1,6 @@
|
|
1
|
-
import { ActionIcon
|
1
|
+
import { ActionIcon } from '@lobehub/ui';
|
2
2
|
import { createStyles } from 'antd-style';
|
3
|
-
import {
|
4
|
-
ChevronDown,
|
5
|
-
ChevronRight,
|
6
|
-
LayoutPanelTop,
|
7
|
-
LogsIcon,
|
8
|
-
LucideBug,
|
9
|
-
LucideBugOff,
|
10
|
-
} from 'lucide-react';
|
3
|
+
import { LayoutPanelTop, LogsIcon, LucideBug, LucideBugOff } from 'lucide-react';
|
11
4
|
import { CSSProperties, memo, useState } from 'react';
|
12
5
|
import { useTranslation } from 'react-i18next';
|
13
6
|
import { Flexbox } from 'react-layout-kit';
|
@@ -68,6 +61,7 @@ export const useStyles = createStyles(({ css, token, cx }) => ({
|
|
68
61
|
interface InspectorProps {
|
69
62
|
apiName: string;
|
70
63
|
arguments?: string;
|
64
|
+
hidePluginUI?: boolean;
|
71
65
|
id: string;
|
72
66
|
identifier: string;
|
73
67
|
index: number;
|
@@ -94,6 +88,7 @@ const Inspectors = memo<InspectorProps>(
|
|
94
88
|
setShowRender,
|
95
89
|
showPluginRender,
|
96
90
|
setShowPluginRender,
|
91
|
+
hidePluginUI = false,
|
97
92
|
}) => {
|
98
93
|
const { t } = useTranslation('plugin');
|
99
94
|
const { styles } = useStyles();
|
@@ -120,10 +115,9 @@ const Inspectors = memo<InspectorProps>(
|
|
120
115
|
messageId={messageId}
|
121
116
|
toolCallId={id}
|
122
117
|
/>
|
123
|
-
<Icon icon={showRender ? ChevronDown : ChevronRight} />
|
124
118
|
</Flexbox>
|
125
119
|
<Flexbox className={styles.actions} horizontal>
|
126
|
-
{showRender && (
|
120
|
+
{showRender && !hidePluginUI && (
|
127
121
|
<ActionIcon
|
128
122
|
icon={showPluginRender ? LogsIcon : LayoutPanelTop}
|
129
123
|
onClick={() => {
|
@@ -16,9 +16,15 @@ const useStyles = createStyles(({ css, token, cx }) => ({
|
|
16
16
|
color: ${token.colorText};
|
17
17
|
}
|
18
18
|
`,
|
19
|
+
codeContainer: css`
|
20
|
+
border-radius: ${token.borderRadiusLG}px;
|
21
|
+
`,
|
19
22
|
container: css`
|
20
23
|
position: relative;
|
21
24
|
|
25
|
+
overflow: auto;
|
26
|
+
|
27
|
+
max-height: 200px;
|
22
28
|
padding-block: 4px;
|
23
29
|
padding-inline: 12px 64px;
|
24
30
|
border-radius: ${token.borderRadiusLG}px;
|
@@ -90,6 +96,14 @@ const Arguments = memo<ArgumentsProps>(({ arguments: args = '', shine, actions }
|
|
90
96
|
);
|
91
97
|
}
|
92
98
|
|
99
|
+
// if (args.length > 100) {
|
100
|
+
// return (
|
101
|
+
// <Highlighter language={'json'} showLanguage={false} variant={'filled'}>
|
102
|
+
// {JSON.stringify(displayArgs, null, 2)}
|
103
|
+
// </Highlighter>
|
104
|
+
// );
|
105
|
+
// }
|
106
|
+
|
93
107
|
const hasMinWidth = Object.keys(displayArgs).length > 1;
|
94
108
|
|
95
109
|
if (Object.keys(displayArgs).length === 0) return null;
|
@@ -101,18 +115,29 @@ const Arguments = memo<ArgumentsProps>(({ arguments: args = '', shine, actions }
|
|
101
115
|
{actions}
|
102
116
|
</Flexbox>
|
103
117
|
)}
|
104
|
-
{
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
)
|
115
|
-
|
118
|
+
{args.length > 100 ? (
|
119
|
+
<Highlighter
|
120
|
+
language={'json'}
|
121
|
+
showLanguage={false}
|
122
|
+
style={{ padding: 8 }}
|
123
|
+
variant={'borderless'}
|
124
|
+
>
|
125
|
+
{JSON.stringify(displayArgs, null, 2)}
|
126
|
+
</Highlighter>
|
127
|
+
) : (
|
128
|
+
Object.entries(displayArgs).map(([key, value]) => {
|
129
|
+
return (
|
130
|
+
<ObjectEntity
|
131
|
+
editable={false}
|
132
|
+
hasMinWidth={hasMinWidth}
|
133
|
+
key={key}
|
134
|
+
objectKey={key}
|
135
|
+
shine={shine}
|
136
|
+
value={value}
|
137
|
+
/>
|
138
|
+
);
|
139
|
+
})
|
140
|
+
)}
|
116
141
|
</div>
|
117
142
|
);
|
118
143
|
});
|
@@ -6,6 +6,7 @@ import { memo, useCallback, useEffect, useState } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
7
7
|
import { Flexbox } from 'react-layout-kit';
|
8
8
|
|
9
|
+
import PluginResult from '@/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResultJSON';
|
9
10
|
import PluginRender from '@/features/PluginsUI/Render';
|
10
11
|
import { useChatStore } from '@/store/chat';
|
11
12
|
import { chatSelectors } from '@/store/chat/selectors';
|
@@ -39,6 +40,7 @@ const CustomRender = memo<CustomRenderProps>(
|
|
39
40
|
showPluginRender,
|
40
41
|
setShowPluginRender,
|
41
42
|
pluginError,
|
43
|
+
tool_call_id,
|
42
44
|
}) => {
|
43
45
|
const { t } = useTranslation(['tool', 'common']);
|
44
46
|
const [loading] = useChatStore((s) => [chatSelectors.isPluginApiInvoking(id)(s)]);
|
@@ -80,9 +82,9 @@ const CustomRender = memo<CustomRenderProps>(
|
|
80
82
|
|
81
83
|
if (loading) return <Arguments arguments={requestArgs} shine />;
|
82
84
|
|
83
|
-
|
84
|
-
|
85
|
-
{
|
85
|
+
if (showPluginRender)
|
86
|
+
return (
|
87
|
+
<Flexbox gap={12} id={id} width={'100%'}>
|
86
88
|
<PluginRender
|
87
89
|
arguments={plugin?.arguments}
|
88
90
|
content={content}
|
@@ -94,37 +96,44 @@ const CustomRender = memo<CustomRenderProps>(
|
|
94
96
|
pluginState={pluginState}
|
95
97
|
type={plugin?.type}
|
96
98
|
/>
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
99
|
+
</Flexbox>
|
100
|
+
);
|
101
|
+
|
102
|
+
if (isEditing)
|
103
|
+
return (
|
104
|
+
<KeyValueEditor
|
105
|
+
initialValue={safeParseJson(requestArgs || '')}
|
106
|
+
onCancel={handleCancel}
|
107
|
+
onFinish={handleFinish}
|
108
|
+
/>
|
109
|
+
);
|
110
|
+
|
111
|
+
return (
|
112
|
+
<Flexbox gap={12} id={id} width={'100%'}>
|
113
|
+
<Arguments
|
114
|
+
actions={
|
115
|
+
<>
|
116
|
+
<ActionIcon
|
117
|
+
icon={Edit3Icon}
|
118
|
+
onClick={() => {
|
119
|
+
setIsEditing(true);
|
120
|
+
}}
|
121
|
+
size={'small'}
|
122
|
+
title={t('edit', { ns: 'common' })}
|
123
|
+
/>
|
124
|
+
<ActionIcon
|
125
|
+
icon={PlayCircleIcon}
|
126
|
+
onClick={async () => {
|
127
|
+
await reInvokeToolMessage(id);
|
128
|
+
}}
|
129
|
+
size={'small'}
|
130
|
+
title={t('run', { ns: 'common' })}
|
131
|
+
/>
|
132
|
+
</>
|
133
|
+
}
|
134
|
+
arguments={requestArgs}
|
135
|
+
/>
|
136
|
+
{tool_call_id && <PluginResult toolCallId={tool_call_id} variant={'outlined'} />}
|
128
137
|
</Flexbox>
|
129
138
|
);
|
130
139
|
},
|
@@ -1,7 +1,9 @@
|
|
1
|
-
import { CSSProperties, memo, useState } from 'react';
|
1
|
+
import { CSSProperties, memo, useEffect, useState } from 'react';
|
2
2
|
import { Flexbox } from 'react-layout-kit';
|
3
3
|
|
4
4
|
import AnimatedCollapsed from '@/components/AnimatedCollapsed';
|
5
|
+
import { useChatStore } from '@/store/chat';
|
6
|
+
import { chatSelectors } from '@/store/chat/selectors';
|
5
7
|
|
6
8
|
import Inspectors from './Inspector';
|
7
9
|
import Render from './Render';
|
@@ -15,29 +17,44 @@ export interface InspectorProps {
|
|
15
17
|
messageId: string;
|
16
18
|
payload: object;
|
17
19
|
style?: CSSProperties;
|
20
|
+
type?: string;
|
18
21
|
}
|
19
22
|
|
20
23
|
const Tool = memo<InspectorProps>(
|
21
|
-
({ arguments: requestArgs, apiName, messageId, id, index, identifier, style, payload }) => {
|
22
|
-
const [
|
24
|
+
({ arguments: requestArgs, apiName, messageId, id, index, identifier, style, payload, type }) => {
|
25
|
+
const [showDetail, setShowDetail] = useState(type !== 'mcp');
|
23
26
|
const [showPluginRender, setShowPluginRender] = useState(false);
|
27
|
+
const isLoading = useChatStore(chatSelectors.isInToolsCalling(messageId, index));
|
28
|
+
|
29
|
+
useEffect(() => {
|
30
|
+
if (type !== 'mcp') return;
|
31
|
+
|
32
|
+
setTimeout(
|
33
|
+
() => {
|
34
|
+
setShowDetail(isLoading);
|
35
|
+
},
|
36
|
+
isLoading ? 1 : 1500,
|
37
|
+
);
|
38
|
+
}, [isLoading]);
|
24
39
|
|
25
40
|
return (
|
26
41
|
<Flexbox gap={8} style={style}>
|
27
42
|
<Inspectors
|
28
43
|
apiName={apiName}
|
29
44
|
arguments={requestArgs}
|
45
|
+
// mcp don't have ui render
|
46
|
+
hidePluginUI={type === 'mcp'}
|
30
47
|
id={id}
|
31
48
|
identifier={identifier}
|
32
49
|
index={index}
|
33
50
|
messageId={messageId}
|
34
51
|
payload={payload}
|
35
52
|
setShowPluginRender={setShowPluginRender}
|
36
|
-
setShowRender={
|
53
|
+
setShowRender={setShowDetail}
|
37
54
|
showPluginRender={showPluginRender}
|
38
|
-
showRender={
|
55
|
+
showRender={showDetail}
|
39
56
|
/>
|
40
|
-
<AnimatedCollapsed open={
|
57
|
+
<AnimatedCollapsed open={showDetail}>
|
41
58
|
<Render
|
42
59
|
messageId={messageId}
|
43
60
|
requestArgs={requestArgs}
|
@@ -108,6 +108,11 @@ export interface AIGenerateAction {
|
|
108
108
|
id?: string,
|
109
109
|
action?: Action,
|
110
110
|
) => AbortController | undefined;
|
111
|
+
internal_toggleMessageInToolsCalling: (
|
112
|
+
loading: boolean,
|
113
|
+
id?: string,
|
114
|
+
action?: Action,
|
115
|
+
) => AbortController | undefined;
|
111
116
|
/**
|
112
117
|
* Controls the streaming state of tool calling processes, updating the UI accordingly
|
113
118
|
*/
|
@@ -445,6 +450,7 @@ export const generateAIChat: StateCreator<
|
|
445
450
|
|
446
451
|
// if it's the function call message, trigger the function method
|
447
452
|
if (isToolsCalling) {
|
453
|
+
get().internal_toggleMessageInToolsCalling(true, assistantId);
|
448
454
|
await refreshMessages();
|
449
455
|
await triggerToolCalls(assistantId, {
|
450
456
|
threadId: params?.threadId,
|
@@ -467,6 +473,7 @@ export const generateAIChat: StateCreator<
|
|
467
473
|
|
468
474
|
// 5. if it's the function call message, trigger the function method
|
469
475
|
if (isFunctionCall) {
|
476
|
+
get().internal_toggleMessageInToolsCalling(true, assistantId);
|
470
477
|
await refreshMessages();
|
471
478
|
await triggerToolCalls(assistantId, {
|
472
479
|
threadId: params?.threadId,
|
@@ -828,6 +835,9 @@ export const generateAIChat: StateCreator<
|
|
828
835
|
internal_toggleChatLoading: (loading, id, action) => {
|
829
836
|
return get().internal_toggleLoadingArrays('chatLoadingIds', loading, id, action);
|
830
837
|
},
|
838
|
+
internal_toggleMessageInToolsCalling: (loading, id) => {
|
839
|
+
return get().internal_toggleLoadingArrays('messageInToolsCallingIds', loading, id);
|
840
|
+
},
|
831
841
|
internal_toggleChatReasoning: (loading, id, action) => {
|
832
842
|
return get().internal_toggleLoadingArrays('reasoningLoadingIds', loading, id, action);
|
833
843
|
},
|
@@ -6,6 +6,7 @@ export interface ChatAIChatState {
|
|
6
6
|
chatLoadingIdsAbortController?: AbortController;
|
7
7
|
inputFiles: File[];
|
8
8
|
inputMessage: string;
|
9
|
+
messageInToolsCallingIds: string[];
|
9
10
|
/**
|
10
11
|
* is the message is in RAG flow
|
11
12
|
*/
|
@@ -26,6 +27,7 @@ export const initialAiChatState: ChatAIChatState = {
|
|
26
27
|
chatLoadingIds: [],
|
27
28
|
inputFiles: [],
|
28
29
|
inputMessage: '',
|
30
|
+
messageInToolsCallingIds: [],
|
29
31
|
messageRAGLoadingIds: [],
|
30
32
|
pluginApiLoadingIds: [],
|
31
33
|
reasoningLoadingIds: [],
|
@@ -177,6 +177,14 @@ const isToolCallStreaming = (id: string, index: number) => (s: ChatStoreState) =
|
|
177
177
|
return isLoading[index];
|
178
178
|
};
|
179
179
|
|
180
|
+
const isInToolsCalling = (id: string, index: number) => (s: ChatStoreState) => {
|
181
|
+
const isStreamingToolsCalling = isToolCallStreaming(id, index)(s);
|
182
|
+
|
183
|
+
const isInvokingPluginApi = s.messageInToolsCallingIds.includes(id);
|
184
|
+
|
185
|
+
return isStreamingToolsCalling || isInvokingPluginApi;
|
186
|
+
};
|
187
|
+
|
180
188
|
const isAIGenerating = (s: ChatStoreState) =>
|
181
189
|
s.chatLoadingIds.some((id) => mainDisplayChatIDs(s).includes(id));
|
182
190
|
|
@@ -223,6 +231,7 @@ export const chatSelectors = {
|
|
223
231
|
isCreatingMessage,
|
224
232
|
isCurrentChatLoaded,
|
225
233
|
isHasMessageLoading,
|
234
|
+
isInToolsCalling,
|
226
235
|
isMessageEditing,
|
227
236
|
isMessageGenerating,
|
228
237
|
isMessageInChatReasoning,
|
@@ -283,6 +283,8 @@ export const chatPlugin: StateCreator<
|
|
283
283
|
|
284
284
|
await Promise.all(messagePools);
|
285
285
|
|
286
|
+
await get().internal_toggleMessageInToolsCalling(false, assistantId);
|
287
|
+
|
286
288
|
// only default type tool calls should trigger AI message
|
287
289
|
if (!shouldCreateMessage) return;
|
288
290
|
|