@lobehub/chat 1.62.10 → 1.63.0

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 (112) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/changelog/v1.json +24 -0
  3. package/docs/self-hosting/environment-variables/model-provider.mdx +18 -0
  4. package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +18 -0
  5. package/docs/self-hosting/server-database/sealos.mdx +5 -1
  6. package/locales/ar/chat.json +26 -0
  7. package/locales/ar/models.json +21 -0
  8. package/locales/bg-BG/chat.json +26 -0
  9. package/locales/bg-BG/models.json +21 -0
  10. package/locales/de-DE/chat.json +26 -0
  11. package/locales/de-DE/models.json +21 -0
  12. package/locales/en-US/chat.json +26 -0
  13. package/locales/en-US/models.json +21 -0
  14. package/locales/es-ES/chat.json +26 -0
  15. package/locales/es-ES/models.json +21 -0
  16. package/locales/fa-IR/chat.json +26 -0
  17. package/locales/fa-IR/models.json +21 -0
  18. package/locales/fr-FR/chat.json +26 -0
  19. package/locales/fr-FR/models.json +21 -0
  20. package/locales/it-IT/chat.json +26 -0
  21. package/locales/it-IT/models.json +21 -0
  22. package/locales/ja-JP/chat.json +26 -0
  23. package/locales/ja-JP/models.json +21 -0
  24. package/locales/ko-KR/chat.json +26 -0
  25. package/locales/ko-KR/models.json +21 -0
  26. package/locales/nl-NL/chat.json +26 -0
  27. package/locales/nl-NL/models.json +21 -0
  28. package/locales/pl-PL/chat.json +26 -0
  29. package/locales/pl-PL/models.json +21 -0
  30. package/locales/pt-BR/chat.json +26 -0
  31. package/locales/pt-BR/models.json +21 -0
  32. package/locales/ru-RU/chat.json +26 -0
  33. package/locales/ru-RU/models.json +21 -0
  34. package/locales/tr-TR/chat.json +26 -0
  35. package/locales/tr-TR/models.json +21 -0
  36. package/locales/vi-VN/chat.json +26 -0
  37. package/locales/vi-VN/models.json +21 -0
  38. package/locales/zh-CN/chat.json +27 -1
  39. package/locales/zh-CN/models.json +25 -4
  40. package/locales/zh-TW/chat.json +26 -0
  41. package/locales/zh-TW/models.json +21 -0
  42. package/package.json +3 -3
  43. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +1 -0
  44. package/src/config/aiModels/google.ts +8 -0
  45. package/src/config/aiModels/groq.ts +111 -95
  46. package/src/config/aiModels/hunyuan.ts +36 -4
  47. package/src/config/aiModels/internlm.ts +4 -5
  48. package/src/config/aiModels/jina.ts +3 -0
  49. package/src/config/aiModels/mistral.ts +35 -21
  50. package/src/config/aiModels/novita.ts +293 -32
  51. package/src/config/aiModels/perplexity.ts +14 -2
  52. package/src/config/aiModels/qwen.ts +91 -37
  53. package/src/config/aiModels/sensenova.ts +70 -17
  54. package/src/config/aiModels/siliconcloud.ts +5 -3
  55. package/src/config/aiModels/stepfun.ts +19 -0
  56. package/src/config/aiModels/taichu.ts +4 -2
  57. package/src/config/aiModels/upstage.ts +24 -11
  58. package/src/config/modelProviders/openrouter.ts +1 -0
  59. package/src/config/modelProviders/qwen.ts +2 -1
  60. package/src/config/modelProviders/volcengine.ts +4 -1
  61. package/src/const/settings/agent.ts +1 -0
  62. package/src/database/repositories/aiInfra/index.test.ts +2 -5
  63. package/src/database/repositories/aiInfra/index.ts +6 -2
  64. package/src/database/schemas/message.ts +2 -1
  65. package/src/database/server/models/aiModel.ts +1 -1
  66. package/src/database/server/models/aiProvider.ts +6 -1
  67. package/src/features/ChatInput/ActionBar/Model/ControlsForm.tsx +38 -0
  68. package/src/features/ChatInput/ActionBar/Model/ExtendControls.tsx +40 -0
  69. package/src/features/ChatInput/ActionBar/Model/index.tsx +132 -0
  70. package/src/features/ChatInput/ActionBar/Params/index.tsx +2 -2
  71. package/src/features/ChatInput/ActionBar/Search/ExaIcon.tsx +15 -0
  72. package/src/features/ChatInput/ActionBar/Search/ModelBuiltinSearch.tsx +68 -0
  73. package/src/features/ChatInput/ActionBar/Search/SwitchPanel.tsx +167 -0
  74. package/src/features/ChatInput/ActionBar/Search/index.tsx +76 -0
  75. package/src/features/ChatInput/ActionBar/config.ts +4 -2
  76. package/src/features/Conversation/Messages/Assistant/SearchGrounding.tsx +153 -0
  77. package/src/features/Conversation/Messages/Assistant/index.tsx +7 -1
  78. package/src/features/ModelSelect/index.tsx +1 -1
  79. package/src/features/ModelSwitchPanel/index.tsx +2 -3
  80. package/src/hooks/useEnabledChatModels.ts +1 -1
  81. package/src/libs/agent-runtime/azureai/index.ts +21 -2
  82. package/src/libs/agent-runtime/google/index.test.ts +142 -36
  83. package/src/libs/agent-runtime/google/index.ts +26 -51
  84. package/src/libs/agent-runtime/novita/__snapshots__/index.test.ts.snap +3 -3
  85. package/src/libs/agent-runtime/openrouter/__snapshots__/index.test.ts.snap +3 -3
  86. package/src/libs/agent-runtime/openrouter/index.ts +20 -20
  87. package/src/libs/agent-runtime/perplexity/index.test.ts +2 -2
  88. package/src/libs/agent-runtime/qwen/index.ts +38 -55
  89. package/src/libs/agent-runtime/types/chat.ts +6 -2
  90. package/src/libs/agent-runtime/utils/streams/google-ai.ts +29 -4
  91. package/src/libs/agent-runtime/utils/streams/openai.ts +1 -1
  92. package/src/libs/agent-runtime/utils/streams/protocol.ts +1 -1
  93. package/src/locales/default/chat.ts +28 -0
  94. package/src/services/chat.ts +10 -0
  95. package/src/store/agent/slices/chat/__snapshots__/selectors.test.ts.snap +1 -0
  96. package/src/store/agent/slices/chat/selectors.ts +6 -0
  97. package/src/store/aiInfra/slices/aiModel/selectors.ts +36 -0
  98. package/src/store/aiInfra/slices/aiProvider/initialState.ts +2 -2
  99. package/src/store/aiInfra/slices/aiProvider/selectors.ts +14 -0
  100. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +15 -5
  101. package/src/store/chat/slices/message/action.ts +1 -1
  102. package/src/store/user/slices/modelList/selectors/modelProvider.ts +1 -1
  103. package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +1 -0
  104. package/src/types/agent/index.ts +4 -0
  105. package/src/types/aiModel.ts +35 -8
  106. package/src/types/aiProvider.ts +7 -10
  107. package/src/types/message/base.ts +2 -5
  108. package/src/types/message/chat.ts +5 -3
  109. package/src/types/openai/chat.ts +5 -0
  110. package/src/types/search.ts +29 -0
  111. package/src/utils/fetch/fetchSSE.ts +11 -11
  112. package/src/features/ChatInput/ActionBar/ModelSwitch.tsx +0 -20
