@lobehub/chat 1.84.9 → 1.84.11

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 (93) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/apps/desktop/electron.vite.config.ts +3 -0
  3. package/changelog/v1.json +18 -0
  4. package/locales/ar/components.json +2 -1
  5. package/locales/ar/models.json +63 -0
  6. package/locales/bg-BG/components.json +2 -1
  7. package/locales/bg-BG/models.json +63 -0
  8. package/locales/de-DE/components.json +2 -1
  9. package/locales/de-DE/models.json +63 -0
  10. package/locales/en-US/components.json +2 -1
  11. package/locales/en-US/models.json +63 -0
  12. package/locales/es-ES/components.json +2 -1
  13. package/locales/es-ES/models.json +63 -0
  14. package/locales/fa-IR/components.json +2 -1
  15. package/locales/fa-IR/models.json +63 -0
  16. package/locales/fr-FR/components.json +2 -1
  17. package/locales/fr-FR/models.json +63 -0
  18. package/locales/it-IT/components.json +2 -1
  19. package/locales/it-IT/models.json +63 -0
  20. package/locales/ja-JP/components.json +2 -1
  21. package/locales/ja-JP/models.json +63 -0
  22. package/locales/ko-KR/components.json +2 -1
  23. package/locales/ko-KR/models.json +63 -0
  24. package/locales/nl-NL/components.json +2 -1
  25. package/locales/nl-NL/models.json +63 -0
  26. package/locales/pl-PL/components.json +2 -1
  27. package/locales/pl-PL/models.json +63 -0
  28. package/locales/pt-BR/components.json +2 -1
  29. package/locales/pt-BR/models.json +63 -0
  30. package/locales/ru-RU/components.json +2 -1
  31. package/locales/ru-RU/models.json +63 -0
  32. package/locales/tr-TR/components.json +2 -1
  33. package/locales/tr-TR/models.json +63 -0
  34. package/locales/vi-VN/components.json +2 -1
  35. package/locales/vi-VN/models.json +63 -0
  36. package/locales/zh-CN/components.json +2 -1
  37. package/locales/zh-CN/models.json +63 -0
  38. package/locales/zh-TW/components.json +2 -1
  39. package/locales/zh-TW/models.json +63 -0
  40. package/package.json +2 -2
  41. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx +1 -1
  42. package/src/app/[variants]/(main)/chat/(workspace)/@topic/features/Header.tsx +5 -1
  43. package/src/app/[variants]/(main)/chat/settings/page.tsx +1 -0
  44. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +4 -1
  45. package/src/app/[variants]/(main)/settings/provider/(detail)/ollama/CheckError.tsx +4 -2
  46. package/src/app/[variants]/(main)/settings/provider/(detail)/ollama/Container.tsx +2 -2
  47. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/index.tsx +1 -0
  48. package/src/components/FileParsingStatus/index.tsx +1 -7
  49. package/src/components/ModelSelect/index.tsx +2 -2
  50. package/src/config/aiModels/siliconcloud.ts +89 -7
  51. package/src/config/modelProviders/google.ts +16 -0
  52. package/src/features/ChatInput/ActionBar/{Clear.tsx → Clear/index.tsx} +3 -2
  53. package/src/features/ChatInput/ActionBar/History/Controls.tsx +72 -0
  54. package/src/features/ChatInput/ActionBar/History/index.tsx +46 -0
  55. package/src/features/ChatInput/ActionBar/Knowledge/index.tsx +31 -25
  56. package/src/features/ChatInput/ActionBar/Knowledge/{Dropdown.tsx → useControls.tsx} +20 -40
  57. package/src/features/ChatInput/ActionBar/Model/ControlsForm.tsx +8 -1
  58. package/src/features/ChatInput/ActionBar/Model/index.tsx +27 -19
  59. package/src/features/ChatInput/ActionBar/Params/{ParamsControls.tsx → Controls.tsx} +9 -9
  60. package/src/features/ChatInput/ActionBar/Params/index.tsx +17 -20
  61. package/src/features/ChatInput/{STT → ActionBar/STT}/common.tsx +1 -0
  62. package/src/features/ChatInput/ActionBar/Search/{SwitchPanel.tsx → Controls.tsx} +12 -11
  63. package/src/features/ChatInput/ActionBar/Search/index.tsx +20 -25
  64. package/src/features/ChatInput/ActionBar/Token/TokenTag.tsx +1 -1
  65. package/src/features/ChatInput/ActionBar/Tools/ToolItem.tsx +15 -6
  66. package/src/features/ChatInput/ActionBar/Tools/index.tsx +26 -18
  67. package/src/features/ChatInput/ActionBar/Tools/{Dropdown.tsx → useControls.tsx} +38 -63
  68. package/src/features/ChatInput/ActionBar/Upload/ServerMode.tsx +10 -11
  69. package/src/features/ChatInput/ActionBar/components/Action.tsx +90 -0
  70. package/src/features/ChatInput/{components → ActionBar/components}/ActionDropdown.tsx +4 -4
  71. package/src/features/ChatInput/{components → ActionBar/components}/ActionPopover.tsx +5 -4
  72. package/src/features/ChatInput/ActionBar/{Knowledge/ListItem.tsx → components/CheckbokWithLoading.tsx} +14 -12
  73. package/src/features/ChatInput/ActionBar/config.ts +1 -1
  74. package/src/features/Conversation/Actions/Error.tsx +10 -2
  75. package/src/features/Conversation/Error/OllamaBizError/index.tsx +2 -2
  76. package/src/features/Conversation/Error/index.tsx +3 -10
  77. package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/Loading.tsx +1 -1
  78. package/src/features/ModelSwitchPanel/index.tsx +18 -5
  79. package/src/features/{Conversation/Error/OllamaDesktopSetupGuide/index.tsx → OllamaSetupGuide/Desktop.tsx} +25 -20
  80. package/src/features/OllamaSetupGuide/index.tsx +17 -0
  81. package/src/features/ShareModal/ShareImage/ChatList/index.tsx +1 -1
  82. package/src/features/ShareModal/ShareImage/Preview.tsx +2 -2
  83. package/src/features/ShareModal/ShareImage/index.tsx +8 -6
  84. package/src/hooks/useImgToClipboard.ts +4 -1
  85. package/src/layout/GlobalProvider/Locale.tsx +0 -8
  86. package/src/libs/agent-runtime/siliconcloud/index.ts +17 -1
  87. package/src/locales/default/components.ts +1 -0
  88. package/src/utils/server/auth.ts +6 -0
  89. package/src/features/ChatInput/ActionBar/History.tsx +0 -78
  90. package/src/features/Conversation/Error/OllamaBizError/SetupGuide.tsx +0 -14
  91. /package/src/features/ChatInput/{STT → ActionBar/STT}/browser.tsx +0 -0
  92. /package/src/features/ChatInput/{STT → ActionBar/STT}/index.tsx +0 -0
  93. /package/src/features/ChatInput/{STT → ActionBar/STT}/openai.tsx +0 -0
