@lobehub/lobehub 2.0.0-next.1 → 2.0.0-next.3

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 (33) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/changelog/v1.json +18 -0
  3. package/package.json +1 -1
  4. package/packages/const/src/models.ts +13 -0
  5. package/packages/model-bank/src/aiModels/azure.ts +155 -0
  6. package/packages/model-bank/src/aiModels/bedrock.ts +44 -0
  7. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.ts +1 -1
  8. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +33 -3
  9. package/packages/model-runtime/src/core/parameterResolver.ts +3 -0
  10. package/packages/model-runtime/src/providers/azureOpenai/index.ts +2 -1
  11. package/src/app/(backend)/oidc/consent/route.ts +0 -1
  12. package/src/app/[variants]/(main)/settings/_layout/SettingsContent.tsx +0 -3
  13. package/src/features/AgentSetting/AgentPlugin/index.tsx +20 -12
  14. package/src/app/[variants]/(main)/settings/llm/ProviderList/Azure/index.tsx +0 -93
  15. package/src/app/[variants]/(main)/settings/llm/ProviderList/Bedrock/index.tsx +0 -70
  16. package/src/app/[variants]/(main)/settings/llm/ProviderList/Cloudflare/index.tsx +0 -39
  17. package/src/app/[variants]/(main)/settings/llm/ProviderList/Github/index.tsx +0 -52
  18. package/src/app/[variants]/(main)/settings/llm/ProviderList/HuggingFace/index.tsx +0 -52
  19. package/src/app/[variants]/(main)/settings/llm/ProviderList/Ollama/index.tsx +0 -20
  20. package/src/app/[variants]/(main)/settings/llm/ProviderList/OpenAI/index.tsx +0 -17
  21. package/src/app/[variants]/(main)/settings/llm/ProviderList/providers.tsx +0 -132
  22. package/src/app/[variants]/(main)/settings/llm/components/Checker.tsx +0 -118
  23. package/src/app/[variants]/(main)/settings/llm/components/ProviderConfig/index.tsx +0 -303
  24. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/CustomModelOption.tsx +0 -98
  25. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelConfigModal/Form.tsx +0 -104
  26. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelConfigModal/index.tsx +0 -77
  27. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelFetcher.tsx +0 -105
  28. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/Option.tsx +0 -68
  29. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/index.tsx +0 -146
  30. package/src/app/[variants]/(main)/settings/llm/const.ts +0 -20
  31. package/src/app/[variants]/(main)/settings/llm/features/Footer.tsx +0 -35
  32. package/src/app/[variants]/(main)/settings/llm/index.tsx +0 -30
  33. package/src/app/[variants]/(main)/settings/llm/type.ts +0 -5
