feat: add temperature and top-p to ModelSetting and add ChatConfigurationModal and bind backend data to KnowledgeCard (#65)

* feat: bind backend data to KnowledgeCard

* feat: add ChatConfigurationModal

* feat: add temperature and top-p to ModelSetting
This commit is contained in:
balibabu
2024-02-19 19:16:23 +08:00
committed by GitHub
parent f3d0ebd293
commit 452020d33a
19 changed files with 890 additions and 10 deletions

View File

@@ -0,0 +1,35 @@
import { Form, Input } from 'antd';
import classNames from 'classnames';
import { ISegmentedContentProps } from './interface';
import styles from './index.less';
const AssistantSetting = ({ show }: ISegmentedContentProps) => {
return (
<section
className={classNames({
[styles.segmentedHidden]: !show,
})}
>
<Form.Item
name={'name'}
label="Assistant name"
rules={[{ required: true }]}
>
<Input placeholder="e.g. Resume Jarvis" />
</Form.Item>
<Form.Item name={'avatar'} label="Assistant avatar">
<Input />
</Form.Item>
<Form.Item name={'keywords'} label="Keywords">
<Input.TextArea autoSize={{ minRows: 3 }} />
</Form.Item>
<Form.Item name={'opener'} label="Set an opener">
<Input.TextArea autoSize={{ minRows: 5 }} />
</Form.Item>
</section>
);
};
export default AssistantSetting;

View File

@@ -0,0 +1,103 @@
import { Form, FormInstance, Input, InputRef } from 'antd';
import React, { useContext, useEffect, useRef, useState } from 'react';
const EditableContext = React.createContext<FormInstance<any> | null>(null);
interface EditableRowProps {
index: number;
}
interface Item {
key: string;
name: string;
age: string;
address: string;
}
export const EditableRow: React.FC<EditableRowProps> = ({
index,
...props
}) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
interface EditableCellProps {
title: React.ReactNode;
editable: boolean;
children: React.ReactNode;
dataIndex: keyof Item;
record: Item;
handleSave: (record: Item) => void;
}
export const EditableCell: React.FC<EditableCellProps> = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef<InputRef>(null);
const form = useContext(EditableContext)!;
useEffect(() => {
if (editing) {
inputRef.current!.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};
const save = async () => {
try {
const values = await form.validateFields();
toggleEdit();
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log('Save failed:', errInfo);
}
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{ margin: 0 }}
name={dataIndex}
rules={[
{
required: true,
message: `${title} is required.`,
},
]}
>
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
</Form.Item>
) : (
<div
className="editable-cell-value-wrap"
style={{ paddingRight: 24 }}
onClick={toggleEdit}
>
{children}
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};

View File

@@ -0,0 +1,43 @@
.chatConfigurationDescription {
font-size: 14px;
}
.variableContainer {
padding-bottom: 20px;
.variableAlign {
text-align: right;
}
.variableLabel {
margin-right: 16px;
}
.variableTable {
margin-top: 14px;
}
.editableRow {
:global(.editable-cell) {
position: relative;
}
:global(.editable-cell-value-wrap) {
padding: 5px 12px;
cursor: pointer;
height: 22px !important;
}
&:hover {
:global(.editable-cell-value-wrap) {
padding: 4px 11px;
border: 1px solid #d9d9d9;
border-radius: 2px;
}
}
}
}
.segmentedHidden {
opacity: 0;
height: 0;
width: 0;
margin: 0;
}

View File

@@ -0,0 +1,107 @@
import { ReactComponent as ChatConfigurationAtom } from '@/assets/svg/chat-configuration-atom.svg';
import { IModalManagerChildrenProps } from '@/components/modal-manager';
import { Divider, Flex, Form, Modal, Segmented } from 'antd';
import { SegmentedValue } from 'antd/es/segmented';
import { useState } from 'react';
import AssistantSetting from './assistant-setting';
import ModelSetting from './model-setting';
import PromptEngine from './prompt-engine';
import styles from './index.less';
enum ConfigurationSegmented {
AssistantSetting = 'Assistant Setting',
ModelSetting = 'Model Setting',
PromptEngine = 'Prompt Engine',
}
const segmentedMap = {
[ConfigurationSegmented.AssistantSetting]: AssistantSetting,
[ConfigurationSegmented.ModelSetting]: ModelSetting,
[ConfigurationSegmented.PromptEngine]: PromptEngine,
};
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 18 },
};
const validateMessages = {
required: '${label} is required!',
types: {
email: '${label} is not a valid email!',
number: '${label} is not a valid number!',
},
number: {
range: '${label} must be between ${min} and ${max}',
},
};
const ChatConfigurationModal = ({
visible,
hideModal,
}: IModalManagerChildrenProps) => {
const [form] = Form.useForm();
const [value, setValue] = useState<ConfigurationSegmented>(
ConfigurationSegmented.AssistantSetting,
);
const handleOk = async () => {
const x = await form.validateFields();
console.info(x);
};
const handleCancel = () => {
hideModal();
};
const handleSegmentedChange = (val: SegmentedValue) => {
setValue(val as ConfigurationSegmented);
};
const title = (
<Flex gap={16}>
<ChatConfigurationAtom></ChatConfigurationAtom>
<div>
<b>Chat Configuration</b>
<div className={styles.chatConfigurationDescription}>
Here, dress up a dedicated assistant for your special knowledge bases!
💕
</div>
</div>
</Flex>
);
return (
<Modal
title={title}
width={688}
open={visible}
onOk={handleOk}
onCancel={handleCancel}
>
<Segmented
size={'large'}
value={value}
onChange={handleSegmentedChange}
options={Object.values(ConfigurationSegmented)}
block
/>
<Divider></Divider>
<Form
{...layout}
name="nest-messages"
form={form}
style={{ maxWidth: 600 }}
validateMessages={validateMessages}
colon={false}
>
{Object.entries(segmentedMap).map(([key, Element]) => (
<Element key={key} show={key === value}></Element>
))}
</Form>
</Modal>
);
};
export default ChatConfigurationModal;

