@lobehub/lobehub 2.0.0-next.89 → 2.0.0-next.90

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 (61) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/locales/ar/chat.json +4 -0
  4. package/locales/ar/models.json +3 -6
  5. package/locales/bg-BG/chat.json +4 -0
  6. package/locales/bg-BG/models.json +3 -6
  7. package/locales/de-DE/chat.json +4 -0
  8. package/locales/de-DE/models.json +3 -6
  9. package/locales/en-US/chat.json +4 -0
  10. package/locales/en-US/common.json +1 -0
  11. package/locales/en-US/models.json +3 -6
  12. package/locales/es-ES/chat.json +4 -0
  13. package/locales/es-ES/models.json +3 -6
  14. package/locales/fa-IR/chat.json +4 -0
  15. package/locales/fa-IR/models.json +3 -6
  16. package/locales/fr-FR/chat.json +4 -0
  17. package/locales/fr-FR/models.json +3 -6
  18. package/locales/it-IT/chat.json +4 -0
  19. package/locales/it-IT/models.json +3 -6
  20. package/locales/ja-JP/chat.json +4 -0
  21. package/locales/ja-JP/models.json +3 -6
  22. package/locales/ko-KR/chat.json +4 -0
  23. package/locales/ko-KR/models.json +3 -6
  24. package/locales/nl-NL/chat.json +4 -0
  25. package/locales/nl-NL/models.json +3 -6
  26. package/locales/pl-PL/chat.json +4 -0
  27. package/locales/pl-PL/models.json +3 -6
  28. package/locales/pt-BR/chat.json +4 -0
  29. package/locales/pt-BR/models.json +3 -6
  30. package/locales/ru-RU/chat.json +4 -0
  31. package/locales/ru-RU/models.json +3 -6
  32. package/locales/tr-TR/chat.json +4 -0
  33. package/locales/tr-TR/models.json +3 -6
  34. package/locales/vi-VN/chat.json +4 -0
  35. package/locales/vi-VN/models.json +3 -6
  36. package/locales/zh-CN/chat.json +4 -0
  37. package/locales/zh-CN/common.json +1 -0
  38. package/locales/zh-CN/models.json +3 -6
  39. package/locales/zh-TW/chat.json +4 -0
  40. package/locales/zh-TW/models.json +3 -6
  41. package/package.json +1 -1
  42. package/packages/model-bank/src/types/aiModel.ts +1 -0
  43. package/packages/types/src/message/common/metadata.ts +6 -0
  44. package/packages/utils/src/time.ts +4 -0
  45. package/src/app/(backend)/middleware/auth/index.ts +1 -2
  46. package/src/app/(backend)/webapi/chat/[provider]/route.ts +1 -1
  47. package/src/app/(backend)/webapi/models/[provider]/pull/route.ts +1 -1
  48. package/src/app/(backend)/webapi/models/[provider]/route.ts +1 -1
  49. package/src/app/(backend)/webapi/text-to-image/[provider]/route.ts +1 -1
  50. package/src/app/[variants]/(main)/settings/provider/features/ModelList/ModelItem.tsx +74 -97
  51. package/src/features/Conversation/Messages/Group/Tool/Inspector/BuiltinPluginTitle.tsx +22 -11
  52. package/src/features/Conversation/Messages/Group/Tool/Inspector/StatusIndicator.tsx +41 -0
  53. package/src/features/Conversation/Messages/Group/Tool/Inspector/ToolTitle.tsx +13 -3
  54. package/src/features/Conversation/Messages/Group/Tool/Inspector/index.tsx +28 -34
  55. package/src/features/Conversation/Messages/Group/Tool/Render/AbortResponse.tsx +37 -0
  56. package/src/features/Conversation/Messages/Group/Tool/Render/index.tsx +6 -0
  57. package/src/features/Conversation/Messages/Group/Tool/index.tsx +1 -0
  58. package/src/locales/default/chat.ts +1 -0
  59. package/src/locales/default/common.ts +1 -0
  60. package/src/server/routers/lambda/file.ts +0 -6
  61. package/src/store/chat/slices/message/actions/publicApi.ts +15 -0