@@ -1,303 +0,0 @@
1
- 'use client';
2
-
3
- import { ProviderCombine } from '@lobehub/icons';
4
- import {
5
- Form,
6
- type FormGroupItemType,
7
- type FormItemProps,
8
- Icon,
9
- Input,
10
- InputPassword,
11
- Tooltip,
12
- } from '@lobehub/ui';
13
- import { Switch } from 'antd';
14
- import { createStyles } from 'antd-style';
15
- import { debounce } from 'lodash-es';
16
- import { LockIcon } from 'lucide-react';
17
- import Link from 'next/link';
18
- import { ReactNode, memo } from 'react';
19
- import { Trans, useTranslation } from 'react-i18next';
20
- import { Center, Flexbox } from 'react-layout-kit';
21
- import urlJoin from 'url-join';
22
-
23
- import { useSyncSettings } from '@/app/[variants]/(main)/settings/hooks/useSyncSettings';
24
- import {
25
- KeyVaultsConfigKey,
26
- LLMProviderApiTokenKey,
27
- LLMProviderBaseUrlKey,
28
- LLMProviderConfigKey,
29
- LLMProviderModelListKey,
30
- } from '@/app/[variants]/(main)/settings/llm/const';
31
- import { FORM_STYLE } from '@/const/layoutTokens';
32
- import { AES_GCM_URL, BASE_PROVIDER_DOC_URL } from '@/const/url';
33
- import { isServerMode } from '@/const/version';
34
- import { useUserStore } from '@/store/user';
35
- import { keyVaultsConfigSelectors, modelConfigSelectors } from '@/store/user/selectors';
36
- import { ModelProviderCard } from '@/types/llm';
37
- import { GlobalLLMProviderKey } from '@/types/user/settings';
38
-
39
- import Checker from '../Checker';
40
- import ProviderModelListSelect from '../ProviderModelList';
41
-
42
- const useStyles = createStyles(({ css, prefixCls, responsive, token }) => ({
43
- aceGcm: css`
44
- padding-block: 0 !important;
45
- .${prefixCls}-form-item-label {
46
- display: none;
47
- }
48
- .${prefixCls}-form-item-control {
49
- width: 100%;
50
-
51
- font-size: 12px;
52
- color: ${token.colorTextSecondary};
53
- text-align: center;
54
-
55
- opacity: 0.66;
56
-
57
- transition: opacity 0.2s ${token.motionEaseInOut};
58
-
59
- &:hover {
60
- opacity: 1;
61
- }
62
- }
63
- `,
64
- form: css`
65
- .${prefixCls}-form-item-control:has(.${prefixCls}-input,.${prefixCls}-select) {
66
- flex: none;
67
- width: min(70%, 800px);
68
- min-width: min(70%, 800px) !important;
69
- }
70
- ${responsive.mobile} {
71
- width: 100%;
72
- min-width: unset !important;
73
- }
74
- .${prefixCls}-select-selection-overflow-item {
75
- font-size: 12px;
76
- }
77
- `,
78
- help: css`
79
- border-radius: 50%;
80
-
81
- font-size: 12px;
82
- font-weight: 500;
83
- color: ${token.colorTextDescription};
84
-
85
- background: ${token.colorFillTertiary};
86
-
87
- &:hover {
88
- color: ${token.colorText};
89
- background: ${token.colorFill};
90
- }
91
- `,
92
- }));
93
-
94
- export interface ProviderConfigProps extends Omit<ModelProviderCard, 'id' | 'chatModels'> {
95
- apiKeyItems?: FormItemProps[];
96
- canDeactivate?: boolean;
97
- checkerItem?: FormItemProps;
98
- className?: string;
99
- extra?: ReactNode;
100
- hideSwitch?: boolean;
101
- id: GlobalLLMProviderKey;
102
- modelList?: {
103
- azureDeployName?: boolean;
104
- notFoundContent?: ReactNode;
105
- placeholder?: string;
106
- showModelFetcher?: boolean;
107
- };
108
- showAceGcm?: boolean;
109
- title?: ReactNode;
110
- }
111
-
112
- const ProviderConfig = memo<ProviderConfigProps>(
113
- ({
114
- apiKeyItems,
115
- id,
116
- proxyUrl,
117
- showApiKey = true,
118
- checkModel,
119
- canDeactivate = true,
120
- checkerItem,
121
- modelList,
122
- title,
123
- defaultShowBrowserRequest,
124
- disableBrowserRequest,
125
- className,
126
- name,
127
- showAceGcm = true,
128
- showChecker = true,
129
- extra,
130
- }) => {
131
- const { t } = useTranslation('setting');
132
- const [form] = Form.useForm();
133
- const { cx, styles } = useStyles();
134
- const [
135
- toggleProviderEnabled,
136
- setSettings,
137
- enabled,
138
- isFetchOnClient,
139
- isProviderEndpointNotEmpty,
140
- isProviderApiKeyNotEmpty,
141
- ] = useUserStore((s) => [
142
- s.toggleProviderEnabled,
143
- s.setSettings,
144
- modelConfigSelectors.isProviderEnabled(id)(s),
145
- modelConfigSelectors.isProviderFetchOnClient(id)(s),
146
- keyVaultsConfigSelectors.isProviderEndpointNotEmpty(id)(s),
147
- keyVaultsConfigSelectors.isProviderApiKeyNotEmpty(id)(s),
148
- ]);
149
-
150
- useSyncSettings(form);
151
-
152
- const apiKeyItem: FormItemProps[] = !showApiKey
153
- ? []
154
- : (apiKeyItems ?? [
155
- {
156
- children: (
157
- <InputPassword
158
- autoComplete={'new-password'}
159
- placeholder={t(`llm.apiKey.placeholder`, { name })}
160
- />
161
- ),
162
- desc: t(`llm.apiKey.desc`, { name }),
163
- label: t(`llm.apiKey.title`),
164
- name: [KeyVaultsConfigKey, id, LLMProviderApiTokenKey],
165
- },
166
- ]);
167
-
168
- const aceGcmItem: FormItemProps = {
169
- children: (
170
- <>
171
- <Icon icon={LockIcon} style={{ marginRight: 4 }} />
172
- <Trans i18nKey="llm.aesGcm" ns={'setting'}>
173
- 您的秘钥与代理地址等将使用
174
- <Link href={AES_GCM_URL} style={{ marginInline: 4 }} target={'_blank'}>
175
- AES-GCM
176
- </Link>
177
- 加密算法进行加密
178
- </Trans>
179
- </>
180
- ),
181
- className: styles.aceGcm,
182
- minWidth: undefined,
183
- };
184
-
185
- const showEndpoint = !!proxyUrl;
186
-
187
- const formItems = [
188
- ...apiKeyItem,
189
- showEndpoint && {
190
- children: <Input allowClear placeholder={proxyUrl?.placeholder} />,
191
- desc: proxyUrl?.desc || t('llm.proxyUrl.desc'),
192
- label: proxyUrl?.title || t('llm.proxyUrl.title'),
193
- name: [KeyVaultsConfigKey, id, LLMProviderBaseUrlKey],
194
- },
195
- /*
196
- * Conditions to show Client Fetch Switch
197
- * 1. provider is not disabled browser request
198
- * 2. provider show browser request by default
199
- * 3. Provider allow to edit endpoint and the value of endpoint is not empty
200
- * 4. There is an apikey provided by user
201
- */
202
- !disableBrowserRequest &&
203
- (defaultShowBrowserRequest ||
204
- (showEndpoint && isProviderEndpointNotEmpty) ||
205
- (showApiKey && isProviderApiKeyNotEmpty)) && {
206
- children: (
207
- <Switch
208
- onChange={(enabled) => {
209
- setSettings({ [LLMProviderConfigKey]: { [id]: { fetchOnClient: enabled } } });
210
- }}
211
- value={isFetchOnClient}
212
- />
213
- ),
214
- desc: t('llm.fetchOnClient.desc'),
215
- label: t('llm.fetchOnClient.title'),
216
- minWidth: undefined,
217
- },
218
- {
219
- children: (
220
- <ProviderModelListSelect
221
- notFoundContent={modelList?.notFoundContent}
222
- placeholder={modelList?.placeholder ?? t('llm.modelList.placeholder')}
223
- provider={id}
224
- showAzureDeployName={modelList?.azureDeployName}
225
- showModelFetcher={modelList?.showModelFetcher}
226
- />
227
- ),
228
- desc: t('llm.modelList.desc'),
229
- label: t('llm.modelList.title'),
230
- name: [LLMProviderConfigKey, id, LLMProviderModelListKey],
231
- },
232
- showChecker
233
- ? (checkerItem ?? {
234
- children: <Checker model={checkModel!} provider={id} />,
235
- desc: t('llm.checker.desc'),
236
- label: t('llm.checker.title'),
237
- minWidth: undefined,
238
- })
239
- : undefined,
240
- showAceGcm && isServerMode && aceGcmItem,
241
- ].filter(Boolean) as FormItemProps[];
242
-
243
- /* ↓ cloud slot ↓ */
244
-
245
- /* ↑ cloud slot ↑ */
246
-
247
- const model: FormGroupItemType = {
248
- children: formItems,
249
-
250
- defaultActive: canDeactivate ? enabled : undefined,
251
-
252
- extra: (
253
- <Flexbox align={'center'} gap={8} horizontal>
254
- {extra}
255
- <Tooltip title={t('llm.helpDoc')}>
256
- <Link
257
- href={urlJoin(BASE_PROVIDER_DOC_URL, id)}
258
- onClick={(e) => e.stopPropagation()}
259
- target={'_blank'}
260
- >
261
- <Center className={styles.help} height={20} width={20}>
262
- ?
263
- </Center>
264
- </Link>
265
- </Tooltip>
266
- {canDeactivate ? (
267
- <Switch
268
- onChange={(enabled) => {
269
- toggleProviderEnabled(id, enabled);
270
- }}
271
- value={enabled}
272
- />
273
- ) : undefined}
274
- </Flexbox>
275
- ),
276
- title: (
277
- <Flexbox
278
- align={'center'}
279
- horizontal
280
- style={{
281
- height: 24,
282
- maxHeight: 24,
283
- ...(enabled ? {} : { filter: 'grayscale(100%)', maxHeight: 24, opacity: 0.66 }),
284
- }}
285
- >
286
- {title ?? <ProviderCombine provider={id} size={24} />}
287
- </Flexbox>
288
- ),
289
- };
290
-
291
- return (
292
- <Form
293
- className={cx(styles.form, className)}
294
- form={form}
295
- items={[model]}
296
- onValuesChange={debounce(setSettings, 100)}
297
- {...FORM_STYLE}
298
- />
299
- );
300
- },
301
- );
302
-
303
- export default ProviderConfig;
@@ -1,98 +0,0 @@
1
- import { ModelIcon } from '@lobehub/icons';
2
- import { ActionIcon, Icon, Text } from '@lobehub/ui';
3
- import { App } from 'antd';
4
- import isEqual from 'fast-deep-equal';
5
- import { LucideArrowRight, LucideSettings, LucideTrash2 } from 'lucide-react';
6
- import { memo } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
- import { Flexbox } from 'react-layout-kit';
9
-
10
- import { ModelInfoTags } from '@/components/ModelSelect';
11
- import { useUserStore } from '@/store/user';
12
- import { modelConfigSelectors, modelProviderSelectors } from '@/store/user/selectors';
13
- import { GlobalLLMProviderKey } from '@/types/user/settings';
14
-
15
- interface CustomModelOptionProps {
16
- id: string;
17
- provider: GlobalLLMProviderKey;
18
- }
19
-
20
- const CustomModelOption = memo<CustomModelOptionProps>(({ id, provider }) => {
21
- const { t } = useTranslation('common');
22
- const { t: s } = useTranslation('setting');
23
- const { modal } = App.useApp();
24
-
25
- const [dispatchCustomModelCards, toggleEditingCustomModelCard, removeEnabledModels] =
26
- useUserStore((s) => [
27
- s.dispatchCustomModelCards,
28
- s.toggleEditingCustomModelCard,
29
- s.removeEnabledModels,
30
- ]);
31
-
32
- const modelCard = useUserStore(
33
- modelConfigSelectors.getCustomModelCard({ id, provider }),
34
- isEqual,
35
- );
36
-
37
- const isEnabled = useUserStore(
38
- (s) => modelProviderSelectors.getEnableModelsById(provider)(s)?.includes(id),
39
- isEqual,
40
- );
41
-
42
- return (
43
- <Flexbox align={'center'} distribution={'space-between'} gap={8} horizontal>
44
- <Flexbox align={'center'} gap={8} horizontal style={{ flex: 1, width: '70%' }}>
45
- <ModelIcon model={id} size={32} />
46
- <Flexbox direction={'vertical'} style={{ flex: 1, overflow: 'hidden' }}>
47
- <Flexbox align={'center'} gap={8} horizontal>
48
- <Text ellipsis>{modelCard?.displayName || id}</Text>
49
- <ModelInfoTags id={id} {...modelCard} isCustom />
50
- </Flexbox>
51
- <Text ellipsis style={{ fontSize: 12, marginTop: '4px' }} type={'secondary'}>
52
- {id}
53
- {!!modelCard?.deploymentName && (
54
- <>
55
- <Icon icon={LucideArrowRight} />
56
- {modelCard?.deploymentName}
57
- </>
58
- )}
59
- </Text>
60
- </Flexbox>
61
- </Flexbox>
62
-
63
- <Flexbox horizontal>
64
- <ActionIcon
65
- icon={LucideSettings}
66
- onClick={async (e) => {
67
- e.stopPropagation();
68
- toggleEditingCustomModelCard({ id, provider });
69
- }}
70
- title={s('llm.customModelCards.config')}
71
- />
72
- <ActionIcon
73
- icon={LucideTrash2}
74
- onClick={async (e) => {
75
- e.stopPropagation();
76
- e.preventDefault();
77
-
78
- await modal.confirm({
79
- centered: true,
80
- content: s('llm.customModelCards.confirmDelete'),
81
- okButtonProps: { danger: true },
82
- onOk: async () => {
83
- // delete model and deactivate id
84
- await dispatchCustomModelCards(provider, { id, type: 'delete' });
85
- await removeEnabledModels(provider, id);
86
- },
87
- type: 'warning',
88
- });
89
- }}
90
- style={isEnabled ? { marginRight: '10px' } : {}}
91
- title={t('delete')}
92
- />
93
- </Flexbox>
94
- </Flexbox>
95
- );
96
- });
97
-
98
- export default CustomModelOption;
@@ -1,104 +0,0 @@
1
- import { Input } from '@lobehub/ui';
2
- import { Checkbox, Form, FormInstance } from 'antd';
3
- import { memo, useEffect } from 'react';
4
- import { useTranslation } from 'react-i18next';
5
-
6
- import MaxTokenSlider from '@/components/MaxTokenSlider';
7
- import { useIsMobile } from '@/hooks/useIsMobile';
8
- import { ChatModelCard } from '@/types/llm';
9
-
10
- interface ModelConfigFormProps {
11
- initialValues?: ChatModelCard;
12
- onFormInstanceReady: (instance: FormInstance) => void;
13
- showAzureDeployName?: boolean;
14
- }
15
-
16
- const ModelConfigForm = memo<ModelConfigFormProps>(
17
- ({ showAzureDeployName, onFormInstanceReady, initialValues }) => {
18
- const { t } = useTranslation('setting');
19
-
20
- const [formInstance] = Form.useForm();
21
-
22
- const isMobile = useIsMobile();
23
-
24
- useEffect(() => {
25
- onFormInstanceReady(formInstance);
26
- }, []);
27
-
28
- return (
29
- <div
30
- onClick={(e) => {
31
- e.stopPropagation();
32
- }}
33
- onKeyDown={(e) => {
34
- e.stopPropagation();
35
- }}
36
- >
37
- <Form
38
- colon={false}
39
- form={formInstance}
40
- initialValues={initialValues}
41
- labelCol={{ span: 4 }}
42
- style={{ marginTop: 16 }}
43
- wrapperCol={isMobile ? { span: 18 } : { offset: 1, span: 18 }}
44
- >
45
- <Form.Item
46
- extra={t('llm.customModelCards.modelConfig.id.extra')}
47
- label={t('llm.customModelCards.modelConfig.id.title')}
48
- name={'id'}
49
- >
50
- <Input placeholder={t('llm.customModelCards.modelConfig.id.placeholder')} />
51
- </Form.Item>
52
- {showAzureDeployName && (
53
- <Form.Item
54
- extra={t('llm.customModelCards.modelConfig.azureDeployName.extra')}
55
- label={t('llm.customModelCards.modelConfig.azureDeployName.title')}
56
- name={'deploymentName'}
57
- >
58
- <Input
59
- placeholder={t('llm.customModelCards.modelConfig.azureDeployName.placeholder')}
60
- />
61
- </Form.Item>
62
- )}
63
- <Form.Item
64
- label={t('llm.customModelCards.modelConfig.displayName.title')}
65
- name={'displayName'}
66
- >
67
- <Input placeholder={t('llm.customModelCards.modelConfig.displayName.placeholder')} />
68
- </Form.Item>
69
- <Form.Item
70
- label={t('llm.customModelCards.modelConfig.tokens.title')}
71
- name={'contextWindowTokens'}
72
- >
73
- <MaxTokenSlider />
74
- </Form.Item>
75
- <Form.Item
76
- extra={t('llm.customModelCards.modelConfig.functionCall.extra')}
77
- label={t('llm.customModelCards.modelConfig.functionCall.title')}
78
- name={'functionCall'}
79
- valuePropName={'checked'}
80
- >
81
- <Checkbox />
82
- </Form.Item>
83
- <Form.Item
84
- extra={t('llm.customModelCards.modelConfig.vision.extra')}
85
- label={t('llm.customModelCards.modelConfig.vision.title')}
86
- name={'vision'}
87
- valuePropName={'checked'}
88
- >
89
- <Checkbox />
90
- </Form.Item>
91
- <Form.Item
92
- extra={t('llm.customModelCards.modelConfig.files.extra')}
93
- label={t('llm.customModelCards.modelConfig.files.title')}
94
- name={'files'}
95
- valuePropName={'checked'}
96
- >
97
- <Checkbox />
98
- </Form.Item>
99
- </Form>
100
- </div>
101
- );
102
- },
103
- );
104
- export default ModelConfigForm;
@@ -1,77 +0,0 @@
1
- import { Button, Modal } from '@lobehub/ui';
2
- import { FormInstance } from 'antd';
3
- import isEqual from 'fast-deep-equal';
4
- import { memo, useState } from 'react';
5
- import { useTranslation } from 'react-i18next';
6
-
7
- import { useUserStore } from '@/store/user';
8
- import { modelConfigSelectors } from '@/store/user/selectors';
9
-
10
- import ModelConfigForm from './Form';
11
-
12
- interface ModelConfigModalProps {
13
- provider?: string;
14
- showAzureDeployName?: boolean;
15
- }
16
-
17
- const ModelConfigModal = memo<ModelConfigModalProps>(({ showAzureDeployName, provider }) => {
18
- const { t } = useTranslation('setting');
19
- const { t: tc } = useTranslation('common');
20
- const [formInstance, setFormInstance] = useState<FormInstance>();
21
-
22
- const [open, id, editingProvider, dispatchCustomModelCards, toggleEditingCustomModelCard] =
23
- useUserStore((s) => [
24
- !!s.editingCustomCardModel && provider === s.editingCustomCardModel?.provider,
25
- s.editingCustomCardModel?.id,
26
- s.editingCustomCardModel?.provider,
27
- s.dispatchCustomModelCards,
28
- s.toggleEditingCustomModelCard,
29
- ]);
30
-
31
- const modelCard = useUserStore(
32
- modelConfigSelectors.getCustomModelCard({ id, provider: editingProvider }),
33
- isEqual,
34
- );
35
-
36
- const closeModal = () => {
37
- toggleEditingCustomModelCard(undefined);
38
- };
39
-
40
- return (
41
- <Modal
42
- destroyOnHidden
43
- footer={[
44
- <Button key="cancel" onClick={closeModal}>
45
- {tc('cancel')}
46
- </Button>,
47
- <Button
48
- key="ok"
49
- onClick={() => {
50
- if (!editingProvider || !id || !formInstance) return;
51
- const data = formInstance.getFieldsValue();
52
-
53
- dispatchCustomModelCards(editingProvider as any, { id, type: 'update', value: data });
54
-
55
- closeModal();
56
- }}
57
- style={{ marginInlineStart: '16px' }}
58
- type="primary"
59
- >
60
- {tc('ok')}
61
- </Button>,
62
- ]}
63
- maskClosable
64
- onCancel={closeModal}
65
- open={open}
66
- title={t('llm.customModelCards.modelConfig.modalTitle')}
67
- zIndex={1251} // Select is 1150
68
- >
69
- <ModelConfigForm
70
- initialValues={modelCard}
71
- onFormInstanceReady={setFormInstance}
72
- showAzureDeployName={showAzureDeployName}
73
- />
74
- </Modal>
75
- );
76
- });
77
- export default ModelConfigModal;
@@ -1,105 +0,0 @@
1
- import { ActionIcon, Icon, Text, Tooltip } from '@lobehub/ui';
2
- import { createStyles } from 'antd-style';
3
- import dayjs from 'dayjs';
4
- import isEqual from 'fast-deep-equal';
5
- import { CircleX, LucideLoaderCircle, LucideRefreshCcwDot } from 'lucide-react';
6
- import { memo } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
- import { Flexbox } from 'react-layout-kit';
9
-
10
- import { useUserStore } from '@/store/user';
11
- import {
12
- modelConfigSelectors,
13
- modelProviderSelectors,
14
- settingsSelectors,
15
- } from '@/store/user/selectors';
16
- import { GlobalLLMProviderKey } from '@/types/user/settings';
17
-
18
- const useStyles = createStyles(({ css, token }) => ({
19
- hover: css`
20
- cursor: pointer;
21
-
22
- padding-block: 4px;
23
- padding-inline: 8px;
24
- border-radius: ${token.borderRadius}px;
25
-
26
- transition: all 0.2s ease-in-out;
27
-
28
- &:hover {
29
- color: ${token.colorText};
30
- background-color: ${token.colorFillSecondary};
31
- }
32
- `,
33
- }));
34
-
35
- interface ModelFetcherProps {
36
- provider: GlobalLLMProviderKey;
37
- }
38
-
39
- const ModelFetcher = memo<ModelFetcherProps>(({ provider }) => {
40
- const { styles } = useStyles();
41
- const { t } = useTranslation('setting');
42
- const [useFetchProviderModelList, clearObtainedModels] = useUserStore((s) => [
43
- s.useFetchProviderModelList,
44
- s.clearObtainedModels,
45
- s.setModelProviderConfig,
46
- ]);
47
- const enabledAutoFetch = useUserStore(modelConfigSelectors.isAutoFetchModelsEnabled(provider));
48
- const latestFetchTime = useUserStore(
49
- (s) => settingsSelectors.providerConfig(provider)(s)?.latestFetchTime,
50
- );
51
- const totalModels = useUserStore(
52
- (s) => modelProviderSelectors.getModelCardsById(provider)(s).length,
53
- );
54
-
55
- const remoteModels = useUserStore(
56
- modelProviderSelectors.remoteProviderModelCards(provider),
57
- isEqual,
58
- );
59
-
60
- const { mutate, isValidating } = useFetchProviderModelList(provider, enabledAutoFetch);
61
-
62
- return (
63
- <Text style={{ fontSize: 12 }} type={'secondary'}>
64
- <Flexbox align={'center'} gap={0} horizontal justify={'space-between'}>
65
- <div style={{ display: 'flex', lineHeight: '24px' }}>
66
- {t('llm.modelList.total', { count: totalModels })}
67
- {remoteModels && remoteModels.length > 0 && (
68
- <ActionIcon
69
- icon={CircleX}
70
- onClick={() => clearObtainedModels(provider)}
71
- size={'small'}
72
- title={t('llm.fetcher.clear')}
73
- />
74
- )}
75
- </div>
76
- <Tooltip
77
- styles={{ root: { pointerEvents: 'none' } }}
78
- title={
79
- latestFetchTime
80
- ? t('llm.fetcher.latestTime', {
81
- time: dayjs(latestFetchTime).format('YYYY-MM-DD HH:mm:ss'),
82
- })
83
- : t('llm.fetcher.noLatestTime')
84
- }
85
- >
86
- <Flexbox
87
- align={'center'}
88
- className={styles.hover}
89
- gap={4}
90
- horizontal
91
- onClick={() => mutate()}
92
- >
93
- <Icon
94
- icon={isValidating ? LucideLoaderCircle : LucideRefreshCcwDot}
95
- size={'small'}
96
- spin={isValidating}
97
- />
98
- <div>{isValidating ? t('llm.fetcher.fetching') : t('llm.fetcher.fetch')}</div>
99
- </Flexbox>
100
- </Tooltip>
101
- </Flexbox>
102
- </Text>
103
- );
104
- });
105
- export default ModelFetcher;