View File

@@ -0,0 +1,3 @@
export interface ISegmentedContentProps {
show: boolean;
}

View File

@@ -0,0 +1,155 @@
import { Divider, Flex, Form, InputNumber, Select, Slider } from 'antd';
import classNames from 'classnames';
import { ISegmentedContentProps } from './interface';
import styles from './index.less';
const { Option } = Select;
const ModelSetting = ({ show }: ISegmentedContentProps) => {
return (
<section
className={classNames({
[styles.segmentedHidden]: !show,
})}
>
<Form.Item
label="Model"
name="model"
// rules={[{ required: true, message: 'Please input!' }]}
>
<Select />
</Form.Item>
<Divider></Divider>
<Form.Item
label="Parameters"
name="parameters"
// rules={[{ required: true, message: 'Please input!' }]}
>
<Select />
</Form.Item>
<Form.Item label="Temperature">
<Flex gap={20}>
<Flex flex={1}>
<Form.Item
name={['address', 'province']}
noStyle
rules={[{ required: true, message: 'Province is required' }]}
>
<Slider style={{ display: 'inline-block', width: '100%' }} />
</Form.Item>
</Flex>
<Form.Item
name={['address', 'street']}
noStyle
rules={[{ required: true, message: 'Street is required' }]}
>
<InputNumber
style={{
width: 50,
}}
/>
</Form.Item>
</Flex>
</Form.Item>
<Form.Item label="Top P">
<Flex gap={20}>
<Flex flex={1}>
<Form.Item
name={['address', 'province']}
noStyle
rules={[{ required: true, message: 'Province is required' }]}
>
<Slider style={{ display: 'inline-block', width: '100%' }} />
</Form.Item>
</Flex>
<Form.Item
name={['address', 'street']}
noStyle
rules={[{ required: true, message: 'Street is required' }]}
>
<InputNumber
style={{
width: 50,
}}
/>
</Form.Item>
</Flex>
</Form.Item>
<Form.Item label="Presence Penalty">
<Flex gap={20}>
<Flex flex={1}>
<Form.Item
name={['address', 'province']}
noStyle
rules={[{ required: true, message: 'Province is required' }]}
>
<Slider style={{ display: 'inline-block', width: '100%' }} />
</Form.Item>
</Flex>
<Form.Item
name={['address', 'street']}
noStyle
rules={[{ required: true, message: 'Street is required' }]}
>
<InputNumber
style={{
width: 50,
}}
/>
</Form.Item>
</Flex>
</Form.Item>
<Form.Item label="Frequency Penalty">
<Flex gap={20}>
<Flex flex={1}>
<Form.Item
name={['address', 'province']}
noStyle
rules={[{ required: true, message: 'Province is required' }]}
>
<Slider style={{ display: 'inline-block', width: '100%' }} />
</Form.Item>
</Flex>
<Form.Item
name={['address', 'street']}
noStyle
rules={[{ required: true, message: 'Street is required' }]}
>
<InputNumber
style={{
width: 50,
}}
/>
</Form.Item>
</Flex>
</Form.Item>
<Form.Item label="Max Tokens">
<Flex gap={20}>
<Flex flex={1}>
<Form.Item
name={['address', 'province']}
noStyle
rules={[{ required: true, message: 'Province is required' }]}
>
<Slider style={{ display: 'inline-block', width: '100%' }} />
</Form.Item>
</Flex>
<Form.Item
name={['address', 'street']}
noStyle
rules={[{ required: true, message: 'Street is required' }]}
>
<InputNumber
style={{
width: 50,
}}
/>
</Form.Item>
</Flex>
</Form.Item>
</section>
);
};
export default ModelSetting;

