@lobehub/lobehub 2.0.0-next.65 → 2.0.0-next.67

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 (70) hide show
  1. package/.github/workflows/claude-translator.yml +1 -0
  2. package/CHANGELOG.md +50 -0
  3. package/changelog/v1.json +18 -0
  4. package/locales/ar/chat.json +3 -0
  5. package/locales/bg-BG/chat.json +3 -0
  6. package/locales/de-DE/chat.json +3 -0
  7. package/locales/en-US/chat.json +3 -0
  8. package/locales/es-ES/chat.json +3 -0
  9. package/locales/fa-IR/chat.json +3 -0
  10. package/locales/fr-FR/chat.json +3 -0
  11. package/locales/it-IT/chat.json +3 -0
  12. package/locales/ja-JP/chat.json +3 -0
  13. package/locales/ko-KR/chat.json +3 -0
  14. package/locales/nl-NL/chat.json +3 -0
  15. package/locales/pl-PL/chat.json +3 -0
  16. package/locales/pt-BR/chat.json +3 -0
  17. package/locales/ru-RU/chat.json +3 -0
  18. package/locales/tr-TR/chat.json +3 -0
  19. package/locales/vi-VN/chat.json +3 -0
  20. package/locales/zh-CN/chat.json +3 -0
  21. package/locales/zh-TW/chat.json +3 -0
  22. package/package.json +7 -6
  23. package/packages/conversation-flow/src/__tests__/fixtures/index.ts +4 -8
  24. package/packages/conversation-flow/src/__tests__/fixtures/inputs/{assistant-with-tools.json → assistantGroup/assistant-with-tools.json} +2 -1
  25. package/packages/conversation-flow/src/__tests__/fixtures/inputs/assistantGroup/index.ts +8 -0
  26. package/packages/conversation-flow/src/__tests__/fixtures/inputs/index.ts +2 -4
  27. package/packages/conversation-flow/src/__tests__/fixtures/outputs/{assistant-with-tools.json → assistantGroup/assistant-with-tools.json} +8 -8
  28. package/packages/conversation-flow/src/__tests__/fixtures/outputs/assistantGroup/index.ts +8 -0
  29. package/packages/conversation-flow/src/__tests__/parse.test.ts +6 -6
  30. package/packages/conversation-flow/src/parse.ts +45 -1
  31. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +64 -0
  32. package/packages/database/package.json +2 -2
  33. package/packages/obervability-otel/package.json +1 -1
  34. package/packages/types/src/message/common/metadata.ts +8 -1
  35. package/packages/types/src/message/ui/chat.ts +1 -0
  36. package/src/app/(backend)/market/agent/[[...segments]]/route.ts +1 -1
  37. package/src/app/(backend)/market/oidc/[[...segments]]/route.ts +1 -1
  38. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/ChatItem/index.tsx +1 -0
  39. package/src/app/[variants]/(main)/chat/components/conversation/features/ChatMinimap/index.tsx +21 -28
  40. package/src/app/market-auth-callback/layout.tsx +27 -3
  41. package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +2 -2
  42. package/src/features/ChatItem/style.ts +4 -0
  43. package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +18 -4
  44. package/src/features/Conversation/Messages/Assistant/CollapsedMessage.tsx +37 -0
  45. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +16 -9
  46. package/src/features/Conversation/Messages/Assistant/index.tsx +329 -230
  47. package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +31 -9
  48. package/src/features/Conversation/Messages/Group/CollapsedMessage.tsx +37 -0
  49. package/src/features/Conversation/Messages/Group/{GroupChildren.tsx → Group.tsx} +18 -4
  50. package/src/features/Conversation/Messages/Group/GroupItem.tsx +3 -5
  51. package/src/features/Conversation/Messages/Group/index.tsx +84 -19
  52. package/src/features/Conversation/Messages/User/Actions/ActionsBar.tsx +3 -3
  53. package/src/features/Conversation/Messages/index.tsx +24 -8
  54. package/src/features/Conversation/components/VirtualizedList/VirtuosoContext.ts +13 -13
  55. package/src/features/Conversation/components/VirtualizedList/index.tsx +92 -58
  56. package/src/features/Conversation/components/WideScreenContainer/index.tsx +10 -6
  57. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +14 -0
  58. package/src/features/Conversation/hooks/useDoubleClickEdit.ts +3 -3
  59. package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +1 -1
  60. package/src/libs/mcp/__tests__/index.test.ts +6 -6
  61. package/src/locales/default/chat.ts +3 -0
  62. package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +9 -1
  63. package/src/store/chat/slices/message/actions/publicApi.ts +17 -0
  64. package/src/store/chat/slices/message/selectors/displayMessage.ts +1 -1
  65. package/src/store/chat/slices/message/selectors/messageState.ts +7 -0
  66. package/src/store/chat/slices/translate/action.test.ts +26 -32
  67. package/src/store/chat/slices/translate/action.ts +3 -3
  68. /package/packages/conversation-flow/src/__tests__/fixtures/inputs/{complex-scenario.json → assistantGroup/tools-with-branches.json} +0 -0
  69. /package/packages/conversation-flow/src/__tests__/fixtures/outputs/{complex-scenario.json → assistantGroup/tools-with-branches.json} +0 -0
  70. /package/src/features/Conversation/Messages/Group/{GroupContext.tsx → GroupContext.ts} +0 -0
