Skip to content

Commit

Permalink
✨ feat: support display model list
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed Apr 10, 2024
1 parent cef3f8c commit e59635f
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 66 deletions.
6 changes: 3 additions & 3 deletions src/app/settings/llm/OpenAI/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useGlobalStore } from '@/store/global';
import { modelProviderSelectors } from '@/store/global/selectors';

import Checker from '../components/Checker';
import CustomModelSelect from '../components/CustomModelList';
import ProviderConfig from '../components/ProviderConfig';
import { LLMProviderConfigKey } from '../const';

Expand Down Expand Up @@ -66,10 +67,9 @@ const LLM = memo(() => {
},
{
children: (
<Input.TextArea
allowClear
<CustomModelSelect
placeholder={t('llm.openai.customModelName.placeholder')}
style={{ height: 100 }}
provider={'openai'}
/>
),
desc: t('llm.openai.customModelName.desc'),
Expand Down
28 changes: 28 additions & 0 deletions src/app/settings/llm/components/CustomModelList/Option.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Typography } from 'antd';
import isEqual from 'fast-deep-equal';
import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';

import ModelIcon from '@/components/ModelIcon';
import { ModelInfoTags } from '@/components/ModelSelect';
import { useGlobalStore } from '@/store/global';
import { modelProviderSelectors } from '@/store/global/slices/settings/selectors';

export const OptionRender = memo<{ displayName: string; id: string }>(({ displayName, id: id }) => {
const model = useGlobalStore((s) => modelProviderSelectors.modelCardById(id)(s), isEqual);

return (
<Flexbox align={'center'} gap={8} horizontal>
<ModelIcon model={id} size={32} />
<Flexbox>
<Flexbox align={'center'} gap={8} horizontal>
{displayName}
<ModelInfoTags directionReverse placement={'top'} {...model!} />
</Flexbox>
<Typography.Text style={{ fontSize: 12 }} type={'secondary'}>
{id}
</Typography.Text>
</Flexbox>
</Flexbox>
);
});
51 changes: 51 additions & 0 deletions src/app/settings/llm/components/CustomModelList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Select } from 'antd';
import { css, cx } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { memo } from 'react';

import { useGlobalStore } from '@/store/global';
import { modelProviderSelectors } from '@/store/global/selectors';

import { OptionRender } from './Option';

const popup = css`
&.ant-select-dropdown {
.ant-select-item-option-selected {
font-weight: normal;
}
}
`;

interface CustomModelSelectProps {
placeholder?: string;
provider: string;
}

const CustomModelSelect = memo<CustomModelSelectProps>(({ provider, placeholder }) => {
const providerCard = useGlobalStore(
(s) => modelProviderSelectors.modelSelectList(s).find((s) => s.id === provider),
isEqual,
);
const defaultEnableModel = providerCard?.chatModels.filter((v) => !v.hidden).map((m) => m.id);

return (
<Select
allowClear
defaultValue={defaultEnableModel}
mode="tags"
optionFilterProp="label"
optionRender={({ label, value }) => (
<OptionRender displayName={label as string} id={value as string} />
)}
options={providerCard?.chatModels.map((model) => ({
label: model.displayName || model.id,
value: model.id,
}))}
placeholder={placeholder}
popupClassName={cx(popup)}
popupMatchSelectWidth={false}
/>
);
});

export default CustomModelSelect;
13 changes: 7 additions & 6 deletions src/app/settings/llm/components/ProviderConfig/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { modelProviderSelectors } from '@/store/global/selectors';
import { GlobalLLMProviderKey } from '@/types/settings';

import Checker from '../Checker';
import CustomModelSelect from '../CustomModelList';

