RWKV-Runner/frontend/src/pages/Configs.tsx

376 lines
18 KiB
TypeScript
Raw Normal View History

2023-06-05 16:12:58 +00:00
import { Dropdown, Input, Label, Option, Select, Switch, Text } from '@fluentui/react-components';
import { AddCircle20Regular, DataUsageSettings20Regular, Delete20Regular, Save20Regular } from '@fluentui/react-icons';
2023-05-21 04:54:37 +00:00
import React, { FC } from 'react';
import { Section } from '../components/Section';
import { Labeled } from '../components/Labeled';
import { ToolTipButton } from '../components/ToolTipButton';
2023-05-20 08:07:08 +00:00
import commonStore from '../stores/commonStore';
2023-05-21 04:54:37 +00:00
import { observer } from 'mobx-react-lite';
import { toast } from 'react-toastify';
import { ValuedSlider } from '../components/ValuedSlider';
import { NumberInput } from '../components/NumberInput';
import { Page } from '../components/Page';
import { useNavigate } from 'react-router';
import { RunButton } from '../components/RunButton';
import { updateConfig } from '../apis';
import { ConvertModel, FileExists } from '../../wailsjs/go/backend_golang/App';
import { getStrategy, refreshLocalModels } from '../utils';
import { useTranslation } from 'react-i18next';
import { WindowShow } from '../../wailsjs/runtime/runtime';
2023-06-03 12:18:57 +00:00
import strategyImg from '../assets/images/strategy.jpg';
import strategyZhImg from '../assets/images/strategy_zh.jpg';
2023-06-05 16:12:58 +00:00
import { ResetConfigsButton } from '../components/ResetConfigsButton';
2023-05-15 13:55:57 +00:00
2023-05-20 08:07:08 +00:00
export type ApiParameters = {
apiPort: number
maxResponseToken: number;
temperature: number;
topP: number;
presencePenalty: number;
frequencyPenalty: number;
}
2023-06-06 14:42:38 +00:00
export type Device = 'CPU' | 'CUDA' | 'MPS' | 'Custom';
2023-05-20 08:07:08 +00:00
export type Precision = 'fp16' | 'int8' | 'fp32';
export type ModelParameters = {
// different models can not have the same name
modelName: string;
device: Device;
precision: Precision;
storedLayers: number;
maxStoredLayers: number;
2023-05-23 05:33:27 +00:00
useCustomCuda?: boolean;
2023-05-31 04:26:10 +00:00
customStrategy?: string;
2023-05-20 08:07:08 +00:00
}
export type ModelConfig = {
// different configs can have the same name
name: string;
apiParameters: ApiParameters
modelParameters: ModelParameters
}
2023-05-15 13:55:57 +00:00
export const Configs: FC = observer(() => {
2023-05-21 04:54:37 +00:00
const { t } = useTranslation();
2023-05-15 13:55:57 +00:00
const [selectedIndex, setSelectedIndex] = React.useState(commonStore.currentModelConfigIndex);
const [selectedConfig, setSelectedConfig] = React.useState(commonStore.modelConfigs[selectedIndex]);
2023-06-03 12:18:57 +00:00
const [displayStrategyImg, setDisplayStrategyImg] = React.useState(false);
2023-05-15 14:51:52 +00:00
const navigate = useNavigate();
2023-05-20 03:48:32 +00:00
const port = selectedConfig.apiParameters.apiPort;
2023-05-15 14:51:52 +00:00
2023-05-15 13:55:57 +00:00
const updateSelectedIndex = (newIndex: number) => {
setSelectedIndex(newIndex);
setSelectedConfig(commonStore.modelConfigs[newIndex]);
// if you don't want to update the config used by the current startup in real time, comment out this line
commonStore.setCurrentConfigIndex(newIndex);
};
const setSelectedConfigName = (newName: string) => {
2023-05-21 04:54:37 +00:00
setSelectedConfig({ ...selectedConfig, name: newName });
2023-05-15 13:55:57 +00:00
};
const setSelectedConfigApiParams = (newParams: Partial<ApiParameters>) => {
setSelectedConfig({
...selectedConfig, apiParameters: {
...selectedConfig.apiParameters,
...newParams
}
});
};
const setSelectedConfigModelParams = (newParams: Partial<ModelParameters>) => {
setSelectedConfig({
...selectedConfig, modelParameters: {
...selectedConfig.modelParameters,
...newParams
}
});
};
2023-05-04 15:55:24 +00:00
2023-05-17 03:39:00 +00:00
const onClickSave = () => {
commonStore.setModelConfig(selectedIndex, selectedConfig);
updateConfig({
max_tokens: selectedConfig.apiParameters.maxResponseToken,
temperature: selectedConfig.apiParameters.temperature,
top_p: selectedConfig.apiParameters.topP,
presence_penalty: selectedConfig.apiParameters.presencePenalty,
frequency_penalty: selectedConfig.apiParameters.frequencyPenalty
});
2023-05-21 04:54:37 +00:00
toast(t('Config Saved'), { autoClose: 300, type: 'success' });
2023-05-17 03:39:00 +00:00
};
2023-05-05 15:23:34 +00:00
return (
2023-05-18 12:48:53 +00:00
<Page title={t('Configs')} content={
2023-05-15 13:55:57 +00:00
<div className="flex flex-col gap-2 overflow-hidden">
<div className="flex gap-2 items-center">
2023-05-21 04:54:37 +00:00
<Dropdown style={{ minWidth: 0 }} className="grow" value={commonStore.modelConfigs[selectedIndex].name}
selectedOptions={[selectedIndex.toString()]}
onOptionSelect={(_, data) => {
if (data.optionValue) {
updateSelectedIndex(Number(data.optionValue));
}
}}>
2023-05-15 13:55:57 +00:00
{commonStore.modelConfigs.map((config, index) =>
<Option key={index} value={index.toString()}>{config.name}</Option>
)}
</Dropdown>
2023-05-21 04:54:37 +00:00
<ToolTipButton desc={t('New Config')} icon={<AddCircle20Regular />} onClick={() => {
2023-05-15 13:55:57 +00:00
commonStore.createModelConfig();
updateSelectedIndex(commonStore.modelConfigs.length - 1);
2023-05-21 04:54:37 +00:00
}} />
<ToolTipButton desc={t('Delete Config')} icon={<Delete20Regular />} onClick={() => {
2023-05-15 13:55:57 +00:00
commonStore.deleteModelConfig(selectedIndex);
updateSelectedIndex(Math.min(selectedIndex, commonStore.modelConfigs.length - 1));
2023-05-21 04:54:37 +00:00
}} />
2023-06-05 16:12:58 +00:00
<ResetConfigsButton afterConfirm={() => {
setSelectedIndex(0);
setSelectedConfig(commonStore.modelConfigs[0]);
}} />
2023-05-21 04:54:37 +00:00
<ToolTipButton desc={t('Save Config')} icon={<Save20Regular />} onClick={onClickSave} />
2023-05-15 13:55:57 +00:00
</div>
<div className="flex items-center gap-4">
2023-05-18 12:48:53 +00:00
<Label>{t('Config Name')}</Label>
2023-05-15 13:55:57 +00:00
<Input className="grow" value={selectedConfig.name} onChange={(e, data) => {
setSelectedConfigName(data.value);
2023-05-21 04:54:37 +00:00
}} />
2023-05-15 13:55:57 +00:00
</div>
<div className="flex flex-col gap-2 overflow-y-hidden">
<Section
2023-05-18 12:48:53 +00:00
title={t('Default API Parameters')}
desc={t('Hover your mouse over the text to view a detailed description. Settings marked with * will take effect immediately after being saved.')}
2023-05-15 13:55:57 +00:00
content={
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
2023-05-20 03:48:32 +00:00
<Labeled label={t('API Port')}
2023-05-21 04:54:37 +00:00
desc={t('Open the following URL with your browser to view the API documentation') + `: http://127.0.0.1:${port}/docs. ` +
2023-05-22 02:08:38 +00:00
t('This tool\'s API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the \'https://api.openai.com\' part in the API address with \'') + `http://127.0.0.1:${port}` + '\'.'}
2023-05-21 04:54:37 +00:00
content={
<NumberInput value={port} min={1} max={65535} step={1}
onChange={(e, data) => {
setSelectedConfigApiParams({
apiPort: data.value
});
}} />
} />
2023-05-24 13:27:23 +00:00
<Labeled label={t('Max Response Token') + ' *'}
2023-05-21 04:54:37 +00:00
desc={t('By default, the maximum number of tokens that can be answered in a single response, it can be changed by the user by specifying API parameters.')}
content={
<ValuedSlider value={selectedConfig.apiParameters.maxResponseToken} min={100} max={8100}
step={400}
input
onChange={(e, data) => {
setSelectedConfigApiParams({
maxResponseToken: data.value
});
}} />
} />
2023-05-24 13:27:23 +00:00
<Labeled label={t('Temperature') + ' *'}
2023-06-13 15:18:04 +00:00
desc={t('Sampling temperature, it\'s like giving alcohol to a model, the higher the stronger the randomness and creativity, while the lower, the more focused and deterministic it will be.')}
2023-05-21 04:54:37 +00:00
content={
<ValuedSlider value={selectedConfig.apiParameters.temperature} min={0} max={2} step={0.1}
input
onChange={(e, data) => {
setSelectedConfigApiParams({
temperature: data.value
});
}} />
} />
2023-05-24 13:27:23 +00:00
<Labeled label={t('Top_P') + ' *'}
2023-06-13 15:18:04 +00:00
desc={t('Just like feeding sedatives to the model. Consider the results of the top n% probability mass, 0.1 considers the top 10%, with higher quality but more conservative, 1 considers all results, with lower quality but more diverse.')}
2023-05-21 04:54:37 +00:00
content={
<ValuedSlider value={selectedConfig.apiParameters.topP} min={0} max={1} step={0.1} input
onChange={(e, data) => {
setSelectedConfigApiParams({
topP: data.value
});
}} />
} />
2023-05-24 13:27:23 +00:00
<Labeled label={t('Presence Penalty') + ' *'}
2023-05-21 04:54:37 +00:00
desc={t('Positive values penalize new tokens based on whether they appear in the text so far, increasing the model\'s likelihood to talk about new topics.')}
content={
<ValuedSlider value={selectedConfig.apiParameters.presencePenalty} min={-2} max={2}
step={0.1} input
onChange={(e, data) => {
setSelectedConfigApiParams({
presencePenalty: data.value
});
}} />
} />
2023-05-24 13:27:23 +00:00
<Labeled label={t('Frequency Penalty') + ' *'}
2023-05-21 04:54:37 +00:00
desc={t('Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model\'s likelihood to repeat the same line verbatim.')}
content={
<ValuedSlider value={selectedConfig.apiParameters.frequencyPenalty} min={-2} max={2}
step={0.1} input
onChange={(e, data) => {
setSelectedConfigApiParams({
frequencyPenalty: data.value
});
}} />
} />
2023-05-15 13:55:57 +00:00
</div>
}
/>
<Section
2023-05-18 12:48:53 +00:00
title={t('Model Parameters')}
2023-05-15 13:55:57 +00:00
content={
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
2023-05-18 12:48:53 +00:00
<Labeled label={t('Model')} content={
2023-05-15 13:55:57 +00:00
<div className="flex gap-2 grow">
2023-05-21 04:54:37 +00:00
<Select style={{ minWidth: 0 }} className="grow"
value={selectedConfig.modelParameters.modelName}
onChange={(e, data) => {
setSelectedConfigModelParams({
modelName: data.value
});
}}>
{!commonStore.modelSourceList.find(item => item.name === selectedConfig.modelParameters.modelName)?.isComplete
&& <option key={-1}
value={selectedConfig.modelParameters.modelName}>{selectedConfig.modelParameters.modelName}
</option>}
2023-05-15 13:55:57 +00:00
{commonStore.modelSourceList.map((modelItem, index) =>
modelItem.isComplete && <option key={index} value={modelItem.name}>{modelItem.name}</option>
2023-05-15 13:55:57 +00:00
)}
2023-05-15 14:51:52 +00:00
</Select>
2023-05-21 04:54:37 +00:00
<ToolTipButton desc={t('Manage Models')} icon={<DataUsageSettings20Regular />} onClick={() => {
navigate({ pathname: '/models' });
}} />
2023-05-15 13:55:57 +00:00
</div>
2023-05-21 04:54:37 +00:00
} />
2023-06-13 15:18:04 +00:00
<ToolTipButton text={t('Convert')}
desc={t('Convert model with these configs. Using a converted model will greatly improve the loading speed, but model parameters of the converted model cannot be modified.')}
onClick={async () => {
if (commonStore.platform == 'darwin') {
toast(t('MacOS is not yet supported for performing this operation, please do it manually.'), { type: 'info' });
return;
} else if (commonStore.platform == 'linux') {
toast(t('Linux is not yet supported for performing this operation, please do it manually.'), { type: 'info' });
return;
}
2023-06-01 08:54:21 +00:00
2023-06-13 15:18:04 +00:00
const modelPath = `${commonStore.settings.customModelsPath}/${selectedConfig.modelParameters.modelName}`;
if (await FileExists(modelPath)) {
const strategy = getStrategy(selectedConfig);
const newModelPath = modelPath + '-' + strategy.replace(/[:> *+]/g, '-');
toast(t('Start Converting'), { autoClose: 1000, type: 'info' });
ConvertModel(commonStore.settings.customPythonPath, modelPath, strategy, newModelPath).then(() => {
toast(`${t('Convert Success')} - ${newModelPath}`, { type: 'success' });
refreshLocalModels({ models: commonStore.modelSourceList }, false);
}).catch(e => {
const errMsg = e.message || e;
if (errMsg.includes('path contains space'))
2023-06-15 13:57:54 +00:00
toast(`${t('Convert Failed')} - ${t('File Path Cannot Contain Space')}`, { type: 'error' });
2023-06-13 15:18:04 +00:00
else
toast(`${t('Convert Failed')} - ${e.message || e}`, { type: 'error' });
});
setTimeout(WindowShow, 1000);
} else {
toast(`${t('Model Not Found')} - ${modelPath}`, { type: 'error' });
}
}} />
<Labeled label={t('Strategy')} content={
2023-05-31 04:26:10 +00:00
<Dropdown style={{ minWidth: 0 }} className="grow" value={t(selectedConfig.modelParameters.device)!}
2023-05-21 04:54:37 +00:00
selectedOptions={[selectedConfig.modelParameters.device]}
onOptionSelect={(_, data) => {
2023-05-31 04:26:10 +00:00
if (data.optionValue) {
2023-05-21 04:54:37 +00:00
setSelectedConfigModelParams({
2023-05-31 04:26:10 +00:00
device: data.optionValue as Device
2023-05-21 04:54:37 +00:00
});
}
}}>
2023-05-31 04:26:10 +00:00
<Option value="CPU">CPU</Option>
2023-06-06 14:42:38 +00:00
{commonStore.platform === 'darwin' && <Option value="MPS">MPS</Option>}
2023-05-31 04:26:10 +00:00
<Option value="CUDA">CUDA</Option>
<Option value="Custom">{t('Custom')!}</Option>
2023-05-15 13:55:57 +00:00
</Dropdown>
2023-05-21 04:54:37 +00:00
} />
2023-05-31 04:26:10 +00:00
{
selectedConfig.modelParameters.device != 'Custom' && <Labeled label={t('Precision')}
desc={t('int8 uses less VRAM, but has slightly lower quality. fp16 has higher quality, and fp32 has the best quality.')}
content={
<Dropdown style={{ minWidth: 0 }} className="grow"
value={selectedConfig.modelParameters.precision}
selectedOptions={[selectedConfig.modelParameters.precision]}
onOptionSelect={(_, data) => {
if (data.optionText) {
setSelectedConfigModelParams({
precision: data.optionText as Precision
});
}
}}>
<Option>fp16</Option>
<Option>int8</Option>
<Option>fp32</Option>
</Dropdown>
} />
}
{
2023-06-03 11:38:24 +00:00
selectedConfig.modelParameters.device == 'CUDA' &&
<Labeled label={t('Current Strategy')}
content={<Text> {getStrategy(selectedConfig)} </Text>} />
}
{
selectedConfig.modelParameters.device == 'CUDA' &&
<Labeled label={t('Stored Layers')}
2023-06-13 15:18:04 +00:00
desc={t('Number of the neural network layers loaded into VRAM, the more you load, the faster the speed, but it consumes more VRAM. (If your VRAM is not enough, it will fail to load)')}
2023-05-31 04:26:10 +00:00
content={
<ValuedSlider value={selectedConfig.modelParameters.storedLayers} min={0}
max={selectedConfig.modelParameters.maxStoredLayers} step={1} input
onChange={(e, data) => {
2023-05-21 04:54:37 +00:00
setSelectedConfigModelParams({
2023-05-31 04:26:10 +00:00
storedLayers: data.value
2023-05-21 04:54:37 +00:00
});
2023-05-31 04:26:10 +00:00
}} />
} />
}
{
selectedConfig.modelParameters.device == 'CUDA' && <div />
2023-05-31 04:26:10 +00:00
}
2023-06-03 12:18:57 +00:00
{
displayStrategyImg &&
2023-06-24 15:57:49 +00:00
<img style={{ width: '80vh', height: 'auto', zIndex: 100 }}
className="fixed left-0 top-0 rounded-xl select-none"
2023-06-03 12:18:57 +00:00
src={commonStore.settings.language === 'zh' ? strategyZhImg : strategyImg} />
}
2023-05-31 04:26:10 +00:00
{
selectedConfig.modelParameters.device == 'Custom' &&
2023-06-03 12:18:57 +00:00
<Labeled label="Strategy"
onMouseEnter={() => setDisplayStrategyImg(true)}
onMouseLeave={() => setDisplayStrategyImg(false)}
2023-05-31 04:26:10 +00:00
content={
2023-06-06 14:42:38 +00:00
<Input className="grow"
placeholder={commonStore.platform != 'darwin' ? 'cuda:0 fp16 *20 -> cuda:1 fp16' : 'mps fp32'}
2023-05-31 04:26:10 +00:00
value={selectedConfig.modelParameters.customStrategy}
onChange={(e, data) => {
setSelectedConfigModelParams({
customStrategy: data.value
});
}} />
} />
}
{selectedConfig.modelParameters.device == 'Custom' && <div />}
{
2023-06-06 14:42:38 +00:00
selectedConfig.modelParameters.device != 'CPU' && selectedConfig.modelParameters.device != 'MPS' &&
2023-05-31 04:26:10 +00:00
<Labeled label={t('Use Custom CUDA kernel to Accelerate')}
2023-06-13 15:18:04 +00:00
desc={t('Enabling this option can greatly improve inference speed and save some VRAM, but there may be compatibility issues. If it fails to start, please turn off this option.')}
2023-05-31 04:26:10 +00:00
content={
<Switch checked={selectedConfig.modelParameters.useCustomCuda}
onChange={(e, data) => {
setSelectedConfigModelParams({
useCustomCuda: data.checked
});
}} />
} />
}
2023-05-15 13:55:57 +00:00
</div>
}
/>
</div>
<div className="flex flex-row-reverse sm:fixed bottom-2 right-2">
2023-05-21 04:54:37 +00:00
<RunButton onClickRun={onClickSave} />
2023-05-15 13:55:57 +00:00
</div>
2023-05-05 15:23:34 +00:00
</div>
2023-05-21 04:54:37 +00:00
} />
2023-05-05 15:23:34 +00:00
);
2023-05-15 13:55:57 +00:00
});