@@ -18,6 +18,9 @@ export const useStyles = createStyles(({ css, token }) => ({
18
18
  text-overflow: ellipsis;
19
19
  `,
20
20
 
21
+ expand: css`
22
+ color: ${token.colorText};
23
+ `,
21
24
  shinyText: shinyTextStylish(token),
22
25
  }));
23
26
 
@@ -26,22 +29,30 @@ interface BuiltinPluginTitleProps {
26
29
  icon?: ReactNode;
27
30
  identifier: string;
28
31
  index: number;
32
+ isExpanded?: boolean;
29
33
  isLoading?: boolean;
30
34
  messageId: string;
31
35
  title: string;
32
36
  toolCallId: string;
33
37
  }
34
38
 
35
- const BuiltinPluginTitle = memo<BuiltinPluginTitleProps>(({ apiName, title, isLoading }) => {
36
- const { styles } = useStyles();
37
-
38
- return (
39
- <Flexbox align={'center'} className={isLoading ? styles.shinyText : ''} gap={4} horizontal>
40
- <div>{title}</div>
41
- <Icon icon={ChevronRight} />
42
- <span className={styles.apiName}>{apiName}</span>
43
- </Flexbox>
44
- );
45
- });
39
+ const BuiltinPluginTitle = memo<BuiltinPluginTitleProps>(
40
+ ({ apiName, title, isLoading, isExpanded }) => {
41
+ const { styles, cx } = useStyles();
42
+
43
+ return (
44
+ <Flexbox
45
+ align={'center'}
46
+ className={cx(isLoading && styles.shinyText, isExpanded && styles.expand)}
47
+ gap={4}
48
+ horizontal
49
+ >
50
+ <div>{title}</div>
51
+ <Icon icon={ChevronRight} />
52
+ <span className={styles.apiName}>{apiName}</span>
53
+ </Flexbox>
54
+ );
55
+ },
56
+ );
46
57
 
47
58
  export default BuiltinPluginTitle;
@@ -0,0 +1,41 @@
1
+ import { ToolIntervention } from '@lobechat/types';
2
+ import { Icon, Tooltip } from '@lobehub/ui';
3
+ import { useTheme } from 'antd-style';
4
+ import { Ban, Check, CircleStop, X } from 'lucide-react';
5
+ import { memo } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ interface StatusIndicatorProps {
10
+ intervention?: ToolIntervention;
11
+ result?: { content: string | null; error?: any; state?: any };
12
+ }
13
+
14
+ const StatusIndicator = memo<StatusIndicatorProps>(({ intervention, result }) => {
15
+ const { t } = useTranslation('chat');
16
+ const theme = useTheme();
17
+
18
+ const hasError = !!result?.error;
19
+ const isReject = intervention?.status === 'rejected';
20
+ const isAbort = intervention?.status === 'aborted';
21
+
22
+ return (
23
+ <Flexbox align={'center'} gap={4} horizontal style={{ fontSize: 12 }}>
24
+ {isAbort ? (
25
+ <Tooltip title={t('tool.intervention.toolAbort')}>
26
+ <Icon color={theme.colorTextTertiary} icon={CircleStop} />
27
+ </Tooltip>
28
+ ) : isReject ? (
29
+ <Tooltip title={t('tool.intervention.toolRejected')}>
30
+ <Icon color={theme.colorTextTertiary} icon={Ban} />
31
+ </Tooltip>
32
+ ) : hasError ? (
33
+ <Icon color={theme.colorError} icon={X} />
34
+ ) : (
35
+ <Icon color={theme.colorSuccess} icon={Check} />
36
+ )}
37
+ </Flexbox>
38
+ );
39
+ });
40
+
41
+ export default StatusIndicator;
@@ -26,6 +26,9 @@ export const useStyles = createStyles(({ css, token }) => ({
26
26
  text-overflow: ellipsis;
27
27
  `,
28
28
 
29
+ expand: css`
30
+ color: ${token.colorText};
31
+ `,
29
32
  shinyText: shinyTextStylish(token),
30
33
  }));
31
34
 
@@ -33,15 +36,16 @@ interface ToolTitleProps {
33
36
  apiName: string;
34
37
  identifier: string;
35
38
  index: number;
39
+ isExpanded?: boolean;
36
40
  isLoading?: boolean;
37
41
  messageId: string;
38
42
  toolCallId: string;
39
43
  }
40
44
 