@@ -331,6 +331,7 @@ const OpenRouter: ModelProviderCard = {
331
331
  // https://github.com/lobehub/lobe-chat/issues/5900
332
332
  disableBrowserRequest: true,
333
333
  sdkType: 'openai',
334
+ searchMode: 'params',
334
335
  showModelFetcher: true,
335
336
  },
336
337
  url: 'https://openrouter.ai',
@@ -153,7 +153,8 @@ const Qwen: ModelProviderCard = {
153
153
  },
154
154
  {
155
155
  contextWindowTokens: 32_768,
156
- description: 'QVQ模型是由 Qwen 团队开发的实验性研究模型,专注于提升视觉推理能力,尤其在数学推理领域。',
156
+ description:
157
+ 'QVQ模型是由 Qwen 团队开发的实验性研究模型,专注于提升视觉推理能力,尤其在数学推理领域。',
157
158
  displayName: 'QVQ 72B Preview',
158
159
  id: 'qvq-72b-preview',
159
160
  pricing: {
@@ -7,9 +7,12 @@ const Doubao: ModelProviderCard = {
7
7
  '字节跳动推出的大模型服务的开发平台,提供功能丰富、安全以及具备价格竞争力的模型调用服务,同时提供模型数据、精调、推理、评测等端到端功能,全方位保障您的 AI 应用开发落地。',
8
8
  id: 'volcengine',
9
9
  modelsUrl: 'https://www.volcengine.com/docs/82379/1330310',
10
- name: '火山引擎',
10
+ name: 'Volcengine',
11
11
  settings: {
12
12
  disableBrowserRequest: true, // CORS error
13
+ proxyUrl: {
14
+ placeholder: 'https://ark.cn-beijing.volces.com/api/v3',
15
+ },
13
16
  sdkType: 'openai',
14
17
  showDeployName: true,
15
18
  smoothing: {
@@ -20,6 +20,7 @@ export const DEFAULT_AGENT_CHAT_CONFIG: LobeAgentChatConfig = {
20
20
  enableCompressHistory: true,
21
21
  enableHistoryCount: true,
22
22
  historyCount: 8,
23
+ searchMode: 'off',
23
24
  };
24
25
 
25
26
  export const DEFAULT_AGENT_CONFIG: LobeAgentConfig = {
@@ -2,14 +2,11 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
2
 
3
3
  import { DEFAULT_MODEL_PROVIDER_LIST } from '@/config/modelProviders';
4
4
  import { clientDB, initializeDB } from '@/database/client/db';
5
- import { AiProviderModel } from '@/database/server/models/aiProvider';
6
- import { LobeChatDatabase } from '@/database/type';
7
- import { AiProviderModelListItem } from '@/types/aiModel';
5
+ import { AiProviderModelListItem, EnabledAiModel } from '@/types/aiModel';
8
6
  import {
9
7
  AiProviderDetailItem,
10
8
  AiProviderListItem,
11
9
  AiProviderRuntimeConfig,
12
- EnabledAiModel,
13
10
  EnabledProvider,
14
11
  } from '@/types/aiProvider';
15
12
 
@@ -286,7 +283,7 @@ describe('AiInfraRepos', () => {
286
283
  expect(result).toEqual(
287
284
  expect.arrayContaining([
288
285
  expect.objectContaining({ id: 'taichu_llm' }),
289
- expect.objectContaining({ id: 'taichu2_mm' }),
286
+ expect.objectContaining({ id: 'taichu_vl' }),
290
287
  ]),
291
288
  );
292
289
  });
@@ -5,12 +5,16 @@ import { DEFAULT_MODEL_PROVIDER_LIST } from '@/config/modelProviders';
5
5
  import { AiModelModel } from '@/database/server/models/aiModel';
6
6
  import { AiProviderModel } from '@/database/server/models/aiProvider';
7
7
  import { LobeChatDatabase } from '@/database/type';
8
- import { AIChatModelCard, AiModelSourceEnum, AiProviderModelListItem } from '@/types/aiModel';
8
+ import {
9
+ AIChatModelCard,
10
+ AiModelSourceEnum,
11
+ AiProviderModelListItem,
12
+ EnabledAiModel,
13
+ } from '@/types/aiModel';
9
14
  import {
10
15
  AiProviderDetailItem,
11
16
  AiProviderListItem,
12
17
  AiProviderRuntimeState,
13
- EnabledAiModel,
14
18
  EnabledProvider,
15
19
  } from '@/types/aiProvider';
16
20
  import { ProviderConfig } from '@/types/user/settings';
@@ -13,7 +13,8 @@ import {
13
13
  import { createSelectSchema } from 'drizzle-zod';
14
14
 
15
15
  import { idGenerator } from '@/database/utils/idGenerator';
16
- import { GroundingSearch, ModelReasoning } from '@/types/message';
16
+ import { ModelReasoning } from '@/types/message';
17
+ import { GroundingSearch } from '@/types/search';
17
18
 
18
19
  import { timestamps } from './_helpers';
19
20
  import { agents } from './agent';
@@ -5,9 +5,9 @@ import {
5
5
  AiModelSortMap,
6
6
  AiModelSourceEnum,
7
7
  AiProviderModelListItem,
8
+ EnabledAiModel,
8
9
  ToggleAiModelEnableParams,
9
10
  } from '@/types/aiModel';
10
- import { EnabledAiModel } from '@/types/aiProvider';
11
11
 
12
12
  import { AiModelSelectItem, NewAiModelItem, aiModels } from '../../schemas';
13
13
 
@@ -1,6 +1,7 @@
1
1
  import { and, asc, desc, eq } from 'drizzle-orm/expressions';
2
2
  import { isEmpty } from 'lodash-es';
3
3
 
4
+ import { DEFAULT_MODEL_PROVIDER_LIST } from '@/config/modelProviders';
4
5
  import { LobeChatDatabase } from '@/database/type';
5
6
  import { ModelProvider } from '@/libs/agent-runtime';
6
7
  import {
@@ -10,6 +11,7 @@ import {
10
11
  CreateAiProviderParams,
11
12
  UpdateAiProviderConfigParams,
12
13
  } from '@/types/aiProvider';
14
+ import { merge } from '@/utils/merge';
13
15
 
14
16
  import { AiProviderSelectItem, aiModels, aiProviders } from '../../schemas';
15
17
 
@@ -238,10 +240,13 @@ export class AiProviderModel {
238
240
  let runtimeConfig: Record<string, AiProviderRuntimeConfig> = {};
239
241
 
240
242
  for (const item of result) {
243
+ const builtin = DEFAULT_MODEL_PROVIDER_LIST.find((provider) => provider.id === item.id);
244
+
245
+ const userSettings = item.settings || {};
241
246
  runtimeConfig[item.id] = {
242
247
  fetchOnClient: typeof item.fetchOnClient === 'boolean' ? item.fetchOnClient : undefined,
243
248
  keyVaults: !!item.keyVaults ? await decrypt(item.keyVaults) : {},
244
- settings: item.settings || {},
249
+ settings: !!builtin ? merge(builtin.settings, userSettings) : userSettings,
245
250
  };
246
251
  }
247
252
 
@@ -0,0 +1,38 @@
1
+ import { Form } from '@lobehub/ui';
2
+ import { Switch } from 'antd';
3
+ import { memo } from 'react';
4
+
5
+ import { useAgentStore } from '@/store/agent';
6
+ import { agentSelectors } from '@/store/agent/slices/chat';
7
+ import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
8
+
9
+ const ControlsForm = memo(() => {
10
+ const [model, provider] = useAgentStore((s) => [
11
+ agentSelectors.currentAgentModel(s),
12
+ agentSelectors.currentAgentModelProvider(s),
13
+ ]);
14
+ const modelExtendControls = useAiInfraStore(
15
+ aiModelSelectors.modelExtendControls(model, provider),
16
+ );
17
+
18
+ return (
19
+ <Form
20
+ itemMinWidth={200}
21
+ items={modelExtendControls!.map((item: any) => ({
22
+ children: <Switch />,
23
+ label: item.key,
24
+ minWidth: undefined,
25
+ name: item.key,
26
+ }))}
27
+ itemsType={'flat'}
28
+ onValuesChange={(_, values) => {
29
+ console.log(values);
30
+ }}
31
+ size={'small'}
32
+ style={{ fontSize: 12 }}
33
+ variant={'pure'}
34
+ />
35
+ );
36
+ });
37
+
38
+ export default ControlsForm;
@@ -0,0 +1,40 @@
1
+ import { ActionIcon } from '@lobehub/ui';
2
+ import { Popover } from 'antd';
3
+ import { Settings2Icon } from 'lucide-react';
4
+ import { memo } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import { useIsMobile } from '@/hooks/useIsMobile';
9
+
10
+ import ControlsForm from './ControlsForm';
11
+
12
+ const ExtendControls = memo(() => {
13
+ const { t } = useTranslation('chat');
14
+
15
+ const isMobile = useIsMobile();
16
+ return (
17
+ <Flexbox style={{ marginInlineStart: -4 }}>
18
+ <Popover
19
+ arrow={false}
20
+ content={<ControlsForm />}
21
+ open
22
+ styles={{
23
+ body: {
24
+ minWidth: isMobile ? undefined : 250,
25
+ width: isMobile ? '100vw' : undefined,
26
+ },
27
+ }}
28
+ >
29
+ <ActionIcon
30
+ icon={Settings2Icon}
31
+ placement={'bottom'}
32
+ style={{ borderRadius: 20 }}
33
+ title={t('extendControls.title')}
34
+ />
35
+ </Popover>
36
+ </Flexbox>
37
+ );
38
+ });
39
+
40
+ export default ExtendControls;
@@ -0,0 +1,132 @@
1
+ import { ModelIcon } from '@lobehub/icons';
2
+ import { ActionIcon, Tooltip } from '@lobehub/ui';
3
+ import { Popover } from 'antd';
4
+ import { createStyles } from 'antd-style';
5
+ import { Brain, Settings2Icon } from 'lucide-react';
6
+ import { memo } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Center, Flexbox } from 'react-layout-kit';
9
+
10
+ import ModelSwitchPanel from '@/features/ModelSwitchPanel';
11
+ import { useIsMobile } from '@/hooks/useIsMobile';
12
+ import { useAgentStore } from '@/store/agent';
13
+ import { agentSelectors } from '@/store/agent/selectors';
14
+ import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
15
+
16
+ import ControlsForm from './ControlsForm';
17
+
18
+ const useStyles = createStyles(({ css, token, isDarkMode, cx }) => ({
19
+ container: css`
20
+ border-radius: 20px;
21
+ background: ${isDarkMode ? token.colorFillSecondary : token.colorFillTertiary};
22
+ `,
23
+ icon: cx(
24
+ 'model-switch',
25
+ css`
26
+ transition: scale 400ms cubic-bezier(0.215, 0.61, 0.355, 1);
27
+ `,
28
+ ),
29
+ model: css`
30
+ cursor: pointer;
31
+ border-radius: 8px;
32
+
33
+ :hover {
34
+ background: ${token.colorFillSecondary};
35
+ }
36
+
37
+ :active {
38
+ .model-switch {
39
+ scale: 0.8;
40
+ }
41
+ }
42
+ `,
43
+ modelWithControl: css`
44
+ border-radius: 20px;
45
+
46
+ :hover {
47
+ background: ${token.colorFillTertiary};
48
+ }
49
+ `,
50
+
51
+ video: css`
52
+ overflow: hidden;
53
+ border-radius: 8px;
54
+ `,
55
+ }));
56
+
57
+ const ModelSwitch = memo(() => {
58
+ const { t } = useTranslation('chat');
59
+ const { styles, cx } = useStyles();
60
+ const [model, provider, isLoading] = useAgentStore((s) => [
61
+ agentSelectors.currentAgentModel(s),
62
+ agentSelectors.currentAgentModelProvider(s),
63
+ agentSelectors.isAgentConfigLoading(s),
64
+ ]);
65
+
66
+ const isModelHasExtendControls = useAiInfraStore(
67
+ aiModelSelectors.isModelHasExtendControls(model, provider),
68
+ );
69
+
70
+ const isMobile = useIsMobile();
71
+
72
+ if (isLoading)
73
+ return (
74
+ <ActionIcon
75
+ icon={Brain}
76
+ placement={'bottom'}
77
+ style={{
78
+ cursor: 'not-allowed',
79
+ }}
80
+ title={t('ModelSwitch.title')}
81
+ />
82
+ );
83
+
84
+ return (
85
+ <Flexbox
86
+ align={'center'}
87
+ className={isModelHasExtendControls ? styles.container : ''}
88
+ horizontal
89
+ >
90
+ <ModelSwitchPanel>
91
+ <Center
92
+ className={cx(styles.model, isModelHasExtendControls && styles.modelWithControl)}
93
+ height={36}
94
+ width={36}
95
+ >
96
+ <Tooltip placement={'bottom'} title={[provider, model].join(' / ')}>
97
+ <div className={styles.icon}>
98
+ <ModelIcon model={model} size={22} />
99
+ </div>
100
+ </Tooltip>
101
+ </Center>
102
+ </ModelSwitchPanel>
103
+
104
+ {isModelHasExtendControls && (
105
+ <Flexbox style={{ marginInlineStart: -4 }}>
106
+ <Popover
107
+ arrow={false}
108
+ content={<ControlsForm />}
109
+ open
110
+ styles={{
111
+ body: {
112
+ minWidth: isMobile ? undefined : 200,
113
+ width: isMobile ? '100vw' : undefined,
114
+ },
115
+ }}
116
+ >
117
+ <ActionIcon
118
+ icon={Settings2Icon}
119
+ placement={'bottom'}
120
+ style={{ borderRadius: 20 }}
121
+ title={t('extendControls.title')}
122
+ />
123
+ </Popover>
124
+ </Flexbox>
125
+ )}
126
+ </Flexbox>
127
+ );
128
+ });
129
+
130
+ ModelSwitch.displayName = 'ModelSwitch';
131
+
132
+ export default ModelSwitch;
@@ -1,7 +1,7 @@
1
1
  import { ActionIcon } from '@lobehub/ui';
2
2
  import { Popover } from 'antd';
3
3
  import { useTheme } from 'antd-style';
4
- import { Settings2Icon } from 'lucide-react';
4
+ import { SlidersHorizontal } from 'lucide-react';
5
5
  import { memo, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
@@ -41,7 +41,7 @@ const Params = memo(() => {
41
41
  trigger={'click'}
42
42
  >
43
43
  <ActionIcon
44
- icon={Settings2Icon}
44
+ icon={SlidersHorizontal}
45
45
  placement={'bottom'}
46
46
  title={popoverOpen ? undefined : t('settingModel.params.title')}
47
47
  />
@@ -0,0 +1,15 @@
1
+ const ExaIcon = () => {
2
+ return (
3
+ <svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
4
+ <rect fill="white" height="24" width="24" />
5
+ <path
6
+ clipRule="evenodd"
7
+ d="M4 2H20V3.49254L13.1718 12L20 20.5075V22H4V2ZM12.1006 10.6234L17.6493 3.49254H6.5519L12.1006 10.6234ZM5.80014 5.27954V11.2537H10.4488L5.80014 5.27954ZM10.4488 12.7463H5.80014V18.7205L10.4488 12.7463ZM6.5519 20.5075L12.1006 13.3766L17.6493 20.5075H6.5519Z"
8
+ fill="#1F40ED"
9
+ fillRule="evenodd"
10
+ />
11
+ </svg>
12
+ );
13
+ };
14
+
15
+ export default ExaIcon;
@@ -0,0 +1,68 @@
1
+ import { Google } from '@lobehub/icons';
2
+ import { Icon } from '@lobehub/ui';
3
+ import { Switch } from 'antd';
4
+ import { Search } from 'lucide-react';
5
+ import { memo, useState } from 'react';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ import { useAgentStore } from '@/store/agent';
10
+ import { agentSelectors } from '@/store/agent/selectors';
11
+ import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
12
+
13
+ import ExaIcon from './ExaIcon';
14
+
15
+ interface SearchEngineIconProps {
16
+ icon?: string;
17
+ }
18
+
19
+ const SearchEngineIcon = ({ icon }: SearchEngineIconProps) => {
20
+ switch (icon) {
21
+ case 'google': {
22
+ return <Google.Color />;
23
+ }
24
+
25
+ case 'exa': {
26
+ return <ExaIcon />;
27
+ }
28
+
29
+ default: {
30
+ return <Icon icon={Search} size={{ fontSize: 16 }} />;
31
+ }
32
+ }
33
+ };
34
+
35
+ const ModelBuiltinSearch = memo(() => {
36
+ const { t } = useTranslation('chat');
37
+ const [model, provider, checked, updateAgentChatConfig] = useAgentStore((s) => [
38
+ agentSelectors.currentAgentModel(s),
39
+ agentSelectors.currentAgentModelProvider(s),
40
+ agentSelectors.currentAgentChatConfig(s).useModelBuiltinSearch,
41
+ s.updateAgentChatConfig,
42
+ ]);
43
+
44
+ const [isLoading, setLoading] = useState(false);
45
+ const modelCard = useAiInfraStore(aiModelSelectors.getEnabledModelById(model, provider));
46
+
47
+ return (
48
+ <Flexbox
49
+ align={'center'}
50
+ horizontal
51
+ justify={'space-between'}
52
+ onClick={async () => {
53
+ setLoading(true);
54
+ await updateAgentChatConfig({ useModelBuiltinSearch: !checked });
55
+ setLoading(false);
56
+ }}
57
+ padding={'8px 12px'}
58
+ style={{ cursor: 'pointer', userSelect: 'none' }}
59
+ >
60
+ <Flexbox align={'center'} gap={4} horizontal>
61
+ <SearchEngineIcon icon={modelCard?.settings?.searchProvider} />
62
+ {t('search.mode.useModelBuiltin')}
63
+ </Flexbox>
64
+ <Switch checked={checked} loading={isLoading} size={'small'} />
65
+ </Flexbox>
66
+ );
67
+ });
68
+ export default ModelBuiltinSearch;
@@ -0,0 +1,167 @@
1
+ import { DisconnectOutlined } from '@ant-design/icons';
2
+ import { Icon } from '@lobehub/ui';
3
+ import { Divider, Typography } from 'antd';
4
+ import { createStyles } from 'antd-style';
5
+ import { CheckIcon, SparklesIcon } from 'lucide-react';
6
+ import { ReactNode, memo } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Flexbox } from 'react-layout-kit';
9
+
10
+ import { useAgentStore } from '@/store/agent';
11
+ import { agentSelectors } from '@/store/agent/slices/chat';
12
+ import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
13
+ import { SearchMode } from '@/types/search';
14
+
15
+ import ModelBuiltinSearch from './ModelBuiltinSearch';
16
+
17
+ const { Text } = Typography;
18
+
19
+ interface NetworkOption {
20
+ description: string;
21
+ disable?: boolean;
22
+ icon: ReactNode;
23
+ label: string;
24
+ value: SearchMode;
25
+ }
26
+
27
+ const useStyles = createStyles(({ css, token }) => ({
28
+ check: css`
29
+ margin-inline-start: 12px;
30
+ font-size: 16px;
31
+ color: ${token.colorPrimary};
32
+ `,
33
+ content: css`
34
+ flex: 1;
35
+ width: 230px;
36
+ `,
37
+ description: css`
38
+ font-size: 12px;
39
+ color: ${token.colorTextSecondary};
40
+ `,
41
+ disable: css`
42
+ cursor: not-allowed;
43
+ opacity: 0.45;
44
+ `,
45
+ iconWrapper: css`
46
+ display: flex;
47
+ flex-shrink: 0;
48
+ align-items: center;
49
+ justify-content: center;
50
+
51
+ width: 32px;
52
+ height: 32px;
53
+ border-radius: 8px;
54
+
55
+ font-size: 16px;
56
+
57
+ background: ${token.colorFillQuaternary};
58
+ `,
59
+ option: css`
60
+ cursor: pointer;
61
+
62
+ align-items: center;
63
+
64
+ width: 100%;
65
+ padding-block: 8px;
66
+ padding-inline: 12px;
67
+ border-radius: 8px;
68
+
69
+ transition: background-color 0.2s;
70
+
71
+ &:hover {
72
+ background: ${token.colorFillTertiary};
73
+ }
74
+ `,
75
+ title: css`
76
+ margin-block-end: 2px;
77
+ font-size: 14px;
78
+ font-weight: 500;
79
+ color: ${token.colorText};
80
+ `,
81
+ }));
82
+
83
+ const Item = memo<NetworkOption>(({ value, description, icon, label, disable }) => {
84
+ const { t } = useTranslation('chat');
85
+ const { styles } = useStyles();
86
+ const [mode, updateAgentChatConfig] = useAgentStore((s) => [
87
+ agentSelectors.agentSearchMode(s),
88
+ s.updateAgentChatConfig,
89
+ ]);
90
+
91
+ return (
92
+ <Flexbox
93
+ className={styles.option}
94
+ gap={24}
95
+ horizontal
96
+ key={value}
97
+ onClick={() => updateAgentChatConfig({ searchMode: value })}
98
+ >
99
+ <Flexbox className={disable ? styles.disable : ''} gap={8} horizontal>
100
+ <div className={styles.iconWrapper}>{icon}</div>
101
+ <div className={styles.content}>
102
+ <div className={styles.title}>{label}</div>
103
+ <Text className={styles.description} type="secondary">
104
+ {disable ? t('search.mode.disable') : description}
105
+ </Text>
106
+ </div>
107
+ </Flexbox>
108
+ {mode === value && <Icon className={styles.check} icon={CheckIcon} />}
109
+ </Flexbox>
110
+ );
111
+ });
112
+
113
+ interface AINetworkSettingsProps {
114
+ providerSearch?: boolean;
115
+ }
116
+ const AINetworkSettings = memo<AINetworkSettingsProps>(() => {
117
+ const { t } = useTranslation('chat');
118
+ const [model, provider] = useAgentStore((s) => [
119
+ agentSelectors.currentAgentModel(s),
120
+ agentSelectors.currentAgentModelProvider(s),
121
+ ]);
122
+
123
+ const supportFC = useAiInfraStore(aiModelSelectors.isModelSupportToolUse(model, provider));
124
+ const isModelHasBuiltinSearchConfig = useAiInfraStore(
125
+ aiModelSelectors.isModelHasBuiltinSearchConfig(model, provider),
126
+ );
127
+
128
+ const options: NetworkOption[] = [
129
+ {
130
+ description: t('search.mode.off.desc'),
131
+ icon: <DisconnectOutlined />,
132
+ label: t('search.mode.off.title'),
133
+ value: 'off',
134
+ },
135
+ // 等应用层联网功能做好以后再开启
136
+ // {
137
+ // description: t('search.mode.on.desc'),
138
+ // icon: <WifiOutlined />,
139
+ // label: t('search.mode.on.title'),
140
+ // value: 'on',
141
+ // },
142
+ {
143
+ description: t('search.mode.auto.desc'),
144
+ disable: !supportFC,
145
+ icon: <Icon icon={SparklesIcon} />,
146
+ label: t('search.mode.auto.title'),
147
+ value: 'auto',
148
+ },
149
+ ];
150
+
151
+ return (
152
+ <Flexbox gap={8}>
153
+ {options.map((option) => (
154
+ <Item {...option} key={option.value} />
155
+ ))}
156
+
157
+ {isModelHasBuiltinSearchConfig && (
158
+ <>
159
+ <Divider style={{ margin: 0, paddingInline: 12 }} />
160
+ <ModelBuiltinSearch />
161
+ </>
162
+ )}
163
+ </Flexbox>
164
+ );
165
+ });
166
+
167
+ export default AINetworkSettings;