@@ -3,7 +3,7 @@
3
3
  import { LOADING_FLAT } from '@lobechat/const';
4
4
  import { UIChatMessage } from '@lobechat/types';
5
5
  import { Tag } from '@lobehub/ui';
6
- import { css, cx, useResponsive } from 'antd-style';
6
+ import { createStyles, css, cx, useResponsive } from 'antd-style';
7
7
  import isEqual from 'fast-deep-equal';
8
8
  import { ReactNode, memo, useCallback, useMemo } from 'react';
9
9
  import { useTranslation } from 'react-i18next';
@@ -15,7 +15,6 @@ import BorderSpacing from '@/features/ChatItem/components/BorderSpacing';
15
15
  import ErrorContent from '@/features/ChatItem/components/ErrorContent';
16
16
  import MessageContent from '@/features/ChatItem/components/MessageContent';
17
17
  import Title from '@/features/ChatItem/components/Title';
18
- import { useStyles } from '@/features/ChatItem/style';
19
18
  import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
20
19
  import { useAgentStore } from '@/store/agent';
21
20
  import { agentChatConfigSelectors } from '@/store/agent/selectors';
@@ -44,6 +43,103 @@ const messageContainer = cx(css`
44
43
  background: none;
45
44
  `);
46
45
 
46
+ export const useStyles = createStyles(
47
+ (
48
+ { cx, css, token, responsive },
49
+ {
50
+ placement,
51
+ variant,
52
+ editing,
53
+ }: { editing?: boolean; placement?: 'left' | 'right'; variant?: 'bubble' | 'docs' },
54
+ ) => {
55
+ const rawContainerStylish = css`
56
+ margin-block-end: -16px;
57
+ transition: background-color 100ms ${token.motionEaseOut};
58
+ `;
59
+
60
+ const editingStylish =
61
+ editing &&
62
+ css`
63
+ width: 100%;
64
+ `;
65
+
66
+ return {
67
+ actions: cx(
68
+ css`
69
+ flex: none;
70
+ align-self: ${variant === 'bubble'
71
+ ? 'flex-end'
72
+ : placement === 'left'
73
+ ? 'flex-start'
74
+ : 'flex-end'};
75
+ justify-content: ${placement === 'left' ? 'flex-end' : 'flex-start'};
76
+ `,
77
+ editing &&
78
+ css`
79
+ pointer-events: none !important;
80
+ opacity: 0 !important;
81
+ `,
82
+ ),
83
+ container: cx(
84
+ variant === 'docs' && rawContainerStylish,
85
+ css`
86
+ position: relative;
87
+
88
+ width: 100%;
89
+ max-width: 100vw;
90
+ padding-block: 24px 12px;
91
+ padding-inline: 12px;
92
+
93
+ @supports (content-visibility: auto) {
94
+ contain-intrinsic-size: auto 100lvh;
95
+ }
96
+
97
+ time {
98
+ display: inline-block;
99
+ white-space: nowrap;
100
+ }
101
+
102
+ div[role='menubar'] {
103
+ display: flex;
104
+ }
105
+
106
+ time,
107
+ div[role='menubar'] {
108
+ pointer-events: none;
109
+ opacity: 0;
110
+ transition: opacity 200ms ${token.motionEaseOut};
111
+ }
112
+
113
+ &:hover {
114
+ time,
115
+ div[role='menubar'] {
116
+ pointer-events: unset;
117
+ opacity: 1;
118
+ }
119
+ }
120
+
121
+ ${responsive.mobile} {
122
+ padding-block-start: ${variant === 'docs' ? '16px' : '12px'};
123
+ padding-inline: 8px;
124
+ }
125
+ `,
126
+ ),
127
+ messageContent: cx(
128
+ editingStylish,
129
+ css`
130
+ position: relative;
131
+ overflow: hidden;
132
+ max-width: 100%;
133
+
134
+ ${responsive.mobile} {
135
+ flex-direction: column !important;
136
+ }
137
+ `,
138
+ ),
139
+ };
140
+ },
141
+ );
142
+
47
143
  const isHtmlCode = (content: string, language: string) => {
48
144
  return (
49
145
  language === 'html' ||
@@ -57,244 +153,247 @@ interface AssistantMessageProps {
57
153
  disableEditing?: boolean;
58
154
  id: string;
59
155
  index: number;
156
+ isLatestItem?: boolean;
60
157
  }
61
158
 
62
- const AssistantMessage = memo<AssistantMessageProps>(({ id, index, disableEditing }) => {
63
- const item = useChatStore(
64
- displayMessageSelectors.getDisplayMessageById(id),
65
- isEqual,
66
- ) as UIChatMessage;
67
-
68
- const {
69
- error,
70
- role,
71
- search,
72
- content,
73
- createdAt,
74
- tools,
75
- extra,
76
- model,
77
- provider,
78
- meta,
79
- targetId,
80
- groupId,
81
- performance,
82
- usage,
83
- metadata,
84
- } = item;
85
-
86
- const showTitle = !!groupId;
87
-
88
- const avatar = meta;
89
- const { t } = useTranslation('chat');
90
- const { mobile } = useResponsive();
91
- const placement = 'left';
92
- const type = useAgentStore(agentChatConfigSelectors.displayMode);
93
- const variant = type === 'chat' ? 'bubble' : 'docs';
94
-
95
- const { transitionMode, highlighterTheme, mermaidTheme } = useUserStore(
96
- userGeneralSettingsSelectors.config,
97
- );
98
-
99
- const [generating, isInRAGFlow, editing] = useChatStore((s) => [
100
- messageStateSelectors.isMessageGenerating(id)(s),
101
- messageStateSelectors.isMessageInRAGFlow(id)(s),
102
- messageStateSelectors.isMessageEditing(id)(s),
103
- ]);
104
-
105
- const { styles } = useStyles({
106
- editing,
107
- placement,
108
- primary: false,
109
- showTitle,
110
- time: createdAt,
111
- title: avatar.title,
112
- variant,
113
- });
114
- const errorContent = useErrorContent(error);
115
-
116
- // remove line breaks in artifact tag to make the ast transform easier
117
- const message = !editing ? normalizeThinkTags(processWithArtifact(content)) : content;
118
-
119
- // when the message is in RAG flow or the AI generating, it should be in loading state
120
- const loading = isInRAGFlow || generating;
121
-
122
- const animated = transitionMode === 'fadeIn' && generating;
123
-
124
- const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
125
- const currentSession = useSessionStore(sessionSelectors.currentSession);
126
- const sessionId = isGroupSession && currentSession ? currentSession.id : '';
127
- const groupConfig = useChatGroupStore(chatGroupSelectors.getGroupConfig(sessionId || ''));
128
-
129
- const reducted =
130
- isGroupSession && targetId !== null && targetId !== 'user' && !groupConfig?.revealDM;
131
-
132
- // Get target name for DM indicator
133
- const userName = useUserStore(userProfileSelectors.nickName) || 'User';
134
- const agents = useSessionStore(sessionSelectors.currentGroupAgents);
135
-
136
- const dmIndicator = useMemo(() => {
137
- if (!targetId) return undefined;
138
-
139
- let targetName = targetId;
140
- if (targetId === 'user') {
141
- targetName = t('dm.you');
142
- } else {
143
- const targetAgent = agents?.find((agent) => agent.id === targetId);
144
- targetName = targetAgent?.title || targetId;
145
- }
146
-
147
- return <Tag>{t('dm.visibleTo', { target: targetName })}</Tag>;
148
- }, [targetId, userName, agents, t]);
149
-
150
- // ======================= Performance Optimization ======================= //
151
- // these useMemo/useCallback are all for the performance optimization
152
- // maybe we can remove it in React 19
153
- // ======================================================================== //
154
-
155
- const components = useMemo(
156
- () =>
157
- Object.fromEntries(
158
- markdownElements.map((element) => {
159
- const Component = element.Component;
160
-
161
- return [element.tag, (props: any) => <Component {...props} id={id} />];
162
- }),
163
- ),
164
- [id],
165
- );
166
-
167
- const markdownProps = useMemo(
168
- () => ({
169
- animated,
170
- citations: search?.citations,
171
- componentProps: {
172
- highlight: {
173
- actionsRender: ({ content, actionIconSize, language, originalNode }: any) => {
174
- const showHtmlPreview = isHtmlCode(content, language);
175
-
176
- return (
177
- <>
178
- {showHtmlPreview && <HtmlPreviewAction content={content} size={actionIconSize} />}
179
- {originalNode}
180
- </>
181
- );
159
+ const AssistantMessage = memo<AssistantMessageProps>(
160
+ ({ id, index, disableEditing, isLatestItem }) => {
161
+ const item = useChatStore(
162
+ displayMessageSelectors.getDisplayMessageById(id),
163
+ isEqual,
164
+ ) as UIChatMessage;
165
+
166
+ const {
167
+ error,
168
+ role,
169
+ search,
170
+ content,
171
+ createdAt,
172
+ tools,
173
+ extra,
174
+ model,
175
+ provider,
176
+ meta,
177
+ targetId,
178
+ performance,
179
+ usage,
180
+ metadata,
181
+ } = item;
182
+
183
+ const avatar = meta;
184
+ const { t } = useTranslation('chat');
185
+ const { mobile } = useResponsive();
186
+ const placement = 'left';
187
+ const type = useAgentStore(agentChatConfigSelectors.displayMode);
188
+ const variant = type === 'chat' ? 'bubble' : 'docs';
189
+
190
+ const { transitionMode, highlighterTheme, mermaidTheme } = useUserStore(
191
+ userGeneralSettingsSelectors.config,
192
+ );
193
+
194
+ const [generating, isInRAGFlow, editing] = useChatStore((s) => [
195
+ messageStateSelectors.isMessageGenerating(id)(s),
196
+ messageStateSelectors.isMessageInRAGFlow(id)(s),
197
+ messageStateSelectors.isMessageEditing(id)(s),
198
+ ]);
199
+
200
+ const { styles } = useStyles({
201
+ editing,
202
+ placement,
203
+ variant,
204
+ });
205
+ const errorContent = useErrorContent(error);
206
+
207
+ // remove line breaks in artifact tag to make the ast transform easier
208
+ const message = !editing ? normalizeThinkTags(processWithArtifact(content)) : content;
209
+
210
+ // when the message is in RAG flow or the AI generating, it should be in loading state
211
+ const loading = isInRAGFlow || generating;
212
+
213
+ const animated = transitionMode === 'fadeIn' && generating;
214
+
215
+ const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
216
+ const currentSession = useSessionStore(sessionSelectors.currentSession);
217
+ const sessionId = isGroupSession && currentSession ? currentSession.id : '';
218
+ const groupConfig = useChatGroupStore(chatGroupSelectors.getGroupConfig(sessionId || ''));
219
+
220
+ const reducted =
221
+ isGroupSession && targetId !== null && targetId !== 'user' && !groupConfig?.revealDM;
222
+
223
+ // Get target name for DM indicator
224
+ const userName = useUserStore(userProfileSelectors.nickName) || 'User';
225
+ const agents = useSessionStore(sessionSelectors.currentGroupAgents);
226
+
227
+ const dmIndicator = useMemo(() => {
228
+ if (!targetId) return undefined;
229
+
230
+ let targetName = targetId;
231
+ if (targetId === 'user') {
232
+ targetName = t('dm.you');
233
+ } else {
234
+ const targetAgent = agents?.find((agent) => agent.id === targetId);
235
+ targetName = targetAgent?.title || targetId;
236
+ }
237
+
238
+ return <Tag>{t('dm.visibleTo', { target: targetName })}</Tag>;
239
+ }, [targetId, userName, agents, t]);
240
+
241
+ // ======================= Performance Optimization ======================= //
242
+ // these useMemo/useCallback are all for the performance optimization
243
+ // maybe we can remove it in React 19
244
+ // ======================================================================== //
245
+
246
+ const components = useMemo(
247
+ () =>
248
+ Object.fromEntries(
249
+ markdownElements.map((element) => {
250
+ const Component = element.Component;
251
+
252
+ return [element.tag, (props: any) => <Component {...props} id={id} />];
253
+ }),
254
+ ),
255
+ [id],
256
+ );
257
+
258
+ const markdownProps = useMemo(
259
+ () => ({
260
+ animated,
261
+ citations: search?.citations,
262
+ componentProps: {
263
+ highlight: {
264
+ actionsRender: ({ content, actionIconSize, language, originalNode }: any) => {
265
+ const showHtmlPreview = isHtmlCode(content, language);
266
+
267
+ return (
268
+ <>
269
+ {showHtmlPreview && <HtmlPreviewAction content={content} size={actionIconSize} />}
270
+ {originalNode}
271
+ </>
272
+ );
273
+ },
274
+ theme: highlighterTheme,
182
275
  },
183
- theme: highlighterTheme,
276
+ mermaid: { theme: mermaidTheme },
184
277
  },
185
- mermaid: { theme: mermaidTheme },
186
- },
187
- components,
188
- enableCustomFootnotes: true,
189
- rehypePlugins,
190
- remarkPlugins,
191
- showFootnotes:
192
- search?.citations &&
193
- // if the citations are all empty, we should not show the citations
194
- search?.citations.length > 0 &&
195
- // if the citations's url and title are all the same, we should not show the citations
196
- search?.citations.every((item) => item.title !== item.url),
197
- }),
198
- [animated, components, role, search, highlighterTheme, mermaidTheme],
199
- );
200
-
201
- const [isInbox] = useSessionStore((s) => [sessionSelectors.isInboxSession(s)]);
202
- const [toggleSystemRole] = useGlobalStore((s) => [s.toggleSystemRole]);
203
- const openChatSettings = useOpenChatSettings();
204
-
205
- const onAvatarClick = useCallback(() => {
206
- if (!isInbox) {
207
- toggleSystemRole(true);
208
- } else {
209
- openChatSettings();
210
- }
211
- }, [isInbox]);
212
-
213
- const onDoubleClick = useDoubleClickEdit({ disableEditing, error, id, index, role });
214
-
215
- const renderMessage = useCallback(
216
- (editableContent: ReactNode) => (
217
- <AssistantMessageContent {...item} editableContent={editableContent} />
218
- ),
219
- [item],
220
- );
221
- const errorMessage = <ErrorMessageExtra data={item} />;
278
+ components,
279
+ enableCustomFootnotes: true,
280
+ rehypePlugins,
281
+ remarkPlugins,
282
+ showFootnotes:
283
+ search?.citations &&
284
+ // if the citations are all empty, we should not show the citations
285
+ search?.citations.length > 0 &&
286
+ // if the citations's url and title are all the same, we should not show the citations
287
+ search?.citations.every((item) => item.title !== item.url),
288
+ }),
289
+ [animated, components, role, search, highlighterTheme, mermaidTheme],
290
+ );
291
+
292
+ const [isInbox] = useSessionStore((s) => [sessionSelectors.isInboxSession(s)]);
293
+ const [toggleSystemRole] = useGlobalStore((s) => [s.toggleSystemRole]);
294
+ const openChatSettings = useOpenChatSettings();
295
+
296
+ const onAvatarClick = useCallback(() => {
297
+ if (!isInbox) {
298
+ toggleSystemRole(true);
299
+ } else {
300
+ openChatSettings();
301
+ }
302
+ }, [isInbox]);
303
+
304
+ const onDoubleClick = useDoubleClickEdit({ disableEditing, error, id, index, role });
305
+
306
+ const renderMessage = useCallback(
307
+ (editableContent: ReactNode) => (
308
+ <AssistantMessageContent {...item} editableContent={editableContent} />
309
+ ),
310
+ [item],
311
+ );
312
+ const errorMessage = <ErrorMessageExtra data={item} />;
222
313
 
223
- return (
224
- <Flexbox className={styles.container} gap={mobile ? 6 : 12}>
225
- <Flexbox gap={4} horizontal>
226
- <Avatar
227
- alt={avatar.title || 'avatar'}
228
- avatar={avatar}
229
- loading={loading}
230
- onClick={onAvatarClick}
231
- placement={placement}
232
- size={MOBILE_AVATAR_SIZE}
233
- />
234
- <Title
235
- avatar={avatar}
236
- placement={placement}
237
- showTitle
238
- style={{ marginBlockEnd: 0 }}
239
- time={createdAt}
240
- titleAddon={dmIndicator}
241
- />
242
- </Flexbox>
314
+ return (
243
315
  <Flexbox
244
- align={'flex-start'}
245
- className={styles.messageContent}
246
- data-layout={'vertical'} // 添加数据属性以方便样式选择
247
- direction={'vertical'}
248
- gap={8}
249
- width={'fit-content'}
316
+ className={styles.container}
317
+ gap={mobile ? 6 : 12}
318
+ style={isLatestItem ? { minHeight: 'calc(-284px + 100dvh)' } : undefined}
250
319
  >
251
- <Flexbox style={{ flex: 1, maxWidth: '100%' }}>
252
- {error && (message === LOADING_FLAT || !message) ? (
253
- <ErrorContent error={errorContent} message={errorMessage} placement={placement} />
254
- ) : (
255
- <MessageContent
256
- className={messageContainer}
257
- editing={editing}
258
- id={id}
259
- markdownProps={markdownProps}
260
- message={reducted ? `*${t('hideForYou')}*` : message}
261
- messageExtra={
262
- <>
263
- {errorContent && (
264
- <ErrorContent
265
- error={errorContent}
266
- message={errorMessage}
267
- placement={placement}
320
+ <Flexbox gap={4} horizontal>
321
+ <Avatar
322
+ alt={avatar.title || 'avatar'}
323
+ avatar={avatar}
324
+ loading={loading}
325
+ onClick={onAvatarClick}
326
+ placement={placement}
327
+ size={MOBILE_AVATAR_SIZE}
328
+ />
329
+ <Title
330
+ avatar={avatar}
331
+ placement={placement}
332
+ showTitle
333
+ style={{ marginBlockEnd: 0 }}
334
+ time={createdAt}
335
+ titleAddon={dmIndicator}
336
+ />
337
+ </Flexbox>
338
+ <Flexbox
339
+ align={'flex-start'}
340
+ className={styles.messageContent}
341
+ data-layout={'vertical'} // 添加数据属性以方便样式选择
342
+ direction={'vertical'}
343
+ gap={8}
344
+ width={'fit-content'}
345
+ >
346
+ <Flexbox style={{ flex: 1, maxWidth: '100%' }}>
347
+ {error && (message === LOADING_FLAT || !message) ? (
348
+ <ErrorContent error={errorContent} message={errorMessage} placement={placement} />
349
+ ) : (
350
+ <MessageContent
351
+ className={messageContainer}
352
+ editing={editing}
353
+ id={id}
354
+ markdownProps={markdownProps}
355
+ message={reducted ? `*${t('hideForYou')}*` : message}
356
+ messageExtra={
357
+ <>
358
+ {errorContent && (
359
+ <ErrorContent
360
+ error={errorContent}
361
+ message={errorMessage}
362
+ placement={placement}
363
+ />
364
+ )}
365
+ <AssistantMessageExtra
366
+ content={content}
367
+ extra={extra}
368
+ id={id}
369
+ model={model!}
370
+ performance={performance! || metadata}
371
+ provider={provider!}
372
+ tools={tools}
373
+ usage={usage! || metadata}
268
374
  />
269
- )}
270
- <AssistantMessageExtra
271
- content={content}
272
- extra={extra}
273
- id={id}
274
- model={model!}
275
- performance={performance! || metadata}
276
- provider={provider!}
277
- tools={tools}
278
- usage={usage! || metadata}
279
- />
280
- </>
281
- }
282
- onDoubleClick={onDoubleClick}
283
- placement={placement}
284
- renderMessage={renderMessage}
285
- variant={variant}
286
- />
375
+ </>
376
+ }
377
+ onDoubleClick={onDoubleClick}
378
+ placement={placement}
379
+ renderMessage={renderMessage}
380
+ variant={variant}
381
+ />
382
+ )}
383
+ </Flexbox>
384
+ {!disableEditing && !editing && (
385
+ <Flexbox align={'flex-start'} className={styles.actions} role="menubar">
386
+ <AssistantActionsBar data={item} id={id} index={index} />
387
+ </Flexbox>
287
388
  )}
288
389
  </Flexbox>
289
- {!disableEditing && !editing && (
290
- <Flexbox align={'flex-start'} className={styles.actions} role="menubar">
291
- <AssistantActionsBar data={item} id={id} index={index} />
292
- </Flexbox>
293
- )}
390
+ {mobile && <BorderSpacing borderSpacing={MOBILE_AVATAR_SIZE} />}
294
391
  </Flexbox>
295
- {mobile && <BorderSpacing borderSpacing={MOBILE_AVATAR_SIZE} />}
296
- </Flexbox>
297
- );
298
- }, isEqual);
392
+ );
393
+ },
394
+ isEqual,
395
+ );
396
+
397
+ AssistantMessage.displayName = 'AssistantMessage';
299
398
 
300
399
  export default AssistantMessage;