@lobehub/chat 1.7.6 → 1.7.8

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.7.8](https://github.com/lobehub/lobe-chat/compare/v1.7.7...v1.7.8)
6
+
7
+ <sup>Released on **2024-07-30**</sup>
8
+
9
+ #### 💄 Styles
10
+
11
+ - **ui**: Modify and repair UI layout.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Styles
19
+
20
+ - **ui**: Modify and repair UI layout, closes [#3321](https://github.com/lobehub/lobe-chat/issues/3321) ([cda776f](https://github.com/lobehub/lobe-chat/commit/cda776f))
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.7.7](https://github.com/lobehub/lobe-chat/compare/v1.7.6...v1.7.7)
31
+
32
+ <sup>Released on **2024-07-30**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Improve tools calling UI.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Improve tools calling UI, closes [#3326](https://github.com/lobehub/lobe-chat/issues/3326) ([36cabc0](https://github.com/lobehub/lobe-chat/commit/36cabc0))
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.7.6](https://github.com/lobehub/lobe-chat/compare/v1.7.5...v1.7.6)
6
56
 
7
57
  <sup>Released on **2024-07-29**</sup>
package/README.md CHANGED
@@ -268,12 +268,12 @@ Our marketplace is not just a showcase platform but also a collaborative space.
268
268
 
269
269
  | Recent Submits | Description |
270
270
  | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
271
+ | [Code Snark Master](https://chat-preview.lobehub.com/market?agent=code-snark-master)<br/><sup>By **[leter](https://github.com/leter)** on **2024-07-29**</sup> | Specializes in sharp criticism of code, sarcastically pointing out inefficiencies and readability issues<br/>`tech-leadership` `code-review` `sarcastic-style` `programming-consultation` |
272
+ | [Unity Maestro](https://chat-preview.lobehub.com/market?agent=unity-maestro)<br/><sup>By **[thedivergentai](https://github.com/thedivergentai)** on **2024-07-29**</sup> | Expert Unity Game Development Companion<br/>`game-development` `unity` `software-engineering` |
271
273
  | [C Program Learning Assistant](https://chat-preview.lobehub.com/market?agent=sichuan-university-941-c-programming-assistant)<br/><sup>By **[YBGuoYang](https://github.com/YBGuoYang)** on **2024-07-28**</sup> | Assist me in learning C program design<br/>`941` |
272
274
  | [Brand Pioneer](https://chat-preview.lobehub.com/market?agent=brand-pioneer)<br/><sup>By **[SaintFresh](https://github.com/SaintFresh)** on **2024-07-25**</sup> | A brand development specialist, thought leader, brand strategy super-genius, and brand visionary. Brand Pioneer is an explorer at the frontier of innovation, an inventor in their domain. Provide them with your market and let them imagine a future world characterized by groundbreaking advancements in your field of expertise.<br/>`business` `brand-pioneer` `brand-development` `business-assistant` `brand-narrative` |
273
- | [BIDOSx2](https://chat-preview.lobehub.com/market?agent=bidosx-2-v-2)<br/><sup>By **[SaintFresh](https://github.com/SaintFresh)** on **2024-07-21**</sup> | A highly advanced AI LLM transcending conventional AI. 'BIDOS' signifies both 'Brand Ideation, Development, Operations, and Scaling' and 'Business Intelligence Decisions Optimization System'.<br/>`brand-development` `ai-assistant` `market-analysis` `strategic-planning` `business-optimization` `business-intelligence` |
274
- | [Growth Coach](https://chat-preview.lobehub.com/market?agent=personal-development-coach)<br/><sup>By **[zer0boss](https://github.com/zer0boss)** on **2024-07-20**</sup> | Specializes in helping users explore themselves through dialogue, find solutions, and paths to growth<br/>`growth-coach` `self-exploration` `goal-setting` `self-awareness` |
275
275
 
276
- > 📊 Total agents: [<kbd>**306**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
276
+ > 📊 Total agents: [<kbd>**309**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
277
277
 
278
278
  <!-- AGENT LIST -->
279
279
 
package/README.zh-CN.md CHANGED
@@ -256,12 +256,12 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
256
256
 
257
257
  | 最近新增 | 助手说明 |
258
258
  | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
259
+ | [代码毒舌大师](https://chat-preview.lobehub.com/market?agent=code-snark-master)<br/><sup>By **[leter](https://github.com/leter)** on **2024-07-29**</sup> | 擅长尖刻批评代码,讽刺性地指出低效和可读性问题<br/>`技术领导` `代码审查` `讽刺风格` `编程咨询` |
260
+ | [Unity Maestro](https://chat-preview.lobehub.com/market?agent=unity-maestro)<br/><sup>By **[thedivergentai](https://github.com/thedivergentai)** on **2024-07-29**</sup> | Expert Unity Game Development Companion<br/>`game-development` `unity` `software-engineering` |
259
261
  | [c 程序学习助手](https://chat-preview.lobehub.com/market?agent=sichuan-university-941-c-programming-assistant)<br/><sup>By **[YBGuoYang](https://github.com/YBGuoYang)** on **2024-07-28**</sup> | 辅助我进行 c 程序设计的学习<br/>`941` |
260
262
  | [品牌先锋](https://chat-preview.lobehub.com/market?agent=brand-pioneer)<br/><sup>By **[SaintFresh](https://github.com/SaintFresh)** on **2024-07-25**</sup> | 一位品牌发展专家、思想领袖、品牌战略超级天才和品牌远见者。品牌先锋是创新前沿的探险家,在其领域是一位发明家。将您的市场提供给他们,让他们想象一个未来世界,其中以您的专业领域的突破性进展为特征。<br/>`商业` `品牌先锋` `品牌发展` `商业助手` `品牌叙事` |
261
- | [BIDOSx2](https://chat-preview.lobehub.com/market?agent=bidosx-2-v-2)<br/><sup>By **[SaintFresh](https://github.com/SaintFresh)** on **2024-07-21**</sup> | 一种高度先进的 AI LLM,超越传统人工智能。'BIDOS' 既代表 ' 品牌构思、发展、运营和扩展 ',也代表 ' 商业智能决策优化系统 '。<br/>`品牌发展` `ai助手` `市场分析` `战略规划` `业务优化` `商业智能` |
262
- | [成长教练](https://chat-preview.lobehub.com/market?agent=personal-development-coach)<br/><sup>By **[zer0boss](https://github.com/zer0boss)** on **2024-07-20**</sup> | 擅长用对话的方式帮助用户自我探索,找到解决之道和成长之路<br/>`成长教练` `自我探索` `目标设定` `自我觉察` |
263
263
 
264
- > 📊 Total agents: [<kbd>**306**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
264
+ > 📊 Total agents: [<kbd>**309**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
265
265
 
266
266
  <!-- AGENT LIST -->
267
267
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.7.6",
3
+ "version": "1.7.8",
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",
@@ -110,7 +110,7 @@
110
110
  "@clerk/localizations": "2.0.0",
111
111
  "@clerk/nextjs": "^5.2.6",
112
112
  "@clerk/themes": "^2.1.10",
113
- "@google/generative-ai": "^0.14.1",
113
+ "@google/generative-ai": "^0.16.0",
114
114
  "@icons-pack/react-simple-icons": "^9.6.0",
115
115
  "@khmyznikov/pwa-install": "^0.3.9",
116
116
  "@lobehub/chat-plugin-sdk": "^1.32.4",
@@ -14,6 +14,8 @@ import { memo, useMemo } from 'react';
14
14
  import { useTranslation } from 'react-i18next';
15
15
  import { Flexbox } from 'react-layout-kit';
16
16
 
17
+ import BubblesLoading from '@/components/BubblesLoading';
18
+ import { LOADING_FLAT } from '@/const/message';
17
19
  import { useIsMobile } from '@/hooks/useIsMobile';
18
20
  import { useChatStore } from '@/store/chat';
19
21
 
@@ -160,13 +162,19 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav, showMore }) => {
160
162
  spin={isLoading}
161
163
  />
162
164
  {!editing ? (
163
- <Paragraph
164
- className={styles.title}
165
- ellipsis={{ rows: 1, tooltip: { placement: 'left', title } }}
166
- style={{ margin: 0 }}
167
- >
168
- {title}
169
- </Paragraph>
165
+ title === LOADING_FLAT ? (
166
+ <Flexbox flex={1} height={28} justify={'center'}>
167
+ <BubblesLoading />
168
+ </Flexbox>
169
+ ) : (
170
+ <Paragraph
171
+ className={styles.title}
172
+ ellipsis={{ rows: 1, tooltip: { placement: 'left', title } }}
173
+ style={{ margin: 0 }}
174
+ >
175
+ {title}
176
+ </Paragraph>
177
+ )
170
178
  ) : (
171
179
  <EditableText
172
180
  editing={editing}
@@ -43,7 +43,16 @@ const ChatHeaderTitle = memo(() => {
43
43
  </Flexbox>
44
44
  }
45
45
  title={
46
- <div onClick={() => toggleConfig()}>
46
+ <div
47
+ onClick={() => toggleConfig()}
48
+ style={{
49
+ marginRight: '8px',
50
+ maxWidth: '64vw',
51
+ overflow: 'hidden',
52
+ textOverflow: 'ellipsis',
53
+ whiteSpace: 'nowrap',
54
+ }}
55
+ >
47
56
  {displayTitle}
48
57
  {topicLength > 1 ? `(${topicLength + 1})` : ''}
49
58
  </div>
@@ -5,6 +5,7 @@ import { MoreVertical, PencilLine, Plus, Settings2, Trash } from 'lucide-react';
5
5
  import { memo, useMemo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
+ import { useIsMobile } from '@/hooks/useIsMobile';
8
9
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
9
10
  import { useSessionStore } from '@/store/session';
10
11
 
@@ -30,6 +31,8 @@ const Actions = memo<ActionsProps>(
30
31
  const { styles } = useStyles();
31
32
  const { modal, message } = App.useApp();
32
33
 
34
+ const isMobile = useIsMobile();
35
+
33
36
  const [createSession, removeSessionGroup] = useSessionStore((s) => [
34
37
  s.createSession,
35
38
  s.removeSessionGroup,
@@ -123,12 +126,13 @@ const Actions = memo<ActionsProps>(
123
126
  trigger={['click']}
124
127
  >
125
128
  <ActionIcon
129
+ active={isMobile ? true : false}
126
130
  icon={MoreVertical}
127
131
  onClick={(e) => {
128
132
  e.stopPropagation();
129
133
  }}
130
134
  size={{ blockSize: 22, fontSize: 16 }}
131
- style={{ marginRight: -8 }}
135
+ style={{ background: isMobile ? 'transparent' : '', marginRight: -8 }}
132
136
  />
133
137
  </Dropdown>
134
138
  );
@@ -9,7 +9,7 @@ import { Flexbox } from 'react-layout-kit';
9
9
  import ModelIcon from '@/components/ModelIcon';
10
10
  import { ModelInfoTags } from '@/components/ModelSelect';
11
11
  import { useUserStore } from '@/store/user';
12
- import { modelConfigSelectors } from '@/store/user/selectors';
12
+ import { modelConfigSelectors, modelProviderSelectors } from '@/store/user/selectors';
13
13
  import { GlobalLLMProviderKey } from '@/types/user/settings';
14
14
 
15
15
  interface CustomModelOptionProps {
@@ -28,30 +28,34 @@ const CustomModelOption = memo<CustomModelOptionProps>(({ id, provider }) => {
28
28
  s.toggleEditingCustomModelCard,
29
29
  s.removeEnabledModels,
30
30
  ]);
31
+
31
32
  const modelCard = useUserStore(
32
33
  modelConfigSelectors.getCustomModelCard({ id, provider }),
33
34
  isEqual,
34
35
  );
35
36
 
37
+ const isEnabled = useUserStore(
38
+ (s) => modelProviderSelectors.getEnableModelsById(provider)(s)?.includes(id),
39
+ isEqual,
40
+ );
41
+
36
42
  return (
37
43
  <Flexbox align={'center'} distribution={'space-between'} gap={8} horizontal>
38
- <Flexbox align={'center'} gap={8} horizontal>
44
+ <Flexbox align={'center'} gap={8} horizontal style={{ flex: 1, width: '70%' }}>
39
45
  <ModelIcon model={id} size={32} />
40
- <Flexbox>
46
+ <Flexbox direction={'vertical'} style={{ flex: 1, overflow: 'hidden' }}>
41
47
  <Flexbox align={'center'} gap={8} horizontal>
42
- {modelCard?.displayName || id}
48
+ <Typography.Text ellipsis>{modelCard?.displayName || id}</Typography.Text>
43
49
  <ModelInfoTags id={id} {...modelCard} isCustom />
44
50
  </Flexbox>
45
- <Typography.Text style={{ fontSize: 12 }} type={'secondary'}>
46
- <Flexbox gap={2} horizontal>
47
- {id}
48
- {!!modelCard?.deploymentName && (
49
- <>
50
- <Icon icon={LucideArrowRight} />
51
- {modelCard?.deploymentName}
52
- </>
53
- )}
54
- </Flexbox>
51
+ <Typography.Text ellipsis style={{ fontSize: 12, marginTop: '4px' }} type={'secondary'}>
52
+ {id}
53
+ {!!modelCard?.deploymentName && (
54
+ <>
55
+ <Icon icon={LucideArrowRight} />
56
+ {modelCard?.deploymentName}
57
+ </>
58
+ )}
55
59
  </Typography.Text>
56
60
  </Flexbox>
57
61
  </Flexbox>
@@ -83,6 +87,7 @@ const CustomModelOption = memo<CustomModelOptionProps>(({ id, provider }) => {
83
87
  type: 'warning',
84
88
  });
85
89
  }}
90
+ style={isEnabled ? { marginRight: '10px' } : {}}
86
91
  title={t('delete')}
87
92
  />
88
93
  </Flexbox>
@@ -2,6 +2,7 @@ import { Checkbox, Form, FormInstance, Input } from 'antd';
2
2
  import { memo, useEffect } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
 
5
+ import { useIsMobile } from '@/hooks/useIsMobile';
5
6
  import { ChatModelCard } from '@/types/llm';
6
7
 
7
8
  import MaxTokenSlider from './MaxTokenSlider';
@@ -18,6 +19,8 @@ const ModelConfigForm = memo<ModelConfigFormProps>(
18
19
 
19
20
  const [formInstance] = Form.useForm();
20
21
 
22
+ const isMobile = useIsMobile();
23
+
21
24
  useEffect(() => {
22
25
  onFormInstanceReady(formInstance);
23
26
  }, []);
@@ -37,7 +40,7 @@ const ModelConfigForm = memo<ModelConfigFormProps>(
37
40
  initialValues={initialValues}
38
41
  labelCol={{ span: 4 }}
39
42
  style={{ marginTop: 16 }}
40
- wrapperCol={{ offset: 1, span: 18 }}
43
+ wrapperCol={isMobile ? { span: 18 } : { offset: 1, span: 18 }}
41
44
  >
42
45
  <Form.Item
43
46
  extra={t('llm.customModelCards.modelConfig.id.extra')}
@@ -1,4 +1,5 @@
1
1
  import { Icon, Tooltip } from '@lobehub/ui';
2
+ import { Typography } from 'antd';
2
3
  import { createStyles } from 'antd-style';
3
4
  import { Infinity, LucideEye, LucidePaperclip, ToyBrick } from 'lucide-react';
4
5
  import numeral from 'numeral';
@@ -142,7 +143,9 @@ export const ModelItemRender = memo<ModelItemRenderProps>(({ showInfoTag = true,
142
143
  <Flexbox align={'center'} gap={32} horizontal justify={'space-between'}>
143
144
  <Flexbox align={'center'} gap={8} horizontal>
144
145
  <ModelIcon model={model.id} size={20} />
145
- {model.displayName || model.id}
146
+ <Typography.Paragraph ellipsis={false} style={{ marginBottom: 0 }}>
147
+ {model.displayName || model.id}
148
+ </Typography.Paragraph>
146
149
  </Flexbox>
147
150
 
148
151
  {showInfoTag && <ModelInfoTags {...model} />}
@@ -6,11 +6,10 @@ import { memo, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
+ import BubblesLoading from '@/components/BubblesLoading';
9
10
  import { useChatStore } from '@/store/chat';
10
11
  import { ChatTranslate } from '@/types/message';
11
12
 
12
- import BubblesLoading from '../components/BubblesLoading';
13
-
14
13
  interface TranslateProps extends ChatTranslate {
15
14
  id: string;
16
15
  loading?: boolean;
@@ -1,57 +1,84 @@
1
- import { Icon } from '@lobehub/ui';
1
+ import { Icon, Tag } from '@lobehub/ui';
2
+ import { Typography } from 'antd';
2
3
  import isEqual from 'fast-deep-equal';
3
- import { Loader2, LucideChevronDown, LucideChevronRight, LucideToyBrick } from 'lucide-react';
4
+ import { Loader2 } from 'lucide-react';
4
5
  import { CSSProperties, memo, useState } from 'react';
5
6
  import { useTranslation } from 'react-i18next';
6
7
  import { Flexbox } from 'react-layout-kit';
7
8
 
9
+ import PluginAvatar from '@/features/PluginAvatar';
10
+ import { useIsMobile } from '@/hooks/useIsMobile';
8
11
  import { useChatStore } from '@/store/chat';
9
12
  import { chatSelectors } from '@/store/chat/selectors';
10
13
  import { pluginHelpers, useToolStore } from '@/store/tool';
11
14
  import { toolSelectors } from '@/store/tool/selectors';
12
15
 
16
+ import { ToolMessage } from '../../Tool';
13
17
  import Arguments from '../../components/Arguments';
14
18
  import { useStyles } from './style';
15
19
 
16
20
  export interface InspectorProps {
21
+ apiName: string;
17
22
  arguments?: string;
23
+ id: string;
18
24
  identifier: string;
19
25
  index: number;
20
26
  messageId: string;
21
- style: CSSProperties;
27
+ style?: CSSProperties;
22
28
  }
23
29
 
24
30
  const CallItem = memo<InspectorProps>(
25
- ({ arguments: requestArgs, messageId, index, identifier, style }) => {
31
+ ({ arguments: requestArgs, apiName, messageId, id, index, identifier, style }) => {
26
32
  const { t } = useTranslation('plugin');
27
33
  const { styles } = useStyles();
28
34
  const [open, setOpen] = useState(false);
29
35
  const loading = useChatStore(chatSelectors.isToolCallStreaming(messageId, index));
36
+ const toolMessage = useChatStore(chatSelectors.getMessageByToolCallId(id));
37
+ const isMobile = useIsMobile();
30
38
 
31
39
  const pluginMeta = useToolStore(toolSelectors.getMetaById(identifier), isEqual);
32
40
 
33
41
  const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('unknownPlugin');
34
42
 
35
- return (
43
+ // when tool calling stop streaming, we should show the tool message
44
+ return !loading && toolMessage ? (
45
+ <ToolMessage {...toolMessage} />
46
+ ) : (
36
47
  <Flexbox gap={8} style={style}>
37
48
  <Flexbox
38
49
  align={'center'}
39
50
  className={styles.container}
40
51
  distribution={'space-between'}
41
52
  gap={8}
42
- height={32}
43
53
  horizontal
44
54
  onClick={() => {
45
55
  setOpen(!open);
46
56
  }}
47
57
  >
48
58
  <Flexbox align={'center'} gap={8} horizontal>
49
- {loading ? <Icon icon={Loader2} spin /> : <Icon icon={LucideToyBrick} />}
50
- {pluginTitle}
59
+ {loading ? (
60
+ <div>
61
+ <Icon icon={Loader2} spin />
62
+ </div>
63
+ ) : (
64
+ <PluginAvatar identifier={identifier} size={isMobile ? 36 : undefined} />
65
+ )}
66
+ {isMobile ? (
67
+ <Flexbox>
68
+ <div>{pluginTitle}</div>
69
+ <Typography.Text className={styles.apiName} type={'secondary'}>
70
+ {apiName}
71
+ </Typography.Text>
72
+ </Flexbox>
73
+ ) : (
74
+ <>
75
+ <div>{pluginTitle}</div>
76
+ <Tag>{apiName}</Tag>
77
+ </>
78
+ )}
51
79
  </Flexbox>
52
- <Icon icon={open ? LucideChevronDown : LucideChevronRight} />
53
80
  </Flexbox>
54
- {(open || loading) && <Arguments arguments={requestArgs} />}
81
+ {loading && <Arguments arguments={requestArgs} />}
55
82
  </Flexbox>
56
83
  );
57
84
  },
@@ -1,19 +1,30 @@
1
1
  import { createStyles } from 'antd-style';
2
2
 
3
3
  export const useStyles = createStyles(({ css, token }) => ({
4
+ apiName: css`
5
+ overflow: hidden;
6
+ display: -webkit-box;
7
+ -webkit-box-orient: vertical;
8
+ -webkit-line-clamp: 1;
9
+
10
+ font-size: 12px;
11
+ text-overflow: ellipsis;
12
+ `,
4
13
  container: css`
5
14
  cursor: pointer;
6
15
 
7
16
  width: fit-content;
8
- padding-inline: 4px 6px;
17
+ padding-block: 6px;
18
+ padding-inline: 8px;
19
+ padding-inline-end: 12px;
9
20
 
10
21
  color: ${token.colorText};
11
22
 
12
- background: ${token.colorFillTertiary};
23
+ border: 1px solid ${token.colorBorder};
13
24
  border-radius: 8px;
14
25
 
15
26
  &:hover {
16
- background: ${token.colorFillSecondary};
27
+ background: ${token.colorFillTertiary};
17
28
  }
18
29
  `,
19
30
  plugin: css`
@@ -7,7 +7,7 @@ import { chatSelectors } from '@/store/chat/selectors';
7
7
  import { ChatMessage } from '@/types/message';
8
8
 
9
9
  import { DefaultMessage } from '../Default';
10
- import ToolCall from './ToolCalls';
10
+ import ToolCall from './ToolCallItem';
11
11
 
12
12
  export const AssistantMessage = memo<
13
13
  ChatMessage & {
@@ -31,17 +31,16 @@ export const AssistantMessage = memo<
31
31
  />
32
32
  )}
33
33
  {!editing && tools && (
34
- <Flexbox gap={8} horizontal>
34
+ <Flexbox gap={8}>
35
35
  {tools.map((toolCall, index) => (
36
36
  <ToolCall
37
+ apiName={toolCall.apiName}
37
38
  arguments={toolCall.arguments}
39
+ id={toolCall.id}
38
40
  identifier={toolCall.identifier}
39
41
  index={index}
40
42
  key={toolCall.id}
41
43
  messageId={id}
42
- style={{
43
- maxWidth: `max(${100 / tools.length}%, 300px)`,
44
- }}
45
44
  />
46
45
  ))}
47
46
  </Flexbox>
@@ -3,7 +3,7 @@ import { ReactNode, memo } from 'react';
3
3
  import { LOADING_FLAT } from '@/const/message';
4
4
  import { ChatMessage } from '@/types/message';
5
5
 
6
- import BubblesLoading from '../components/BubblesLoading';
6
+ import BubblesLoading from '@/components/BubblesLoading';
7
7
 
8
8
  export const DefaultMessage = memo<
9
9
  ChatMessage & {
@@ -1,12 +1,11 @@
1
1
  import { ReactNode, memo } from 'react';
2
2
  import { Flexbox } from 'react-layout-kit';
3
3
 
4
+ import BubblesLoading from '@/components/BubblesLoading';
4
5
  import { LOADING_FLAT } from '@/const/message';
5
6
  import { FileListPreviewer } from '@/features/FileList';
6
7
  import { ChatMessage } from '@/types/message';
7
8
 
8
- import BubblesLoading from '../components/BubblesLoading';
9
-
10
9
  export const UserMessage = memo<
11
10
  ChatMessage & {
12
11
  editableContent: ReactNode;
@@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next';
10
10
  import { Flexbox } from 'react-layout-kit';
11
11
 
12
12
  import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect';
13
+ import { useIsMobile } from '@/hooks/useIsMobile';
13
14
  import { useAgentStore } from '@/store/agent';
14
15
  import { agentSelectors } from '@/store/agent/slices/chat';
15
16
  import { useUserStore } from '@/store/user';
@@ -49,7 +50,10 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
49
50
  s.updateAgentConfig,
50
51
  ]);
51
52
 
53
+ const isMobile = useIsMobile();
54
+
52
55
  const router = useRouter();
56
+
53
57
  const enabledList = useUserStore(modelProviderSelectors.modelProviderListForModelSelect, isEqual);
54
58
 
55
59
  const items = useMemo<ItemType[]>(() => {
@@ -102,7 +106,7 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
102
106
  overflowY: 'scroll',
103
107
  },
104
108
  }}
105
- placement={'topLeft'}
109
+ placement={isMobile ? 'top' : 'topLeft'}
106
110
  trigger={['click']}
107
111
  >
108
112
  <div className={styles.tag}>{children}</div>
@@ -131,6 +131,36 @@ describe('chatSelectors', () => {
131
131
  });
132
132
  });
133
133
 
134
+ describe('getMessageByToolCallId', () => {
135
+ it('should return undefined if the message with the given id does not exist', () => {
136
+ const message = chatSelectors.getMessageByToolCallId('non-existent-id')(initialStore);
137
+ expect(message).toBeUndefined();
138
+ });
139
+
140
+ it('should return the message object with the matching tool_call_id', () => {
141
+ const toolMessage = {
142
+ id: 'msg3',
143
+ content: 'Function Message',
144
+ role: 'tool',
145
+ tool_call_id: 'ttt',
146
+ plugin: {
147
+ arguments: 'arg1',
148
+ identifier: 'func1',
149
+ apiName: 'ttt',
150
+ type: 'default',
151
+ },
152
+ } as ChatMessage;
153
+ const state = merge(initialStore, {
154
+ messagesMap: {
155
+ [messageMapKey('abc')]: [...mockMessages, toolMessage],
156
+ },
157
+ activeId: 'abc',
158
+ });
159
+ const message = chatSelectors.getMessageByToolCallId('ttt')(state);
160
+ expect(message).toMatchObject(toolMessage);
161
+ });
162
+ });
163
+
134
164
  describe('currentChatsWithHistoryConfig', () => {
135
165
  it('should slice the messages according to the current agent config', () => {
136
166
  const state = merge(initialStore, {
@@ -203,7 +233,7 @@ describe('chatSelectors', () => {
203
233
  });
204
234
 
205
235
  describe('currentChatsWithGuideMessage', () => {
206
- it('should return existing messages if there are any', () => {
236
+ it('should return existing messages except tool message', () => {
207
237
  const state = merge(initialStore, {
208
238
  messagesMap: {
209
239
  [messageMapKey('someActiveId')]: mockMessages,
@@ -211,7 +241,7 @@ describe('chatSelectors', () => {
211
241
  activeId: 'someActiveId',
212
242
  });
213
243
  const chats = chatSelectors.currentChatsWithGuideMessage({} as MetaData)(state);
214
- expect(chats).toEqual(mockedChats);
244
+ expect(chats).toEqual(mockedChats.slice(0, 2));
215
245
  });
216
246
 
217
247
  it('should add a guide message if the chat is brand new', () => {
@@ -65,11 +65,12 @@ const showInboxWelcome = (s: ChatStore): boolean => {
65
65
  return isBrandNewChat;
66
66
  };
67
67
 
68
- // 针对新助手添加初始化时的自定义消息
68
+ // Custom message for new assistant initialization
69
69
  const currentChatsWithGuideMessage =
70
70
  (meta: MetaData) =>
71
71
  (s: ChatStore): ChatMessage[] => {
72
- const data = currentChats(s);
72
+ // skip tool message
73
+ const data = currentChats(s).filter((m) => m.role !== 'tool');
73
74
 
74
75
  const { isAgentEditable } = featureFlagsSelectors(createServerConfigStore().getState());
75
76
 
@@ -125,6 +126,10 @@ const chatsMessageString = (s: ChatStore): string => {
125
126
  const getMessageById = (id: string) => (s: ChatStore) =>
126
127
  chatHelpers.getMessageById(currentChats(s), id);
127
128
 
129
+ const getMessageByToolCallId = (id: string) => (s: ChatStore) => {
130
+ const messages = currentChats(s);
131
+ return messages.find((m) => m.tool_call_id === id);
132
+ };
128
133
  const getTraceIdByMessageId = (id: string) => (s: ChatStore) => getMessageById(id)(s)?.traceId;
129
134
 
130
135
  const latestMessage = (s: ChatStore) => currentChats(s).at(-1);
@@ -160,6 +165,7 @@ export const chatSelectors = {
160
165
  currentChatsWithHistoryConfig,
161
166
  currentToolMessages,
162
167
  getMessageById,
168
+ getMessageByToolCallId,
163
169
  getTraceIdByMessageId,
164
170
  isAIGenerating,
165
171
  isCreatingMessage,