@@ -3,6 +3,95 @@ import { AIChatModelCard } from '@/types/aiModel';
3
3
  // https://siliconflow.cn/zh-cn/models
4
4
 
5
5
  const siliconcloudChatModels: AIChatModelCard[] = [
6
+ {
7
+ abilities: {
8
+ functionCall: true,
9
+ reasoning: true,
10
+ },
11
+ contextWindowTokens: 131_072,
12
+ description:
13
+ 'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
14
+ displayName: 'Qwen3 32B',
15
+ id: 'Qwen/Qwen3-32B',
16
+ organization: 'Qwen',
17
+ pricing: {
18
+ currency: 'CNY',
19
+ input: 0.5,
20
+ output: 2,
21
+ },
22
+ releasedAt: '2025-04-28',
23
+ settings: {
24
+ extendParams: ['enableReasoning', 'reasoningBudgetToken'],
25
+ },
26
+ type: 'chat',
27
+ },
28
+ {
29
+ abilities: {
30
+ functionCall: true,
31
+ reasoning: true,
32
+ },
33
+ contextWindowTokens: 131_072,
34
+ description:
35
+ 'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
36
+ displayName: 'Qwen3 30B A3B',
37
+ id: 'Qwen/Qwen3-30B-A3B',
38
+ organization: 'Qwen',
39
+ pricing: {
40
+ currency: 'CNY',
41
+ input: 0.35,
42
+ output: 1.4,
43
+ },
44
+ releasedAt: '2025-04-28',
45
+ settings: {
46
+ extendParams: ['enableReasoning', 'reasoningBudgetToken'],
47
+ },
48
+ type: 'chat',
49
+ },
50
+ {
51
+ abilities: {
52
+ functionCall: true,
53
+ reasoning: true,
54
+ },
55
+ contextWindowTokens: 131_072,
56
+ description:
57
+ 'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
58
+ displayName: 'Qwen3 14B',
59
+ id: 'Qwen/Qwen3-14B',
60
+ organization: 'Qwen',
61
+ pricing: {
62
+ currency: 'CNY',
63
+ input: 0.25,
64
+ output: 1,
65
+ },
66
+ releasedAt: '2025-04-28',
67
+ settings: {
68
+ extendParams: ['enableReasoning', 'reasoningBudgetToken'],
69
+ },
70
+ type: 'chat',
71
+ },
72
+ {
73
+ abilities: {
74
+ functionCall: true,
75
+ reasoning: true,
76
+ },
77
+ contextWindowTokens: 131_072,
78
+ description:
79
+ 'Qwen3是一款能力大幅提升的新一代通义千问大模型,在推理、通用、Agent和多语言等多个核心能力上均达到业界领先水平,并支持思考模式切换。',
80
+ displayName: 'Qwen3 8B',
81
+ enabled: true,
82
+ id: 'Qwen/Qwen3-8B',
83
+ organization: 'Qwen',
84
+ pricing: {
85
+ currency: 'CNY',
86
+ input: 0,
87
+ output: 0,
88
+ },
89
+ releasedAt: '2025-04-28',
90
+ settings: {
91
+ extendParams: ['enableReasoning', 'reasoningBudgetToken'],
92
+ },
93
+ type: 'chat',
94
+ },
6
95
  {
7
96
  abilities: {
8
97
  reasoning: true,
@@ -11,7 +100,6 @@ const siliconcloudChatModels: AIChatModelCard[] = [
11
100
  description:
12
101
  'GLM-Z1-Rumination-32B-0414 是一个具有沉思能力的深度推理模型(与 OpenAI 的 Deep Research 对标)。与典型的深度思考模型不同,沉思模型采用更长时间的深度思考来解决更开放和复杂的问题。',
13
102
  displayName: 'GLM-Z1-Rumination 32B 0414',
14
- enabled: true,
15
103
  id: 'THUDM/GLM-Z1-Rumination-32B-0414',
16
104
  pricing: {
17
105
  currency: 'CNY',
@@ -28,7 +116,6 @@ const siliconcloudChatModels: AIChatModelCard[] = [
28
116
  description:
29
117
  'GLM-Z1-32B-0414 是一个具有深度思考能力的推理模型。该模型基于 GLM-4-32B-0414 通过冷启动和扩展强化学习开发,并在数学、代码和逻辑任务上进行了进一步训练。与基础模型相比,GLM-Z1-32B-0414 显著提升了数学能力和解决复杂任务的能力。',
30
118
  displayName: 'GLM-Z1 32B 0414',
31
- enabled: true,
32
119
  id: 'THUDM/GLM-Z1-32B-0414',
33
120
  pricing: {
34
121
  currency: 'CNY',
@@ -59,7 +146,6 @@ const siliconcloudChatModels: AIChatModelCard[] = [
59
146
  description:
60
147
  'GLM-4-32B-0414 是 GLM 系列的新一代开源模型,拥有 320 亿参数。该模型性能可与 OpenAI 的 GPT 系列和 DeepSeek 的 V3/R1 系列相媲美。',
61
148
  displayName: 'GLM-4 32B 0414',
62
- enabled: true,
63
149
  id: 'THUDM/GLM-4-32B-0414',
64
150
  pricing: {
65
151
  currency: 'CNY',
@@ -91,7 +177,6 @@ const siliconcloudChatModels: AIChatModelCard[] = [
91
177
  description:
92
178
  'DeepSeek-R1 是一款强化学习(RL)驱动的推理模型,解决了模型中的重复性和可读性问题。在 RL 之前,DeepSeek-R1 引入了冷启动数据,进一步优化了推理性能。它在数学、代码和推理任务中与 OpenAI-o1 表现相当,并且通过精心设计的训练方法,提升了整体效果。',
93
179
  displayName: 'DeepSeek R1',
94
- enabled: true,
95
180
  id: 'deepseek-ai/DeepSeek-R1',
96
181
  pricing: {
97
182
  currency: 'CNY',
@@ -108,7 +193,6 @@ const siliconcloudChatModels: AIChatModelCard[] = [
108
193
  description:
109
194
  'DeepSeek-V3 是一款拥有 6710 亿参数的混合专家(MoE)语言模型,采用多头潜在注意力(MLA)和 DeepSeekMoE 架构,结合无辅助损失的负载平衡策略,优化推理和训练效率。通过在 14.8 万亿高质量tokens上预训练,并进行监督微调和强化学习,DeepSeek-V3 在性能上超越其他开源模型,接近领先闭源模型。',
110
195
  displayName: 'DeepSeek V3',
111
- enabled: true,
112
196
  id: 'deepseek-ai/DeepSeek-V3',
113
197
  pricing: {
114
198
  currency: 'CNY',
@@ -303,7 +387,6 @@ const siliconcloudChatModels: AIChatModelCard[] = [
303
387
  description:
304
388
  'QVQ-72B-Preview 是由 Qwen 团队开发的专注于视觉推理能力的研究型模型,其在复杂场景理解和解决视觉相关的数学问题方面具有独特优势。',
305
389
  displayName: 'QVQ 72B Preview',
306
- enabled: true,
307
390
  id: 'Qwen/QVQ-72B-Preview',
308
391
  pricing: {
309
392
  currency: 'CNY',
@@ -320,7 +403,6 @@ const siliconcloudChatModels: AIChatModelCard[] = [
320
403
  description:
321
404
  'QwQ 是 Qwen 系列的推理模型。与传统的指令调优模型相比,QwQ 具备思考和推理能力,能够在下游任务中实现显著增强的性能,尤其是在解决困难问题方面。QwQ-32B 是中型推理模型,能够在与最先进的推理模型(如 DeepSeek-R1、o1-mini)的对比中取得有竞争力的性能。该模型采用 RoPE、SwiGLU、RMSNorm 和 Attention QKV bias 等技术,具有 64 层网络结构和 40 个 Q 注意力头(GQA 架构中 KV 为 8 个)。',
322
405
  displayName: 'QwQ 32B',
323
- enabled: true,
324
406
  id: 'Qwen/QwQ-32B',
325
407
  pricing: {
326
408
  currency: 'CNY',
@@ -3,6 +3,22 @@ import { ModelProviderCard } from '@/types/llm';
3
3
  // ref: https://ai.google.dev/gemini-api/docs/models/gemini
4
4
  const Google: ModelProviderCard = {
5
5
  chatModels: [
6
+ {
7
+ contextWindowTokens: 1_048_576 + 65_536,
8
+ description:
9
+ 'Gemini 2.5 Pro Experimental 是 Google 最先进的思维模型,能够对代码、数学和STEM领域的复杂问题进行推理,以及使用长上下文分析大型数据集、代码库和文档。',
10
+ displayName: 'Gemini 2.5 Pro Experimental 03-25',
11
+ enabled: true,
12
+ functionCall: true,
13
+ id: 'gemini-2.5-pro-exp-03-25',
14
+ maxOutput: 65_536,
15
+ pricing: {
16
+ input: 0,
17
+ output: 0,
18
+ },
19
+ releasedAt: '2025-03-25',
20
+ vision: true,
21
+ },
6
22
  {
7
23
  contextWindowTokens: 2_097_152 + 8192,
8
24
  description:
@@ -1,4 +1,3 @@
1
- import { ActionIcon } from '@lobehub/ui';
2
1
  import { Popconfirm } from 'antd';
3
2
  import { Eraser } from 'lucide-react';
4
3
  import { memo, useCallback, useState } from 'react';
@@ -11,6 +10,8 @@ import { useUserStore } from '@/store/user';
11
10
  import { settingsSelectors } from '@/store/user/selectors';
12
11
  import { HotkeyEnum } from '@/types/hotkey';
13
12
 
13
+ import Action from '../components/Action';
14
+
14
15
  export const useClearCurrentMessages = () => {
15
16
  const clearMessage = useChatStore((s) => s.clearMessage);
16
17
  const clearImageList = useFileStore((s) => s.clearChatUploadFileList);
@@ -47,7 +48,7 @@ const Clear = memo(() => {
47
48
  </div>
48
49
  }
49
50
  >
50
- <ActionIcon
51
+ <Action
51
52
  icon={Eraser}
52
53
  title={actionTitle}
53
54
  tooltipProps={{
@@ -0,0 +1,72 @@
1
+ import { Form, type FormItemProps, SliderWithInput } from '@lobehub/ui';
2
+ import { Switch } from 'antd';
3
+ import { debounce } from 'lodash-es';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import { useAgentStore } from '@/store/agent';
8
+ import { agentChatConfigSelectors } from '@/store/agent/selectors';
9
+
10
+ interface ControlsProps {
11
+ setUpdating: (updating: boolean) => void;
12
+ updating: boolean;
13
+ }
14
+ const Controls = memo<ControlsProps>(({ updating, setUpdating }) => {
15
+ const { t } = useTranslation('setting');
16
+
17
+ const [historyCount, enableHistoryCount, updateAgentConfig] = useAgentStore((s) => {
18
+ return [
19
+ agentChatConfigSelectors.historyCount(s),
20
+ agentChatConfigSelectors.enableHistoryCount(s),
21
+ s.updateAgentChatConfig,
22
+ ];
23
+ });
24
+
25
+ let items: FormItemProps[] = [
26
+ {
27
+ children: <Switch loading={updating} size={'small'} />,
28
+ label: t('settingChat.enableHistoryCount.title'),
29
+ layout: 'horizontal',
30
+ minWidth: undefined,
31
+ name: 'enableHistoryCount',
32
+ valuePropName: 'checked',
33
+ },
34
+ {
35
+ children: (
36
+ <SliderWithInput
37
+ disabled={!enableHistoryCount}
38
+ max={20}
39
+ min={0}
40
+ size={'small'}
41
+ step={1}
42
+ style={{ marginBlock: 8, paddingLeft: 4 }}
43
+ />
44
+ ),
45
+ name: 'historyCount',
46
+ noStyle: true,
47
+ },
48
+ ];
49
+
50
+ return (
51
+ <Form
52
+ initialValues={{
53
+ enableHistoryCount,
54
+ historyCount,
55
+ }}
56
+ items={items}
57
+ itemsType={'flat'}
58
+ onValuesChange={debounce(async (values) => {
59
+ setUpdating(true);
60
+ await updateAgentConfig(values);
61
+ setUpdating(false);
62
+ }, 500)}
63
+ styles={{
64
+ group: {
65
+ background: 'transparent',
66
+ },
67
+ }}
68
+ />
69
+ );
70
+ });
71
+
72
+ export default Controls;
@@ -0,0 +1,46 @@
1
+ import { Timer, TimerOff } from 'lucide-react';
2
+ import { memo, useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+
5
+ import { useAgentStore } from '@/store/agent';
6
+ import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
7
+
8
+ import Action from '../components/Action';
9
+ import Controls from './Controls';
10
+
11
+ const History = memo(() => {
12
+ const [isLoading] = useAgentStore((s) => [agentSelectors.isAgentConfigLoading(s)]);
13
+ const [updating, setUpdating] = useState(false);
14
+ const { t } = useTranslation('setting');
15
+
16
+ const [historyCount, enableHistoryCount] = useAgentStore((s) => {
17
+ return [
18
+ agentChatConfigSelectors.historyCount(s),
19
+ agentChatConfigSelectors.enableHistoryCount(s),
20
+ ];
21
+ });
22
+
23
+ if (isLoading) return <Action disabled icon={TimerOff} />;
24
+
25
+ const title = t(
26
+ enableHistoryCount
27
+ ? 'settingChat.enableHistoryCount.limited'
28
+ : 'settingChat.enableHistoryCount.unlimited',
29
+ { number: historyCount || 0 },
30
+ );
31
+
32
+ return (
33
+ <Action
34
+ icon={enableHistoryCount ? Timer : TimerOff}
35
+ loading={updating}
36
+ popover={{
37
+ content: <Controls setUpdating={setUpdating} updating={updating} />,
38
+ minWidth: 240,
39
+ }}
40
+ showTooltip={false}
41
+ title={title}
42
+ />
43
+ );
44
+ });
45
+
46
+ export default History;
@@ -1,54 +1,59 @@
1
- import { ActionIcon } from '@lobehub/ui';
2
- import { LibraryBig, LucideLoader2 } from 'lucide-react';
3
- import { Suspense, memo } from 'react';
1
+ import { LibraryBig } from 'lucide-react';
2
+ import { Suspense, memo, useState } from 'react';
4
3
  import { useTranslation } from 'react-i18next';
5
4
 
6
5
  import TipGuide from '@/components/TipGuide';
7
6
  import { LOBE_CHAT_CLOUD } from '@/const/branding';
8
7
  import { isServerMode } from '@/const/version';
8
+ import { AssignKnowledgeBaseModal } from '@/features/KnowledgeBaseModal';
9
9
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
10
10
  import { useUserStore } from '@/store/user';
11
11
  import { preferenceSelectors } from '@/store/user/selectors';
12
12
 
13
- import DropdownMenu from './Dropdown';
13
+ import Action from '../components/Action';
14
+ import { useControls } from './useControls';
14
15
 
15
16
  const enableKnowledge = isServerMode;
16
17
 
17
18
  const Knowledge = memo(() => {
18
19
  const { t } = useTranslation('chat');
19
-
20
20
  const { enableKnowledgeBase } = useServerConfigStore(featureFlagsSelectors);
21
-
22
21
  const [showTip, updateGuideState] = useUserStore((s) => [
23
22
  preferenceSelectors.showUploadFileInKnowledgeBaseTip(s),
24
23
  s.updateGuideState,
25
24
  ]);
25
+ const [modalOpen, setModalOpen] = useState(false);
26
+ const [updating, setUpdating] = useState(false);
26
27
 
27
- if (!enableKnowledgeBase) {
28
- return null;
29
- }
28
+ const items = useControls({ setModalOpen, setUpdating });
30
29
 
31
- const icon = (
32
- <ActionIcon
33
- disabled={!enableKnowledge}
34
- icon={LibraryBig}
35
- title={
36
- enableKnowledge
37
- ? t('knowledgeBase.title')
38
- : t('knowledgeBase.disabled', { cloud: LOBE_CHAT_CLOUD })
39
- }
40
- tooltipProps={{
41
- placement: 'bottom',
30
+ if (!enableKnowledgeBase) return null;
31
+ if (!enableKnowledge)
32
+ return (
33
+ <Action
34
+ disabled
35
+ icon={LibraryBig}
36
+ showTooltip={true}
37
+ title={t('knowledgeBase.disabled', { cloud: LOBE_CHAT_CLOUD })}
38
+ />
39
+ );
40
+
41
+ const content = (
42
+ <Action
43
+ dropdown={{
44
+ maxWidth: 320,
45
+ menu: { items },
46
+ minWidth: 240,
42
47
  }}
48
+ icon={LibraryBig}
49
+ loading={updating}
50
+ showTooltip={false}
51
+ title={t('knowledgeBase.title')}
43
52
  />
44
53
  );
45
54
 
46
- if (!enableKnowledge) return icon;
47
-
48
- const content = <DropdownMenu>{icon}</DropdownMenu>;
49
-
50
55
  return (
51
- <Suspense fallback={<ActionIcon icon={LucideLoader2} spin />}>
56
+ <Suspense fallback={<Action disabled icon={LibraryBig} title={t('knowledgeBase.title')} />}>
52
57
  {showTip ? (
53
58
  <TipGuide
54
59
  onOpenChange={() => {
@@ -63,6 +68,7 @@ const Knowledge = memo(() => {
63
68
  ) : (
64
69
  content
65
70
  )}
71
+ <AssignKnowledgeBaseModal open={modalOpen} setOpen={setModalOpen} />
66
72
  </Suspense>
67
73
  );
68
74
  });
@@ -1,24 +1,24 @@
1
- import { DropdownProps, Icon, ItemType } from '@lobehub/ui';
1
+ import { Icon, ItemType } from '@lobehub/ui';
2
2
  import isEqual from 'fast-deep-equal';
3
3
  import { ArrowRight, LibraryBig } from 'lucide-react';
4
- import { PropsWithChildren, memo, useState } from 'react';
5
4
  import { useTranslation } from 'react-i18next';
6
5
 
7
6
  import FileIcon from '@/components/FileIcon';
8
7
  import RepoIcon from '@/components/RepoIcon';
9
- import { AssignKnowledgeBaseModal } from '@/features/KnowledgeBaseModal';
10
8
  import { useAgentStore } from '@/store/agent';
11
9
  import { agentSelectors } from '@/store/agent/selectors';
12
10
 
13
- import ActionDropdown from '../../components/ActionDropdown';
14
- import ListItem from './ListItem';
11
+ import CheckboxItem from '../components/CheckbokWithLoading';
15
12
 
16
- const DropdownMenu = memo<PropsWithChildren>(({ children }) => {
13
+ export const useControls = ({
14
+ setModalOpen,
15
+ setUpdating,
16
+ }: {
17
+ setModalOpen: (open: boolean) => void;
18
+ setUpdating: (updating: boolean) => void;
19
+ }) => {
17
20
  const { t } = useTranslation('chat');
18
21
 
19
- const [open, setOpen] = useState(false);
20
- const [dropdownOpen, setDropdownOpen] = useState(false);
21
-
22
22
  const files = useAgentStore(agentSelectors.currentAgentFiles, isEqual);
23
23
  const knowledgeBases = useAgentStore(agentSelectors.currentAgentKnowledgeBases, isEqual);
24
24
 
@@ -57,12 +57,14 @@ const DropdownMenu = memo<PropsWithChildren>(({ children }) => {
57
57
  icon: <FileIcon fileName={item.name} fileType={item.type} size={20} />,
58
58
  key: item.id,
59
59
  label: (
60
- <ListItem
61
- enabled={item.enabled}
60
+ <CheckboxItem
61
+ checked={item.enabled}
62
62
  id={item.id}
63
63
  label={item.name}
64
64
  onUpdate={async (id, enabled) => {
65
+ setUpdating(true);
65
66
  await toggleFile(id, enabled);
67
+ setUpdating(false);
66
68
  }}
67
69
  />
68
70
  ),
@@ -73,12 +75,14 @@ const DropdownMenu = memo<PropsWithChildren>(({ children }) => {
73
75
  icon: <RepoIcon />,
74
76
  key: item.id,
75
77
  label: (
76
- <ListItem
77
- enabled={item.enabled}
78
+ <CheckboxItem
79
+ checked={item.enabled}
78
80
  id={item.id}
79
81
  label={item.name}
80
82
  onUpdate={async (id, enabled) => {
83
+ setUpdating(true);
81
84
  await toggleKnowledgeBase(id, enabled);
85
+ setUpdating(false);
82
86
  }}
83
87
  />
84
88
  ),
@@ -97,34 +101,10 @@ const DropdownMenu = memo<PropsWithChildren>(({ children }) => {
97
101
  key: 'knowledge-base-store',
98
102
  label: t('knowledgeBase.viewMore'),
99
103
  onClick: () => {
100
- setDropdownOpen(false);
101
- setOpen(true);
104
+ setModalOpen(true);
102
105
  },
103
106
  },
104
107
  ];
105
108
 
106
- const handleOpenChange: DropdownProps['onOpenChange'] = (nextOpen, info) => {
107
- if (info.source === 'trigger' || nextOpen) {
108
- setDropdownOpen(nextOpen);
109
- }
110
- };
111
-
112
- return (
113
- <>
114
- <ActionDropdown
115
- maxHeight={500}
116
- menu={{
117
- items,
118
- }}
119
- minWidth={240}
120
- onOpenChange={handleOpenChange}
121
- open={dropdownOpen}
122
- >
123
- {children}
124
- </ActionDropdown>
125
- <AssignKnowledgeBaseModal open={open} setOpen={setOpen} />
126
- </>
127
- );
128
- });
129
-
130
- export default DropdownMenu;
109
+ return items;
110
+ };
@@ -13,7 +13,12 @@ import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
13
13
  import ContextCachingSwitch from './ContextCachingSwitch';
14
14
  import ReasoningTokenSlider from './ReasoningTokenSlider';
15
15
 
16
- const ControlsForm = memo(() => {
16
+ interface ControlsProps {
17
+ setUpdating: (updating: boolean) => void;
18
+ updating: boolean;
19
+ }
20
+
21
+ const ControlsForm = memo<ControlsProps>(({ setUpdating }) => {
17
22
  const { t } = useTranslation('chat');
18
23
  const [model, provider, updateAgentChatConfig] = useAgentStore((s) => [
19
24
  agentSelectors.currentAgentModel(s),
@@ -94,7 +99,9 @@ const ControlsForm = memo(() => {
94
99
  }
95
100
  itemsType={'flat'}
96
101
  onValuesChange={async (_, values) => {
102
+ setUpdating(true);
97
103
  await updateAgentChatConfig(values);
104
+ setUpdating(false);
98
105
  }}
99
106
  size={'small'}
100
107
  style={{ fontSize: 12 }}
@@ -1,8 +1,8 @@
1
1
  import { ModelIcon } from '@lobehub/icons';
2
- import { ActionIcon } from '@lobehub/ui';
2
+ import { Icon } from '@lobehub/ui';
3
3
  import { createStyles } from 'antd-style';
4
- import { Settings2Icon } from 'lucide-react';
5
- import { memo } from 'react';
4
+ import { Loader2Icon, Settings2Icon } from 'lucide-react';
5
+ import { memo, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Center, Flexbox } from 'react-layout-kit';
8
8
 
@@ -11,7 +11,7 @@ import { useAgentStore } from '@/store/agent';
11
11
  import { agentSelectors } from '@/store/agent/selectors';
12
12
  import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
13
13
 
14
- import ActionPopover from '../../components/ActionPopover';
14
+ import Action from '../components/Action';
15
15
  import ControlsForm from './ControlsForm';
16
16
 
17
17
  const useStyles = createStyles(({ css, token, cx }) => ({
@@ -55,7 +55,9 @@ const useStyles = createStyles(({ css, token, cx }) => ({
55
55
 
56
56
  const ModelSwitch = memo(() => {
57
57
  const { t } = useTranslation('chat');
58
- const { styles, cx } = useStyles();
58
+ const { styles, cx, theme } = useStyles();
59
+ const [updating, setUpdating] = useState(false);
60
+ const [controlsUpdating, setControlsUpdating] = useState(false);
59
61
 
60
62
  const [model, provider] = useAgentStore((s) => [
61
63
  agentSelectors.currentAgentModel(s),
@@ -68,29 +70,35 @@ const ModelSwitch = memo(() => {
68
70
 
69
71
  return (
70
72
  <Flexbox align={'center'} className={isModelHasExtendParams ? styles.container : ''} horizontal>
71
- <ModelSwitchPanel>
73
+ <ModelSwitchPanel setUpdating={setUpdating} updating={updating}>
72
74
  <Center
73
75
  className={cx(styles.model, isModelHasExtendParams && styles.modelWithControl)}
74
76
  height={36}
75
77
  width={36}
76
78
  >
77
- <div className={styles.icon}>
78
- <ModelIcon model={model} size={22} />
79
- </div>
79
+ {updating ? (
80
+ <Icon color={theme.colorTextDescription} icon={Loader2Icon} size={18} spin />
81
+ ) : (
82
+ <div className={styles.icon}>
83
+ <ModelIcon model={model} size={22} />
84
+ </div>
85
+ )}
80
86
  </Center>
81
87
  </ModelSwitchPanel>
82
88
 
83
89
  {isModelHasExtendParams && (
84
- <ActionPopover content={<ControlsForm />} minWidth={350} placement={'topLeft'}>
85
- <ActionIcon
86
- icon={Settings2Icon}
87
- style={{ borderRadius: 20, marginInlineStart: -4 }}
88
- title={t('extendParams.title')}
89
- tooltipProps={{
90
- placement: 'bottom',
91
- }}
92
- />
93
- </ActionPopover>
90
+ <Action
91
+ icon={Settings2Icon}
92
+ loading={controlsUpdating}
93
+ popover={{
94
+ content: <ControlsForm setUpdating={setControlsUpdating} updating={controlsUpdating} />,
95
+ minWidth: 350,
96
+ placement: 'topLeft',
97
+ }}
98
+ showTooltip={false}
99
+ style={{ borderRadius: 20, marginInlineStart: -4 }}
100
+ title={t('extendParams.title')}
101
+ />
94
102
  )}
95
103
  </Flexbox>
96
104
  );
@@ -16,17 +16,18 @@ import { useAgentStore } from '@/store/agent';
16
16
  import { agentSelectors } from '@/store/agent/selectors';
17
17
  import { useServerConfigStore } from '@/store/serverConfig';
18
18
 
19
- interface ParamsControlsProps {
19
+ interface ControlsProps {
20
20
  setUpdating: (updating: boolean) => void;
21
+ updating: boolean;
21
22
  }
22
- const ParamsControls = memo<ParamsControlsProps>(({ setUpdating }) => {
23
+ const Controls = memo<ControlsProps>(({ setUpdating }) => {
23
24
  const { t } = useTranslation('setting');
24
25
  const mobile = useServerConfigStore((s) => s.isMobile);
25
26
  const updateAgentConfig = useAgentStore((s) => s.updateAgentConfig);
26
27
 
27
28
  const config = useAgentStore(agentSelectors.currentAgentConfig, isEqual);
28
29
 
29
- let items: FormItemProps[] = [
30
+ const items: FormItemProps[] = [
30
31
  {
31
32
  children: <Temperature />,
32
33
  label: (
@@ -78,7 +79,9 @@ const ParamsControls = memo<ParamsControlsProps>(({ setUpdating }) => {
78
79
  initialValues={config}
79
80
  itemMinWidth={200}
80
81
  items={
81
- mobile ? items : items.map(({ tag, ...item }) => ({ ...item, desc: <Tag>{tag}</Tag> }))
82
+ mobile
83
+ ? items
84
+ : items.map(({ tag, ...item }) => ({ ...item, desc: <Tag size={'small'}>{tag}</Tag> }))
82
85
  }
83
86
  itemsType={'flat'}
84
87
  onValuesChange={debounce(async (values) => {
@@ -86,17 +89,14 @@ const ParamsControls = memo<ParamsControlsProps>(({ setUpdating }) => {
86
89
  await updateAgentConfig(values);
87
90
  setUpdating(false);
88
91
  }, 500)}
89
- style={{ fontSize: 12 }}
90
92
  styles={{
91
93
  group: {
92
94
  background: 'transparent',
93
- paddingBottom: mobile ? 16 : 0,
94
- paddingInline: 0,
95
+ paddingBottom: 12,
95
96
  },
96
97
  }}
97
- variant={'borderless'}
98
98
  />
99
99
  );
100
100
  });
101
101
 
102
- export default ParamsControls;
102
+ export default Controls;