feat: vllm support
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
import AutoComplete from '@/components/seal-form/auto-complete';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
interface HintInputProps {
|
||||
value: string;
|
||||
label?: string;
|
||||
onChange: (value: string) => void;
|
||||
sourceOptions?: Global.HintOptions[];
|
||||
}
|
||||
|
||||
const matchReg = /[^=]+=[^=]*$/;
|
||||
|
||||
const HintInput: React.FC<HintInputProps> = (props) => {
|
||||
const { value, label, onChange, sourceOptions } = props;
|
||||
const cursorPosRef = React.useRef(0);
|
||||
const contextBeforeCursorRef = React.useRef('');
|
||||
const [options, setOptions] = React.useState<
|
||||
Array<Global.BaseOption<string>>
|
||||
>([]);
|
||||
|
||||
const generateOptions = (context: string) => {
|
||||
if (!context) {
|
||||
setOptions([]);
|
||||
return;
|
||||
}
|
||||
const match = context.match(matchReg);
|
||||
if (!match) {
|
||||
const list = _.filter(sourceOptions, (item: Global.HintOptions) =>
|
||||
item.label.includes(context)
|
||||
);
|
||||
setOptions(list);
|
||||
return;
|
||||
}
|
||||
const [key, value] = _.split(match[0], '=');
|
||||
const data = _.find(
|
||||
sourceOptions,
|
||||
(item: Global.HintOptions) => item.label === key
|
||||
);
|
||||
if (!data) {
|
||||
setOptions([]);
|
||||
return;
|
||||
}
|
||||
const list = _.filter(data.opts, (item: Global.BaseOption<string>) =>
|
||||
item.label.includes(value)
|
||||
);
|
||||
setOptions(list);
|
||||
};
|
||||
|
||||
const replaceLastEqual = (value: string) => {
|
||||
const matchStr = contextBeforeCursorRef.current.match(matchReg);
|
||||
if (matchStr) {
|
||||
const arr = _.split(matchStr[0], '=');
|
||||
onChange(`${arr[0]}=${value}`);
|
||||
} else {
|
||||
onChange(value?.trim());
|
||||
}
|
||||
};
|
||||
|
||||
const getContextBeforeCursor = _.debounce((e: any) => {
|
||||
cursorPosRef.current = e.target.selectionStart;
|
||||
contextBeforeCursorRef.current = e.target.value.slice(
|
||||
0,
|
||||
cursorPosRef.current
|
||||
);
|
||||
generateOptions(contextBeforeCursorRef.current);
|
||||
}, 100);
|
||||
|
||||
const handleInput = (e: any) => {
|
||||
getContextBeforeCursor(e);
|
||||
onChange(e.target.value?.trim());
|
||||
};
|
||||
|
||||
const handleOnChange = (value: string) => {
|
||||
onChange(value?.trim());
|
||||
};
|
||||
|
||||
const handleOnSelect = (value: string) => {
|
||||
replaceLastEqual(value);
|
||||
setOptions([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<AutoComplete
|
||||
defaultActiveFirstOption={true}
|
||||
value={value}
|
||||
onInput={handleInput}
|
||||
onSelect={handleOnSelect}
|
||||
onFocus={getContextBeforeCursor}
|
||||
label={label}
|
||||
options={options}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(HintInput);
|
||||
@@ -0,0 +1,109 @@
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import Wrapper from '../label-selector/wrapper';
|
||||
import ListItem from './list-item';
|
||||
|
||||
interface ListInputProps {
|
||||
dataList: string[];
|
||||
label: string;
|
||||
description?: string;
|
||||
btnText?: string;
|
||||
options?: Global.HintOptions[];
|
||||
onChange: (data: string[]) => void;
|
||||
}
|
||||
|
||||
const ListInput: React.FC<ListInputProps> = (props) => {
|
||||
const intl = useIntl();
|
||||
const { dataList, label, description, onChange, btnText, options } = props;
|
||||
const [list, setList] = React.useState<{ value: string; uid: number }[]>([]);
|
||||
const countRef = React.useRef(0);
|
||||
const buttonRef = React.useRef<HTMLButtonElement>(null);
|
||||
|
||||
const updateCountRef = () => {
|
||||
countRef.current = countRef.current + 1;
|
||||
};
|
||||
|
||||
const handleOnRemove = (index: number) => {
|
||||
const values = _.cloneDeep(list);
|
||||
values.splice(index, 1);
|
||||
const valueList = _.map(values, 'value').filter((val: string) => !!val);
|
||||
setList(values);
|
||||
onChange(valueList);
|
||||
};
|
||||
|
||||
const handleOnChange = (value: string, index: number) => {
|
||||
const values = _.cloneDeep(list);
|
||||
values[index].value = value;
|
||||
const valueList = _.map(values, 'value').filter((val: string) => !!val);
|
||||
setList(values);
|
||||
onChange(valueList);
|
||||
};
|
||||
|
||||
const handleOnAdd = () => {
|
||||
updateCountRef();
|
||||
const values = _.cloneDeep(list);
|
||||
values.push({
|
||||
value: '',
|
||||
uid: countRef.current
|
||||
});
|
||||
setList(values);
|
||||
setTimeout(() => {
|
||||
buttonRef.current?.scrollIntoView?.({ behavior: 'smooth' });
|
||||
}, 100);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const valueList = _.map(list, 'value').filter((val: string) => !!val);
|
||||
if (!_.isEqual(valueList, dataList)) {
|
||||
const values = _.map(dataList, (value: string) => {
|
||||
updateCountRef();
|
||||
return {
|
||||
value,
|
||||
uid: countRef.current
|
||||
};
|
||||
});
|
||||
setList(values);
|
||||
}
|
||||
}, [dataList]);
|
||||
|
||||
return (
|
||||
<Wrapper label={label} description={description}>
|
||||
<>
|
||||
{_.map(list, (item: any, index: number) => {
|
||||
return (
|
||||
<ListItem
|
||||
options={options}
|
||||
key={item.uid}
|
||||
value={item.value}
|
||||
label={`${index + 1}`}
|
||||
onRemove={() => handleOnRemove(index)}
|
||||
onChange={(val) => handleOnChange(val, index)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<div className="flex justify-center">
|
||||
<Button
|
||||
ref={buttonRef}
|
||||
type="text"
|
||||
block
|
||||
style={{
|
||||
marginTop: 16,
|
||||
backgroundColor: 'var(--ant-color-fill-secondary)'
|
||||
}}
|
||||
onClick={handleOnAdd}
|
||||
>
|
||||
<PlusOutlined className="font-size-14" />{' '}
|
||||
{intl.formatMessage({
|
||||
id: btnText
|
||||
})}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ListInput);
|
||||
@@ -0,0 +1,43 @@
|
||||
// import AutoComplete from '@/components/seal-form/auto-complete';
|
||||
import { MinusOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import React from 'react';
|
||||
import HintInput from './hint-input';
|
||||
import './styles/list-item.less';
|
||||
|
||||
interface LabelItemProps {
|
||||
onRemove: () => void;
|
||||
onChange: (value: string) => void;
|
||||
value: string;
|
||||
label?: string;
|
||||
options?: Global.HintOptions[];
|
||||
}
|
||||
|
||||
const ListItem: React.FC<LabelItemProps> = (props) => {
|
||||
const { onRemove, onChange, label, value, options } = props;
|
||||
|
||||
const handleOnChange = (value: any) => {
|
||||
onChange(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="list-item">
|
||||
<HintInput
|
||||
value={value}
|
||||
onChange={handleOnChange}
|
||||
label={label}
|
||||
sourceOptions={options}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
className="btn"
|
||||
type="default"
|
||||
shape="circle"
|
||||
icon={<MinusOutlined />}
|
||||
onClick={onRemove}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ListItem);
|
||||
@@ -0,0 +1,15 @@
|
||||
.list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.field-wrapper {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user