Files
gpustack-ui/src/pages/playground/components/params-settings.tsx
T

327 lines
9.3 KiB
TypeScript

import FieldWrapper from '@/components/seal-form/field-wrapper';
import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select';
import { INPUT_WIDTH } from '@/constants';
import { InfoCircleOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Form, InputNumber, Slider, Tooltip } from 'antd';
import _ from 'lodash';
import { memo, useCallback, useEffect, useId, useState } from 'react';
import CustomLabelStyles from '../style/custom-label.less';
type ParamsSettingsFormProps = {
seed?: number;
stop?: number;
temperature?: number;
top_p?: number;
max_tokens?: number;
model?: string;
};
type ParamsSettingsProps = {
selectedModel?: string;
showModelSelector?: boolean;
params?: ParamsSettingsFormProps;
model?: string;
modelList: Global.BaseOption<string>[];
onValuesChange?: (changeValues: any, value: Record<string, any>) => void;
setParams: (params: any) => void;
globalParams?: ParamsSettingsFormProps;
};
const METAKEYS: Record<string, any> = {
seed: 'seed',
stop: 'stop',
temperature: 'temperature',
top_p: 'top_p',
n_slot_ctx: 'max_tokens',
max_model_len: 'max_tokens'
};
const ParamsSettings: React.FC<ParamsSettingsProps> = ({
selectedModel,
setParams,
globalParams,
onValuesChange,
modelList,
showModelSelector = true
}) => {
const intl = useIntl();
const initialValues = {
seed: null,
stop: null,
temperature: 1,
top_p: 1,
max_tokens: 1024,
...globalParams
};
const [form] = Form.useForm();
const formId = useId();
const [metaData, setMetaData] = useState<Record<string, any>>({});
const [firstLoad, setFirstLoad] = useState(true);
const handleOnFinish = (values: any) => {
console.log('handleOnFinish', values);
};
const handleOnFinishFailed = (errorInfo: any) => {
console.log('handleOnFinishFailed', errorInfo);
};
const handleValuesChange = useCallback(
(changedValues: any, allValues: any) => {
setParams?.(allValues);
onValuesChange?.(changedValues, allValues);
},
[onValuesChange, setParams]
);
const handleFieldValueChange = useCallback(
(val: any, field: string) => {
const values = form.getFieldsValue();
form.setFieldsValue({
...values,
[field]: val
});
setParams({
...values,
[field]: val
});
onValuesChange?.(
{ [field]: val },
{
...values,
[field]: val
}
);
},
[form, setParams, onValuesChange]
);
const handleModelChange = (val: string) => {
const model = _.find(modelList, { value: val });
const modelMeta = model?.meta || {};
const modelMetaValue = _.pick(modelMeta, _.keys(METAKEYS));
const obj = Object.entries(METAKEYS).reduce((acc: any, [key, value]) => {
const val = modelMetaValue[key];
if (val && _.hasIn(modelMetaValue, key)) {
acc[value] = val;
}
return acc;
}, {});
form.setFieldsValue(obj);
setMetaData(obj);
return obj;
};
useEffect(() => {
let model = selectedModel || '';
if (showModelSelector) {
model = model || _.get(modelList, '[0].value');
}
const modelMetaData = handleModelChange(model);
const mergeData = _.merge({}, initialValues, modelMetaData);
form.setFieldsValue({
...mergeData,
model: model
});
setParams({
...mergeData,
model: model
});
setFirstLoad(false);
}, [modelList, showModelSelector, selectedModel]);
useEffect(() => {
if (!firstLoad) {
form.setFieldsValue(globalParams);
}
}, [globalParams, firstLoad]);
const renderLabel = (args: {
field: string;
label: string;
description: string;
}) => {
return (
<span
className={CustomLabelStyles.label}
style={{ width: INPUT_WIDTH.mini }}
>
<span className="text">
{args.description ? (
<Tooltip title={args.description}>
<span> {args.label}</span>
<span className="m-l-5">
<InfoCircleOutlined />
</span>
</Tooltip>
) : (
<span>{args.label}</span>
)}
</span>
<InputNumber
className="label-val"
variant="outlined"
size="small"
value={form.getFieldValue(args.field)}
controls={false}
onChange={(val) => handleFieldValueChange(val, args.field)}
></InputNumber>
</span>
);
};
return (
<Form
name={formId}
form={form}
onValuesChange={handleValuesChange}
onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed}
>
<div>
{showModelSelector && (
<>
<h3 className="m-b-20 m-l-10 font-size-14 line-24">
{intl.formatMessage({ id: 'playground.model' })}
</h3>
<Form.Item<ParamsSettingsFormProps>
name="model"
rules={[
{
required: true,
message: intl.formatMessage(
{
id: 'common.form.rule.select'
},
{ name: intl.formatMessage({ id: 'playground.model' }) }
)
}
]}
>
<SealSelect
showSearch={true}
options={modelList}
onChange={handleModelChange}
></SealSelect>
</Form.Item>
</>
)}
<h3 className="m-b-20 m-l-10 flex-between flex-center font-size-14 line-24">
<span>{intl.formatMessage({ id: 'playground.parameters' })}</span>
</h3>
<Form.Item<ParamsSettingsFormProps>
name="temperature"
rules={[{ required: false }]}
>
<FieldWrapper
label={renderLabel({
field: 'temperature',
label: 'Temperature',
description: intl.formatMessage({
id: 'playground.params.temperature.tips'
})
})}
style={{ padding: '20px 2px 0' }}
variant="borderless"
>
<Slider
defaultValue={1}
max={2}
step={0.1}
style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }}
tooltip={{ open: false }}
value={form.getFieldValue('temperature') || undefined}
onChange={(val) => handleFieldValueChange(val, 'temperature')}
></Slider>
</FieldWrapper>
</Form.Item>
<Form.Item<ParamsSettingsFormProps>
name="max_tokens"
rules={[{ required: false }]}
>
<FieldWrapper
label={renderLabel({
field: 'max_tokens',
label: 'Max Tokens',
description: intl.formatMessage({
id: 'playground.params.maxtokens.tips'
})
})}
style={{ padding: '20px 2px 0' }}
variant="borderless"
>
<Slider
defaultValue={2048}
max={metaData.max_tokens || 16 * 1024}
step={1}
style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }}
tooltip={{ open: false }}
value={form.getFieldValue('max_tokens') || undefined}
onChange={(val) => handleFieldValueChange(val, 'max_tokens')}
></Slider>
</FieldWrapper>
</Form.Item>
<Form.Item<ParamsSettingsFormProps>
name="top_p"
rules={[{ required: false }]}
>
<FieldWrapper
label={renderLabel({
field: 'top_p',
label: 'Top P',
description: intl.formatMessage({
id: 'playground.params.topp.tips'
})
})}
style={{ padding: '20px 2px 0' }}
variant="borderless"
>
<Slider
defaultValue={1}
max={1}
step={0.1}
style={{ marginBottom: 0, marginTop: 16, marginInline: 0 }}
tooltip={{ open: false }}
value={form.getFieldValue('top_p') || undefined}
onChange={(val) => handleFieldValueChange(val, 'top_p')}
></Slider>
</FieldWrapper>
</Form.Item>
<Form.Item<ParamsSettingsFormProps>
name="seed"
rules={[{ required: false }]}
>
<SealInput.Number
label="Seed"
min={0}
description={intl.formatMessage({
id: 'playground.params.seed.tips'
})}
style={{ width: INPUT_WIDTH.mini }}
></SealInput.Number>
</Form.Item>
<Form.Item<ParamsSettingsFormProps>
name="stop"
rules={[{ required: false }]}
normalize={(value) => {
return value || null;
}}
>
<SealInput.Input
allowClear
label="Stop Sequence"
description={intl.formatMessage({
id: 'playground.params.stop.tips'
})}
style={{ width: INPUT_WIDTH.mini }}
></SealInput.Input>
</Form.Item>
</div>
</Form>
);
};
export default memo(ParamsSettings);