|
| 1 | +import { ActionIcon } from '@lobehub/ui'; |
1 | 2 | import { Select } from 'antd';
|
2 | 3 | import { css, cx } from 'antd-style';
|
3 | 4 | import isEqual from 'fast-deep-equal';
|
| 5 | +import { RotateCwIcon } from 'lucide-react'; |
4 | 6 | import { memo } from 'react';
|
| 7 | +import { useTranslation } from 'react-i18next'; |
| 8 | +import { Flexbox } from 'react-layout-kit'; |
5 | 9 |
|
6 |
| -import { filterEnabledModels } from '@/config/modelProviders'; |
7 | 10 | import { useGlobalStore } from '@/store/global';
|
8 | 11 | import { modelConfigSelectors, modelProviderSelectors } from '@/store/global/selectors';
|
9 | 12 | import { GlobalLLMProviderKey } from '@/types/settings';
|
10 | 13 |
|
11 |
| -import CustomModelOption from './CustomModelOption'; |
12 | 14 | import OptionRender from './Option';
|
13 | 15 |
|
14 |
| -const popup = css` |
15 |
| - &.ant-select-dropdown { |
16 |
| - .ant-select-item-option-selected { |
17 |
| - font-weight: normal; |
| 16 | +const styles = { |
| 17 | + popup: css` |
| 18 | + &.ant-select-dropdown { |
| 19 | + .ant-select-item-option-selected { |
| 20 | + font-weight: normal; |
| 21 | + } |
18 | 22 | }
|
19 |
| - } |
20 |
| -`; |
| 23 | + `, |
| 24 | + reset: css` |
| 25 | + position: absolute; |
| 26 | + top: 50%; |
| 27 | + transform: translateY(-50%); |
| 28 | + z-index: 20; |
| 29 | + inset-inline-end: 28px; |
| 30 | + `, |
| 31 | +}; |
21 | 32 |
|
22 | 33 | interface CustomModelSelectProps {
|
23 |
| - onChange?: (value: string[]) => void; |
24 | 34 | placeholder?: string;
|
25 |
| - provider: string; |
26 |
| - value?: string[]; |
| 35 | + provider: GlobalLLMProviderKey; |
27 | 36 | }
|
28 | 37 |
|
29 |
| -const ProviderModelListSelect = memo<CustomModelSelectProps>( |
30 |
| - ({ provider, placeholder, onChange }) => { |
31 |
| - const providerCard = useGlobalStore( |
32 |
| - (s) => modelProviderSelectors.providerModelList(s).find((s) => s.id === provider), |
33 |
| - isEqual, |
34 |
| - ); |
35 |
| - const providerConfig = useGlobalStore((s) => |
36 |
| - modelConfigSelectors.providerConfig(provider as GlobalLLMProviderKey)(s), |
37 |
| - ); |
| 38 | +const ProviderModelListSelect = memo<CustomModelSelectProps>(({ provider, placeholder }) => { |
| 39 | + const { t } = useTranslation('common'); |
| 40 | + const { t: transSetting } = useTranslation('setting'); |
| 41 | + const chatModelCards = useGlobalStore(modelConfigSelectors.providerModelCards(provider), isEqual); |
| 42 | + const [setModelProviderConfig, dispatchCustomModelCards] = useGlobalStore((s) => [ |
| 43 | + s.setModelProviderConfig, |
| 44 | + s.dispatchCustomModelCards, |
| 45 | + ]); |
| 46 | + const defaultEnableModel = useGlobalStore( |
| 47 | + modelProviderSelectors.defaultEnabledProviderModels(provider), |
| 48 | + isEqual, |
| 49 | + ); |
| 50 | + const enabledModels = useGlobalStore( |
| 51 | + modelConfigSelectors.providerEnableModels(provider), |
| 52 | + isEqual, |
| 53 | + ); |
| 54 | + const showReset = !!enabledModels && !isEqual(defaultEnableModel, enabledModels); |
38 | 55 |
|
39 |
| - const defaultEnableModel = providerCard ? filterEnabledModels(providerCard) : []; |
40 |
| - |
41 |
| - const chatModels = providerCard?.chatModels || []; |
42 |
| - |
43 |
| - return ( |
| 56 | + return ( |
| 57 | + <div style={{ position: 'relative' }}> |
| 58 | + <div className={cx(styles.reset)}> |
| 59 | + {showReset && ( |
| 60 | + <ActionIcon |
| 61 | + icon={RotateCwIcon} |
| 62 | + onClick={() => { |
| 63 | + setModelProviderConfig(provider, { enabledModels: null }); |
| 64 | + }} |
| 65 | + size={'small'} |
| 66 | + title={t('reset')} |
| 67 | + /> |
| 68 | + )} |
| 69 | + </div> |
44 | 70 | <Select<string[]>
|
45 | 71 | allowClear
|
46 |
| - defaultValue={defaultEnableModel} |
47 | 72 | mode="tags"
|
48 |
| - onChange={(value) => { |
49 |
| - onChange?.(value.filter(Boolean)); |
| 73 | + onChange={(value, options) => { |
| 74 | + setModelProviderConfig(provider, { enabledModels: value.filter(Boolean) }); |
| 75 | + |
| 76 | + // if there is a new model, add it to `customModelCards` |
| 77 | + options.forEach((option: { label?: string; value?: string }, index: number) => { |
| 78 | + // if is a known model, it should have value |
| 79 | + // if is an unknown model, the option will be {} |
| 80 | + if (option.value) return; |
| 81 | + |
| 82 | + const modelId = value[index]; |
| 83 | + |
| 84 | + dispatchCustomModelCards(provider, { |
| 85 | + modelCard: { id: modelId }, |
| 86 | + type: 'add', |
| 87 | + }); |
| 88 | + }); |
50 | 89 | }}
|
| 90 | + open |
51 | 91 | optionFilterProp="label"
|
52 | 92 | optionRender={({ label, value }) => {
|
53 |
| - console.log(value); |
54 | 93 | // model is in the chatModels
|
55 |
| - if (chatModels.some((c) => c.id === value)) |
56 |
| - return <OptionRender displayName={label as string} id={value as string} />; |
| 94 | + if (chatModelCards.some((c) => c.id === value)) |
| 95 | + return ( |
| 96 | + <OptionRender |
| 97 | + displayName={label as string} |
| 98 | + id={value as string} |
| 99 | + provider={provider} |
| 100 | + /> |
| 101 | + ); |
57 | 102 |
|
58 |
| - // model is user defined in client |
59 |
| - return <CustomModelOption displayName={label as string} id={value as string} />; |
| 103 | + // model is defined by user in client |
| 104 | + return ( |
| 105 | + <Flexbox align={'center'} gap={8} horizontal> |
| 106 | + {transSetting('llm.customModelCards.addNew', { id: value })} |
| 107 | + </Flexbox> |
| 108 | + ); |
60 | 109 | }}
|
61 |
| - options={chatModels.map((model) => ({ |
| 110 | + options={chatModelCards.map((model) => ({ |
62 | 111 | label: model.displayName || model.id,
|
63 | 112 | value: model.id,
|
64 | 113 | }))}
|
65 | 114 | placeholder={placeholder}
|
66 |
| - popupClassName={cx(popup)} |
67 |
| - value={providerConfig?.enabledModels.filter(Boolean)} |
| 115 | + popupClassName={cx(styles.popup)} |
| 116 | + value={enabledModels ?? defaultEnableModel} |
68 | 117 | />
|
69 |
| - ); |
70 |
| - }, |
71 |
| -); |
| 118 | + </div> |
| 119 | + ); |
| 120 | +}); |
72 | 121 |
|
73 | 122 | export default ProviderModelListSelect;
|
0 commit comments