Skip to content

Commit

Permalink
✨ feat: support update model config
Browse files Browse the repository at this point in the history
  • Loading branch information
arvinxx committed Apr 10, 2024
1 parent 62d6bb7 commit e8ed847
Show file tree
Hide file tree
Showing 15 changed files with 365 additions and 175 deletions.
2 changes: 1 addition & 1 deletion src/app/settings/llm/OpenAI/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const LLM = memo(() => {
children: (
<ProviderModelListSelect
placeholder={t('llm.openai.customModelName.placeholder')}
provider={'openai'}
provider={'openAI'}
/>
),
desc: t('llm.openai.customModelName.desc'),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { ActionIcon } from '@lobehub/ui';
import { App, Typography } from 'antd';
import isEqual from 'fast-deep-equal';
import { LucideSettings, LucideTrash2 } from 'lucide-react';
import { memo, useState } from 'react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

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

import ModelConfigModal from './ModelConfigModal';

interface CustomModelOptionProps {
id: string;
provider: GlobalLLMProviderKey;
Expand All @@ -21,57 +22,60 @@ const CustomModelOption = memo<CustomModelOptionProps>(({ id, provider }) => {
const { t: s } = useTranslation('setting');
const { modal } = App.useApp();

const [open, setOpen] = useState(true);
const [dispatchCustomModelCards] = useGlobalStore((s) => [s.dispatchCustomModelCards]);
const [dispatchCustomModelCards, toggleEditingCustomModelCard] = useGlobalStore((s) => [
s.dispatchCustomModelCards,
s.toggleEditingCustomModelCard,
]);
const modelCard = useGlobalStore(
modelConfigSelectors.getCustomModelCardById({ id, provider }),
isEqual,
);

return (
<>
<Flexbox align={'center'} distribution={'space-between'} gap={8} horizontal>
<Flexbox align={'center'} distribution={'space-between'} gap={8} horizontal>
<Flexbox align={'center'} gap={8} horizontal>
<ModelIcon model={id} size={32} />
<Flexbox>
<ModelIcon model={id} size={32} />
<Flexbox>
<Flexbox align={'center'} gap={8} horizontal>
{id}
{/*<ModelInfoTags id={id} isCustom />*/}
</Flexbox>
<Typography.Text style={{ fontSize: 12 }} type={'secondary'}>
{id}
</Typography.Text>
<Flexbox align={'center'} gap={8} horizontal>
{modelCard?.displayName || id}
<ModelInfoTags id={id} {...modelCard} isCustom />
</Flexbox>
<Typography.Text style={{ fontSize: 12 }} type={'secondary'}>
{id}
</Typography.Text>
</Flexbox>
</Flexbox>

<Flexbox horizontal>
<ActionIcon
icon={LucideSettings}
onClick={async (e) => {
e.stopPropagation();
setOpen(true);
}}
title={s('llm.customModelCards.config')}
/>
<ActionIcon
icon={LucideTrash2}
onClick={async (e) => {
e.stopPropagation();
e.preventDefault();
<Flexbox horizontal>
<ActionIcon
icon={LucideSettings}
onClick={async (e) => {
e.stopPropagation();
toggleEditingCustomModelCard({ id, provider });
}}
title={s('llm.customModelCards.config')}
/>
<ActionIcon
icon={LucideTrash2}
onClick={async (e) => {
e.stopPropagation();
e.preventDefault();

const isConfirm = await modal.confirm({
centered: true,
content: s('llm.customModelCards.confirmDelete'),
okButtonProps: { danger: true },
type: 'warning',
});
const isConfirm = await modal.confirm({
centered: true,
content: s('llm.customModelCards.confirmDelete'),
okButtonProps: { danger: true },
type: 'warning',
});

if (isConfirm) {
dispatchCustomModelCards(provider, { id, type: 'delete' });
}
}}
title={t('delete')}
/>
</Flexbox>
if (isConfirm) {
dispatchCustomModelCards(provider, { id, type: 'delete' });
}
}}
title={t('delete')}
/>
</Flexbox>
<ModelConfigModal onOpenChange={setOpen} open={open} provider={provider} />
</>
</Flexbox>
);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { InputNumber, Slider, SliderSingleProps } from 'antd';
import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import useMergeState from 'use-merge-value';

const exponent = (num: number) => Math.log2(num);
const getRealValue = (num: number) => Math.round(Math.pow(2, num));

const marks: SliderSingleProps['marks'] = {
[exponent(1)]: '1k',
[exponent(2)]: '2k',
[exponent(4)]: '4k',
[exponent(8)]: '8k',
[exponent(16)]: '16k',
[exponent(32)]: '32k',
[exponent(64)]: '64k',
[exponent(128)]: '128k',
[exponent(200)]: '200k',
[exponent(1000)]: '1M',
};

interface MaxTokenSliderProps {
defaultValue?: number;
onChange?: (value: number) => void;
value?: number;
}

const MaxTokenSlider = memo<MaxTokenSliderProps>(({ value, onChange, defaultValue }) => {
const [token, setTokens] = useMergeState(0, {
defaultValue,
onChange,
value: value,
});

const [powValue, setPowValue] = useMergeState(0, {
defaultValue: exponent(typeof defaultValue === 'undefined' ? 0 : defaultValue / 1000),
value: exponent(typeof value === 'undefined' ? 0 : value / 1000),
});

const updateWithPowValue = (value: number) => {
setPowValue(value);

setTokens(getRealValue(value) * 1024);
};
const updateWithRealValue = (value: number) => {
setTokens(value);

setPowValue(exponent(value / 1024));
};

return (
<Flexbox align={'center'} gap={12} horizontal>
<Flexbox flex={1}>
<Slider
marks={marks}
max={exponent(1000)}
min={0}
onChange={updateWithPowValue}
step={1}
tooltip={{
formatter: (x) => {
if (typeof x === 'undefined') return;

const value = getRealValue(x);

if (value < 1000) return value.toFixed(0) + 'K';

return (value / 1000).toFixed(0) + 'M';
},
}}
value={powValue}
/>
</Flexbox>
<div>
<InputNumber
onChange={(e) => {
if (!e) return;

updateWithRealValue(e);
}}
step={1024}
value={token}
/>
</div>
</Flexbox>
);
});
export default MaxTokenSlider;
147 changes: 87 additions & 60 deletions src/app/settings/llm/components/ProviderModelList/ModelConfigModal.tsx
Original file line number Diff line number Diff line change
@@ -1,81 +1,108 @@
import { Modal, SliderWithInput } from '@lobehub/ui';
import { Modal } from '@lobehub/ui';
import { Checkbox, Form, Input } from 'antd';
import isEqual from 'fast-deep-equal';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';

import { GlobalLLMProviderKey } from '@/types/settings';
import { useGlobalStore } from '@/store/global';
import { modelConfigSelectors } from '@/store/global/slices/settings/selectors';

interface ModelConfigModalProps {
id: string;
onOpenChange: (open: boolean) => void;
open?: boolean;
provider: GlobalLLMProviderKey;
}
const ModelConfigModal = memo<ModelConfigModalProps>(({ open, id, onOpenChange }) => {
import MaxTokenSlider from './MaxTokenSlider';

const ModelConfigModal = memo(() => {
const [formInstance] = Form.useForm();
const { t } = useTranslation('setting');

const [open, id, provider, dispatchCustomModelCards, toggleEditingCustomModelCard] =
useGlobalStore((s) => [
!!s.editingCustomCardModel,
s.editingCustomCardModel?.id,
s.editingCustomCardModel?.provider,
s.dispatchCustomModelCards,
s.toggleEditingCustomModelCard,
]);

const modelCard = useGlobalStore(
modelConfigSelectors.getCustomModelCardById({ id, provider }),
isEqual,
);

const closeModal = () => {
toggleEditingCustomModelCard(undefined);
};

return (
<Modal
destroyOnClose
maskClosable
onCancel={() => {
onOpenChange(false);
closeModal();
}}
onOk={() => {
if (!provider || !id) return;
const data = formInstance.getFieldsValue();

dispatchCustomModelCards(provider as any, { id, type: 'update', value: data });

closeModal();
}}
open={open}
title={t('llm.customModelCards.modelConfig.modalTitle')}
>
<Form
colon={false}
form={formInstance}
labelCol={{ offset: 0, span: 4 }}
style={{ marginTop: 16 }}
wrapperCol={{ offset: 1, span: 19 }}
<div
onClick={(e) => {
e.stopPropagation();
}}
onKeyDown={(e) => {
e.stopPropagation();
}}
>
<Form.Item label={t('llm.customModelCards.modelConfig.id.title')} name={'id'}>
<Input placeholder={t('llm.customModelCards.modelConfig.id.placeholder')} />
</Form.Item>
<Form.Item
label={t('llm.customModelCards.modelConfig.displayName.title')}
name={'displayName'}
>
<Input placeholder={t('llm.customModelCards.modelConfig.displayName.placeholder')} />
</Form.Item>
<Form.Item label={t('llm.customModelCards.modelConfig.tokens.title')} name={'tokens'}>
<SliderWithInput
marks={{
100_000: '100k',
128_000: '128k',
16_385: '16k',
200_000: '200k',
32_768: '32k',
4096: '4k',
}}
max={200_000}
min={0}
/>
</Form.Item>
<Form.Item
extra={t('llm.customModelCards.modelConfig.functionCall.extra')}
label={t('llm.customModelCards.modelConfig.functionCall.title')}
name={'functionCall'}
>
<Checkbox />
</Form.Item>
<Form.Item
extra={t('llm.customModelCards.modelConfig.vision.extra')}
label={t('llm.customModelCards.modelConfig.vision.title')}
name={'vision'}
>
<Checkbox />
</Form.Item>
<Form.Item
extra={t('llm.customModelCards.modelConfig.files.extra')}
label={t('llm.customModelCards.modelConfig.files.title')}
name={'files'}
<Form
colon={false}
form={formInstance}
initialValues={modelCard}
labelCol={{ span: 4 }}
style={{ marginTop: 16 }}
wrapperCol={{ offset: 1, span: 18 }}
>
<Checkbox />
</Form.Item>
</Form>
<Form.Item label={t('llm.customModelCards.modelConfig.id.title')} name={'id'}>
<Input placeholder={t('llm.customModelCards.modelConfig.id.placeholder')} />
</Form.Item>
<Form.Item
label={t('llm.customModelCards.modelConfig.displayName.title')}
name={'displayName'}
>
<Input placeholder={t('llm.customModelCards.modelConfig.displayName.placeholder')} />
</Form.Item>
<Form.Item label={t('llm.customModelCards.modelConfig.tokens.title')} name={'tokens'}>
<MaxTokenSlider />
</Form.Item>
<Form.Item
extra={t('llm.customModelCards.modelConfig.functionCall.extra')}
label={t('llm.customModelCards.modelConfig.functionCall.title')}
name={'functionCall'}
valuePropName={'checked'}
>
<Checkbox />
</Form.Item>
<Form.Item
extra={t('llm.customModelCards.modelConfig.vision.extra')}
label={t('llm.customModelCards.modelConfig.vision.title')}
name={'vision'}
valuePropName={'checked'}
>
<Checkbox />
</Form.Item>
<Form.Item
extra={t('llm.customModelCards.modelConfig.files.extra')}
label={t('llm.customModelCards.modelConfig.files.title')}
name={'files'}
valuePropName={'checked'}
>
<Checkbox />
</Form.Item>
</Form>
</div>
</Modal>
);
});
Expand Down
Loading

0 comments on commit e8ed847

Please sign in to comment.