interface ProviderConfigProps {
canDeactivate?: boolean;
Expand All @@ -33,7 +34,7 @@ interface ProviderConfigProps {
const ProviderConfig = memo<ProviderConfigProps>(
({
provider,
showCustomModelName,
showCustomModelName = true,
showEndpoint,
showApiKey = true,
checkModel,
Expand Down Expand Up @@ -74,14 +75,13 @@ const ProviderConfig = memo<ProviderConfigProps>(
},
showCustomModelName && {
children: (
<Input.TextArea
allowClear
<CustomModelSelect
placeholder={t(`llm.${provider}.customModelName.placeholder` as any)}
style={{ height: 100 }}
provider={provider}
/>
),
desc: t(`llm.${provider}.customModelName.desc` as any),
label: t(`llm.${provider}.customModelName.title` as any),
desc: t('llm.modelList.desc'),
label: t('llm.modelList.title'),
name: [LLMProviderConfigKey, provider, LLMProviderCustomModelKey],
},
checkerItem ?? {
Expand Down Expand Up @@ -113,6 +113,7 @@ const ProviderConfig = memo<ProviderConfigProps>(
items={[model]}
onValuesChange={debounce(setSettings, 100)}
{...FORM_STYLE}
itemMinWidth={'max(50%,400px)'}
/>
);
},
Expand Down
9 changes: 6 additions & 3 deletions src/components/ModelIcon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ interface ModelProviderIconProps {
size?: number;
}

const ModelIcon = memo<ModelProviderIconProps>(({ model, size = 12 }) => {
if (!model) return;
const ModelIcon = memo<ModelProviderIconProps>(({ model: originModel, size = 12 }) => {
if (!originModel) return;

// lower case the origin model so to better match more model id case
const model = originModel.toLowerCase();

if (model.startsWith('gpt-3')) return <OpenAI.Avatar size={size} type={'gpt3'} />;
if (model.startsWith('gpt-4')) return <OpenAI.Avatar size={size} type={'gpt4'} />;
Expand All @@ -41,7 +44,7 @@ const ModelIcon = memo<ModelProviderIconProps>(({ model, size = 12 }) => {
return <Baichuan.Avatar background={Baichuan.colorPrimary} size={size} />;
if (model.includes('mistral') || model.includes('mixtral')) return <Mistral.Avatar size={size} />;
if (model.includes('pplx')) return <Perplexity.Avatar size={size} />;
if (model.startsWith('yi-')) return <Yi.Avatar size={size} />;
if (model.includes('yi-')) return <Yi.Avatar size={size} />;
});

export default ModelIcon;
119 changes: 65 additions & 54 deletions src/components/ModelSelect/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import { rgba } from 'polished';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Center, Flexbox } from 'react-layout-kit';
import ModelIcon from 'src/components/ModelIcon';
import ModelProviderIcon from 'src/components/ModelProviderIcon';

import { ChatModelCard } from '@/types/llm';

import ModelIcon from '../ModelIcon';
import ModelProviderIcon from '../ModelProviderIcon';

const useStyles = createStyles(({ css, token }) => ({
custom: css`
width: 36px;
Expand Down Expand Up @@ -56,69 +57,79 @@ const useStyles = createStyles(({ css, token }) => ({
`,
}));

interface ModelInfoTagsProps extends ChatModelCard {
directionReverse?: boolean;
placement?: 'top' | 'right';
}
export const ModelInfoTags = memo<ModelInfoTagsProps>(
({ directionReverse, placement = 'right', ...model }) => {
const { t } = useTranslation('common');
const { styles, cx } = useStyles();

return (
<Flexbox direction={directionReverse ? 'horizontal-reverse' : 'horizontal'} gap={4}>
{model.files && (
<Tooltip placement={placement} title={t('ModelSelect.featureTag.file')}>
<div className={cx(styles.tag, styles.tagGreen)}>
<Icon icon={LucidePaperclip} />
</div>
</Tooltip>
)}
{model.vision && (
<Tooltip placement={placement} title={t('ModelSelect.featureTag.vision')}>
<div className={cx(styles.tag, styles.tagGreen)}>
<Icon icon={LucideEye} />
</div>
</Tooltip>
)}
{model.functionCall && (
<Tooltip
overlayStyle={{ maxWidth: 'unset' }}
placement={placement}
title={t('ModelSelect.featureTag.functionCall')}
>
<div className={cx(styles.tag, styles.tagBlue)}>
<Icon icon={ToyBrick} />
</div>
</Tooltip>
)}
{model.tokens && (
<Tooltip
overlayStyle={{ maxWidth: 'unset' }}
placement={placement}
title={t('ModelSelect.featureTag.tokens', {
tokens: numeral(model.tokens).format('0,0'),
})}
>
<Center className={styles.token}>{Math.floor(model.tokens / 1000)}K</Center>
</Tooltip>
)}
{model.isCustom && (
<Tooltip
overlayStyle={{ maxWidth: 300 }}
placement={placement}
title={t('ModelSelect.featureTag.custom')}
>
<Center className={styles.custom}>DIY</Center>
</Tooltip>
)}
</Flexbox>
);
},
);

interface ModelItemRenderProps extends ChatModelCard {
showInfoTag?: boolean;
}
export const ModelItemRender = memo<ModelItemRenderProps>(({ showInfoTag = true, ...model }) => {
const { styles, cx } = useStyles();
const { t } = useTranslation('common');

return (
<Flexbox align={'center'} gap={32} horizontal justify={'space-between'}>
<Flexbox align={'center'} gap={8} horizontal>
<ModelIcon model={model.id} size={20} />
{model.displayName || model.id}
</Flexbox>

{showInfoTag && (
<Flexbox gap={4} horizontal>
{model.files && (
<Tooltip placement={'right'} title={t('ModelSelect.featureTag.file')}>
<div className={cx(styles.tag, styles.tagGreen)}>
<Icon icon={LucidePaperclip} />
</div>
</Tooltip>
)}
{model.vision && (
<Tooltip placement={'right'} title={t('ModelSelect.featureTag.vision')}>
<div className={cx(styles.tag, styles.tagGreen)}>
<Icon icon={LucideEye} />
</div>
</Tooltip>
)}
{model.functionCall && (
<Tooltip
overlayStyle={{ maxWidth: 'unset' }}
placement={'right'}
title={t('ModelSelect.featureTag.functionCall')}
>
<div className={cx(styles.tag, styles.tagBlue)}>
<Icon icon={ToyBrick} />
</div>
</Tooltip>
)}
{model.tokens && (
<Tooltip
overlayStyle={{ maxWidth: 'unset' }}
placement={'right'}
title={t('ModelSelect.featureTag.tokens', {
tokens: numeral(model.tokens).format('0,0'),
})}
>
<Center className={styles.token}>{Math.floor(model.tokens / 1000)}K</Center>
</Tooltip>
)}
{model.isCustom && (
<Tooltip
overlayStyle={{ maxWidth: 300 }}
placement={'right'}
title={t('ModelSelect.featureTag.custom')}
>
<Center className={styles.custom}>DIY</Center>
</Tooltip>
)}
</Flexbox>
)}
{showInfoTag && <ModelInfoTags {...model} />}
</Flexbox>
);
});
Expand Down
4 changes: 4 additions & 0 deletions src/locales/default/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ export default {
title: 'API Key',
},
},
modelList: {
desc: '选择在会话中展示的模型,多个模型使用逗号(,) 隔开',
title: '模型列表',
},
moonshot: {
title: '月之暗面',
token: {
Expand Down

0 comments on commit e59635f

Please sign in to comment.