41
45
  const ToolTitle = memo<ToolTitleProps>(
42
- ({ identifier, apiName, isLoading, index, toolCallId, messageId }) => {
46
+ ({ identifier, apiName, isLoading, index, toolCallId, messageId, isExpanded }) => {
43
47
  const { t } = useTranslation('plugin');
44
- const { styles } = useStyles();
48
+ const { styles, cx } = useStyles();
45
49
 
46
50
  const pluginMeta = useToolStore(toolSelectors.getMetaById(identifier), isEqual);
47
51
 
@@ -69,6 +73,7 @@ const ToolTitle = memo<ToolTitleProps>(
69
73
  {...builtinPluginTitle}
70
74
  identifier={identifier}
71
75
  index={index}
76
+ isExpanded={isExpanded}
72
77
  isLoading={isLoading}
73
78
  messageId={messageId}
74
79
  toolCallId={toolCallId}
@@ -79,7 +84,12 @@ const ToolTitle = memo<ToolTitleProps>(
79
84
  const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
80
85
 
81
86
  return (
82
- <Flexbox align={'center'} className={isLoading ? styles.shinyText : ''} gap={6} horizontal>
87
+ <Flexbox
88
+ align={'center'}
89
+ className={cx(isLoading && styles.shinyText, isExpanded && styles.expand)}
90
+ gap={6}
91
+ horizontal
92
+ >
83
93
  <div>{pluginTitle}</div> <Icon icon={ChevronRight} />
84
94
  <span className={styles.apiName}>{apiName}</span>
85
95
  </Flexbox>
@@ -1,26 +1,19 @@
1
1
  import { ToolIntervention } from '@lobechat/types';
2
- import { ActionIcon, Icon, Tooltip } from '@lobehub/ui';
2
+ import { ActionIcon } from '@lobehub/ui';
3
3
  import { createStyles } from 'antd-style';
4
- import {
5
- Ban,
6
- Check,
7
- LayoutPanelTop,
8
- LogsIcon,
9
- LucideBug,
10
- LucideBugOff,
11
- Trash2,
12
- X,
13
- } from 'lucide-react';
4
+ import { LayoutPanelTop, LogsIcon, LucideBug, LucideBugOff, Trash2 } from 'lucide-react';
14
5
  import { CSSProperties, memo, useEffect, useState } from 'react';
15
6
  import { useTranslation } from 'react-i18next';
16
7
  import { Flexbox } from 'react-layout-kit';
17
8
 
18
9
  import { LOADING_FLAT } from '@/const/message';
19
10
  import { useChatStore } from '@/store/chat';
11
+ import { dbMessageSelectors } from '@/store/chat/slices/message/selectors';
20
12
  import { shinyTextStylish } from '@/styles/loading';
21
13
 
22
14
  import Debug from './Debug';
23
15
  import Settings from './Settings';
16
+ import StatusIndicator from './StatusIndicator';
24
17
  import ToolTitle from './ToolTitle';
25
18
 
26
19
  export const useStyles = createStyles(({ css, token, cx }) => ({
@@ -86,6 +79,7 @@ interface InspectorProps {
86
79
  showPortal?: boolean;
87
80
  showRender: boolean;
88
81
  style?: CSSProperties;
82
+ toolMessageId?: string;
89
83
  type?: string;
90
84
  }
91
85
 
@@ -103,15 +97,18 @@ const Inspectors = memo<InspectorProps>(
103
97
  setShowPluginRender,
104
98
  type,
105
99
  intervention,
100
+ toolMessageId,
106
101
  }) => {
107
102
  const { t } = useTranslation('plugin');
108
- const { styles, theme } = useStyles();
103
+ const { styles } = useStyles();
109
104
 
110
105
  const [showDebug, setShowDebug] = useState(false);
111
- const [isPinned, setIsPinned] = useState(false);
112
106
  const [isHovered, setIsHovered] = useState(false);
113
107
 
114
- const [deleteAssistantMessage] = useChatStore((s) => [s.deleteAssistantMessage]);
108
+ const [deleteAssistantMessage, toggleInspectExpanded] = useChatStore((s) => [
109
+ s.deleteAssistantMessage,
110
+ s.toggleInspectExpanded,
111
+ ]);
115
112
 
116
113
  const hasError = !!result?.error;
117
114
  const hasSuccessResult = !!result?.content && result.content !== LOADING_FLAT;
@@ -119,18 +116,26 @@ const Inspectors = memo<InspectorProps>(
119
116
  const hasResult = hasSuccessResult || hasError;
120
117
 
121
118
  const isPending = intervention?.status === 'pending';
122
- const isReject = intervention?.status === 'rejected' || intervention?.status === 'aborted';
119
+ const isReject = intervention?.status === 'rejected';
120
+ const isAbort = intervention?.status === 'aborted';
123
121
  const isTitleLoading = !hasResult && !isPending;
124
122
 
125
- // Compute actual render state based on pinned or hovered
126
- const shouldShowRender = isPinned || isHovered;
123
+ const isPersistentExpanded = useChatStore((s) => {
124
+ if (!toolMessageId) return undefined;
125
+
126
+ const message = dbMessageSelectors.getDbMessageById(toolMessageId)(s);
127
+ return message?.metadata?.inspectExpanded;
128
+ });
129
+
130
+ // Compute actual render state based on persistent expanded or hovered
131
+ const shouldShowRender = isPersistentExpanded || isHovered;
127
132
 
128
133
  // Sync with parent state
129
134
  useEffect(() => {
130
135
  setShowRender(shouldShowRender);
131
136
  }, [shouldShowRender, setShowRender]);
132
137
 
133
- const showCustomPluginRender = shouldShowRender && !isPending && !isReject;
138
+ const showCustomPluginRender = shouldShowRender && !isPending && !isReject && !isAbort;
134
139
  return (
135
140
  <Flexbox className={styles.container} gap={4}>
136
141
  <Flexbox align={'center'} distribution={'space-between'} gap={8} horizontal>
@@ -140,15 +145,15 @@ const Inspectors = memo<InspectorProps>(
140
145
  gap={8}
141
146
  horizontal
142
147
  onClick={() => {
143
- setIsPinned(!isPinned);
148
+ if (toolMessageId) toggleInspectExpanded(toolMessageId);
144
149
  }}
145
150
  onMouseEnter={() => {
146
- if (!isPinned) {
151
+ if (!isPersistentExpanded) {
147
152
  setIsHovered(true);
148
153
  }
149
154
  }}
150
155
  onMouseLeave={() => {
151
- if (!isPinned) {
156
+ if (!isPersistentExpanded) {
152
157
  setIsHovered(false);
153
158
  }
154
159
  }}
@@ -158,6 +163,7 @@ const Inspectors = memo<InspectorProps>(
158
163
  apiName={apiName}
159
164
  identifier={identifier}
160
165
  index={index}
166
+ isExpanded={isPersistentExpanded}
161
167
  isLoading={isTitleLoading}
162
168
  messageId={assistantMessageId}
163
169
  toolCallId={id}
@@ -194,19 +200,7 @@ const Inspectors = memo<InspectorProps>(
194
200
 
195
201
  <Settings id={identifier} />
196
202
  </Flexbox>
197
- {hasResult && (
198
- <Flexbox align={'center'} gap={4} horizontal style={{ fontSize: 12 }}>
199
- {isReject ? (
200
- <Tooltip title={t('tool.intervention.toolRejected', { ns: 'chat' })}>
201
- <Icon color={theme.colorTextTertiary} icon={Ban} />
202
- </Tooltip>
203
- ) : hasError ? (
204
- <Icon color={theme.colorError} icon={X} />
205
- ) : (
206
- <Icon color={theme.colorSuccess} icon={Check} />
207
- )}
208
- </Flexbox>
209
- )}
203
+ {hasResult && <StatusIndicator intervention={intervention} result={result} />}
210
204
  </Flexbox>
211
205
  </Flexbox>
212
206
  {showDebug && (
@@ -0,0 +1,37 @@
1
+ import { Icon } from '@lobehub/ui';
2
+ import { createStyles } from 'antd-style';
3
+ import { BanIcon } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ const useStyles = createStyles(({ css, token }) => ({
9
+ container: css`
10
+ padding-block: 8px;
11
+ padding-inline: 6px;
12
+ `,
13
+ reason: css`
14
+ font-size: 12px;
15
+ color: ${token.colorTextTertiary};
16
+ `,
17
+ title: css`
18
+ font-size: 14px;
19
+ color: ${token.colorTextSecondary};
20
+ `,
21
+ }));
22
+
23
+ const AbortResponse = memo(() => {
24
+ const { t } = useTranslation('chat');
25
+ const { styles, theme } = useStyles();
26
+
27
+ return (
28
+ <Flexbox className={styles.container} gap={8}>
29
+ <Flexbox align={'center'} gap={8} horizontal>
30
+ <Icon color={theme.colorTextTertiary} icon={BanIcon} size={16} />
31
+ <div className={styles.title}>{t('tool.intervention.toolAbort')}</div>
32
+ </Flexbox>
33
+ </Flexbox>
34
+ );
35
+ });
36
+
37
+ export default AbortResponse;
@@ -3,6 +3,8 @@ import { ChatToolResult, ToolIntervention } from '@lobechat/types';
3
3
  import { Suspense, memo } from 'react';
4
4
  import { Flexbox } from 'react-layout-kit';
5
5
 
6
+ import AbortResponse from '@/features/Conversation/Messages/Group/Tool/Render/AbortResponse';
7
+
6
8
  import CustomRender from './CustomRender';
7
9
  import ErrorResponse from './ErrorResponse';
8
10
  import Intervention from './Intervention';
@@ -63,6 +65,10 @@ const Render = memo<RenderProps>(
63
65
  return <RejectedResponse reason={intervention.rejectedReason} />;
64
66
  }
65
67
 
68
+ if (intervention?.status === 'aborted') {
69
+ return <AbortResponse />;
70
+ }
71
+
66
72
  if (!result) return null;
67
73
 
68
74
  // Handle error state
@@ -70,6 +70,7 @@ const Tool = memo<GroupToolProps>(
70
70
  setShowRender={setShowToolDetail}
71
71
  showPluginRender={showCustomPluginUI}
72
72
  showRender={showToolContent}
73
+ toolMessageId={toolMessageId}
73
74
  type={type}
74
75
  />
75
76
  <AnimatedCollapsed open={showToolContent} width={{ collapsed: 'auto' }}>
@@ -430,6 +430,7 @@ export default {
430
430
  rejectReasonPlaceholder: '输入拒绝原因将帮助 Agent 理解并优化后续行动',
431
431
  rejectTitle: '拒绝本次工具调用',
432
432
  rejectedWithReason: '本次工具调用被主动拒绝:{{reason}}',
433
+ toolAbort: '本次工具调用被用户取消',
433
434
  toolRejected: '本次工具调用被主动拒绝',
434
435
  },
435
436
  },
@@ -286,6 +286,7 @@ export default {
286
286
  business: '商务合作',
287
287
  support: '邮件支持',
288
288
  },
289
+ new: '新',
289
290
  oauth: 'SSO 登录',
290
291
  officialSite: '官方网站',
291
292
  ok: '确定',
@@ -220,12 +220,6 @@ export const fileRouter = router({
220
220
  embeddingStatus: null,
221
221
  finishEmbedding: false,
222
222
  } as FileListItem;
223
- console.log('[API getKnowledgeItems] Processing document:', {
224
- editorDataPreview: item.editorData ? JSON.stringify(item.editorData).slice(0, 100) : null,
225
- hasEditorData: !!item.editorData,
226
- id: item.id,
227
- name: item.name,
228
- });
229
223
  resultItems.push(documentItem);
230
224
  }
231
225
  }
@@ -47,6 +47,10 @@ export interface MessagePublicApiAction {
47
47
  * Toggle message collapsed state
48
48
  */
49
49
  toggleMessageCollapsed: (id: string, collapsed?: boolean) => Promise<void>;
50
+ /**
51
+ * Toggle tool inspect expanded state
52
+ */
53
+ toggleInspectExpanded: (id: string, expanded?: boolean) => Promise<void>;
50
54
 
51
55
  // ===== Others ===== //
52
56
  copyMessage: (id: string, content: string) => Promise<void>;
@@ -258,4 +262,15 @@ export const messagePublicApi: StateCreator<
258
262
  collapsed: nextCollapsed,
259
263
  });
260
264
  },
265
+
266
+ toggleInspectExpanded: async (id, expanded) => {
267
+ const message = dbMessageSelectors.getDbMessageById(id)(get());
268
+ if (!message) return;
269
+
270
+ // 如果没有传入 expanded,则取反当前状态
271
+ const nextExpanded = expanded ?? !message.metadata?.inspectExpanded;
272
+
273
+ // 直接调用现有的 optimisticUpdateMessageMetadata
274
+ await get().optimisticUpdateMessageMetadata(id, { inspectExpanded: nextExpanded });
275
+ },
261
276
  });