@lobehub/chat 1.106.4 → 1.106.6

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 CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.106.6](https://github.com/lobehub/lobe-chat/compare/v1.106.5...v1.106.6)
6
+
7
+ <sup>Released on **2025-07-31**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix oidc oauth callback pages 404.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix oidc oauth callback pages 404, closes [#8620](https://github.com/lobehub/lobe-chat/issues/8620) ([d136b6e](https://github.com/lobehub/lobe-chat/commit/d136b6e))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 1.106.5](https://github.com/lobehub/lobe-chat/compare/v1.106.4...v1.106.5)
31
+
32
+ <sup>Released on **2025-07-30**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Improve mcp plugin calling and display.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **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))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 1.106.4](https://github.com/lobehub/lobe-chat/compare/v1.106.3...v1.106.4)
6
56
 
7
57
  <sup>Released on **2025-07-30**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Fix oidc oauth callback pages 404."
6
+ ]
7
+ },
8
+ "date": "2025-07-31",
9
+ "version": "1.106.6"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Improve mcp plugin calling and display."
15
+ ]
16
+ },
17
+ "date": "2025-07-30",
18
+ "version": "1.106.5"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "fixes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.106.4",
3
+ "version": "1.106.6",
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, toolCallId, icon, title }) => {
36
+ ({ messageId, index, apiName, icon, title }) => {
37
37
  const { styles } = useStyles();
38
38
 
39
- const isLoading = useChatStore((s) => {
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 language={'json'} style={{ maxHeight: 200, maxWidth: 800, overflow: 'scroll' }}>
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={4} horizontal>
95
- {isLoading ? <Loader /> : <PluginAvatar identifier={identifier} size={20} />}
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, Icon } from '@lobehub/ui';
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
- {Object.entries(displayArgs).map(([key, value]) => {
105
- return (
106
- <ObjectEntity
107
- editable={false}
108
- hasMinWidth={hasMinWidth}
109
- key={key}
110
- objectKey={key}
111
- shine={shine}
112
- value={value}
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
- return (
84
- <Flexbox gap={12} id={id} width={'100%'}>
85
- {showPluginRender ? (
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
- ) : isEditing ? (
98
- <KeyValueEditor
99
- initialValue={safeParseJson(requestArgs || '')}
100
- onCancel={handleCancel}
101
- onFinish={handleFinish}
102
- />
103
- ) : (
104
- <Arguments
105
- actions={
106
- <>
107
- <ActionIcon
108
- icon={Edit3Icon}
109
- onClick={() => {
110
- setIsEditing(true);
111
- }}
112
- size={'small'}
113
- title={t('edit', { ns: 'common' })}
114
- />
115
- <ActionIcon
116
- icon={PlayCircleIcon}
117
- onClick={async () => {
118
- await reInvokeToolMessage(id);
119
- }}
120
- size={'small'}
121
- title={t('run', { ns: 'common' })}
122
- />
123
- </>
124
- }
125
- arguments={requestArgs}
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 [showRender, setShowRender] = useState(true);
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={setShowRender}
53
+ setShowRender={setShowDetail}
37
54
  showPluginRender={showPluginRender}
38
- showRender={showRender}
55
+ showRender={showDetail}
39
56
  />
40
- <AnimatedCollapsed open={showRender}>
57
+ <AnimatedCollapsed open={showDetail}>
41
58
  <Render
42
59
  messageId={messageId}
43
60
  requestArgs={requestArgs}
@@ -79,6 +79,7 @@ export const AssistantMessage = memo<
79
79
  key={toolCall.id}
80
80
  messageId={id}
81
81
  payload={toolCall}
82
+ type={toolCall.type}
82
83
  />
83
84
  ))}
84
85
  </Flexbox>
@@ -77,7 +77,6 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile, dataSource, itemCo
77
77
  initialTopMostItemIndex={dataSource?.length - 1}
78
78
  isScrolling={setIsScrolling}
79
79
  itemContent={itemContent}
80
- overscan={overscan}
81
80
  ref={virtuosoRef}
82
81
  />
83
82
  <AutoScroll
@@ -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
 
@@ -1,12 +0,0 @@
1
- import { notFound } from 'next/navigation';
2
- import { PropsWithChildren } from 'react';
3
-
4
- import { oidcEnv } from '@/envs/oidc';
5
-
6
- const Layout = ({ children }: PropsWithChildren) => {
7
- if (!oidcEnv.ENABLE_OIDC) return notFound();
8
-
9
- return children;
10
- };
11
-
12
- export default Layout;