View File

@@ -0,0 +1,163 @@
import { DeleteOutlined } from '@ant-design/icons';
import {
Button,
Col,
Divider,
Form,
Input,
Row,
Select,
Switch,
Table,
TableProps,
} from 'antd';
import classNames from 'classnames';
import { useState } from 'react';
import { v4 as uuid } from 'uuid';
import { EditableCell, EditableRow } from './editable-cell';
import { ISegmentedContentProps } from './interface';
import styles from './index.less';
interface DataType {
key: string;
optional: boolean;
}
const { Option } = Select;
const PromptEngine = ({ show }: ISegmentedContentProps) => {
const [dataSource, setDataSource] = useState<DataType[]>([]);
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const handleRemove = (key: string) => () => {
const newData = dataSource.filter((item) => item.key !== key);
setDataSource(newData);
};
const handleSave = (row: DataType) => {
const newData = [...dataSource];
const index = newData.findIndex((item) => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
setDataSource(newData);
};
const columns: TableProps<DataType>['columns'] = [
{
title: 'key',
dataIndex: 'variable',
key: 'variable',
onCell: (record: DataType) => ({
record,
editable: true,
dataIndex: 'variable',
title: 'key',
handleSave,
}),
},
{
title: 'optional',
dataIndex: 'optional',
key: 'optional',
width: 40,
align: 'center',
render() {
return <Switch size="small" />;
},
},
{
title: 'operation',
dataIndex: 'operation',
width: 30,
key: 'operation',
align: 'center',
render(_, record) {
return <DeleteOutlined onClick={handleRemove(record.key)} />;
},
},
];
const handleAdd = () => {
setDataSource((state) => [
...state,
{
key: uuid(),
variable: '',
optional: true,
},
]);
};
return (
<section
className={classNames({
[styles.segmentedHidden]: !show,
})}
>
<Form.Item
label="Orchestrate"
name="orchestrate"
rules={[{ required: true, message: 'Please input!' }]}
>
<Input.TextArea autoSize={{ maxRows: 5, minRows: 5 }} />
</Form.Item>
<Divider></Divider>
<section className={classNames(styles.variableContainer)}>
<Row align={'middle'} justify="end">
<Col span={6} className={styles.variableAlign}>
<label className={styles.variableLabel}>Variables</label>
</Col>
<Col span={18} className={styles.variableAlign}>
<Button size="small" onClick={handleAdd}>
Add
</Button>
</Col>
</Row>
{dataSource.length > 0 && (
<Row>
<Col span={6}></Col>
<Col span={18}>
<Table
dataSource={dataSource}
columns={columns}
rowKey={'key'}
className={styles.variableTable}
components={components}
rowClassName={() => styles.editableRow}
/>
</Col>
</Row>
)}
</section>
<Form.Item
label="Select one context"
name="context"
rules={[
{
required: true,
message: 'Please select your favourite colors!',
type: 'array',
},
]}
>
<Select mode="multiple" placeholder="Please select favourite colors">
<Option value="red">Red</Option>
<Option value="green">Green</Option>
<Option value="blue">Blue</Option>
</Select>
</Form.Item>
</section>
);
};
export default PromptEngine;

View File

@@ -0,0 +1,3 @@
.chatContainer {
padding: 0 24px 24px;
}

View File

@@ -0,0 +1,36 @@
import { Button, Flex, Input } from 'antd';
import { ChangeEventHandler, useState } from 'react';
import styles from './index.less';
const ChatContainer = () => {
const [value, setValue] = useState('');
const handlePressEnter = () => {
console.info(value);
};
const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => {
setValue(e.target.value);
};
return (
<Flex flex={1} className={styles.chatContainer} vertical>
<Flex flex={1}>xx</Flex>
<Input
size="large"
placeholder="Message Resume Assistant..."
value={value}
suffix={
<Button type="primary" onClick={handlePressEnter}>
Send
</Button>
}
onPressEnter={handlePressEnter}
onChange={handleInputChange}
/>
</Flex>
);
};
export default ChatContainer;

View File

@@ -0,0 +1,25 @@
.chatWrapper {
height: 100%;
.chatAppWrapper {
width: 288px;
padding: 26px;
}
.chatTitleWrapper {
width: 220px;
padding: 26px 0;
}
.chatTitle {
padding: 5px 15px;
}
.chatTitleContent {
padding: 5px 10px;
}
.divider {
margin: 0;
height: 100%;
}
}

View File

@@ -1,8 +1,64 @@
import { FormOutlined } from '@ant-design/icons';
import { Button, Card, Divider, Flex, Space, Tag } from 'antd';
import { useSelector } from 'umi';
import ChatContainer from './chat-container';
import ModalManager from '@/components/modal-manager';
import ChatConfigurationModal from './chat-configuration-modal';
import styles from './index.less';
const Chat = () => {
const { name } = useSelector((state: any) => state.chatModel);
return <div>chat:{name} </div>;
return (
<Flex className={styles.chatWrapper}>
<Flex className={styles.chatAppWrapper}>
<Flex flex={1} vertical>
<ModalManager>
{({ visible, showModal, hideModal }) => {
return (
<>
<Button type="primary" onClick={() => showModal()}>
Create an Assistant
</Button>
<ChatConfigurationModal
visible={visible}
showModal={showModal}
hideModal={hideModal}
></ChatConfigurationModal>
</>
);
}}
</ModalManager>
<Divider></Divider>
<Card>
<p>Card content</p>
</Card>
</Flex>
</Flex>
<Divider type={'vertical'} className={styles.divider}></Divider>
<Flex className={styles.chatTitleWrapper}>
<Flex flex={1} vertical>
<Flex
justify={'space-between'}
align="center"
className={styles.chatTitle}
>
<Space>
<b>Chat</b>
<Tag>25</Tag>
</Space>
<FormOutlined />
</Flex>
<Divider></Divider>
<section className={styles.chatTitleContent}>today</section>
</Flex>
</Flex>
<Divider type={'vertical'} className={styles.divider}></Divider>
<ChatContainer></ChatContainer>
</Flex>
);
};
export default Chat;

View File

@@ -0,0 +1,45 @@
// RCE CSS
import { MessageList } from 'react-chat-elements';
import 'react-chat-elements/dist/main.css';
const ChatBox = () => {
return (
<div style={{ width: 600 }}>
{/* <MessageBox
position={'left'}
type={'photo'}
text={'react.svg'}
data={{
uri: 'https://facebook.github.io/react/img/logo.svg',
status: {
click: false,
loading: 0,
},
}}
/> */}
<MessageList
// referance={messageListReferance}
className="message-list"
lockable={true}
toBottomHeight={'100%'}
dataSource={[
{
position: 'right',
type: 'text',
text: 'Lorem ipsum dolor sit amet',
date: new Date(),
},
{
position: 'left',
type: 'text',
text: 'Lorem ipsum dolor sit amet',
date: new Date(),
},
]}
/>
</div>
);
};
export default ChatBox;