|
@@ -1,26 +1,627 @@
|
|
|
import * as React from 'react';
|
|
import * as React from 'react';
|
|
|
|
|
+import { useLocation } from 'react-router-dom';
|
|
|
import { observer } from 'mobx-react';
|
|
import { observer } from 'mobx-react';
|
|
|
-import store from './store';
|
|
|
|
|
import './style.less';
|
|
import './style.less';
|
|
|
|
|
+import {
|
|
|
|
|
+ Button, Input, Form, Divider, Splitter, Select, InputNumber, InputNumberProps,
|
|
|
|
|
+ Radio, Switch, Row, Col, Slider, Space, RadioChangeEvent,
|
|
|
|
|
+ Spin
|
|
|
|
|
+} from 'antd';
|
|
|
|
|
+import { PlusCircleOutlined, MinusCircleOutlined } from '@ant-design/icons';
|
|
|
|
|
+import { apis } from '@/apis';
|
|
|
|
|
+import router from '@/router';
|
|
|
|
|
+
|
|
|
|
|
+const { TextArea } = Input;
|
|
|
|
|
+const FormItem = Form.Item;
|
|
|
|
|
+const { Option } = Select;
|
|
|
|
|
+const MAX_COUNT = 10;
|
|
|
|
|
+const Index = 1;
|
|
|
|
|
|
|
|
const QuestionAnswerInfo: React.FC = () => {
|
|
const QuestionAnswerInfo: React.FC = () => {
|
|
|
- const {
|
|
|
|
|
- state,
|
|
|
|
|
- init,
|
|
|
|
|
- reset
|
|
|
|
|
- } = store;
|
|
|
|
|
- const {
|
|
|
|
|
- pageLoading
|
|
|
|
|
- } = state;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ const [form] = Form.useForm();
|
|
|
|
|
+
|
|
|
|
|
+ // top_p
|
|
|
|
|
+ const [topPValue, setTopPValue] = React.useState(0.1);
|
|
|
|
|
+ const TopPDecimalStep: React.FC = () => {
|
|
|
|
|
+
|
|
|
|
|
+ const onChange: InputNumberProps['onChange'] = (value) => {
|
|
|
|
|
+ if (Number.isNaN(value)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ setTopPValue(value as number);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <Row>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Slider
|
|
|
|
|
+ min={0}
|
|
|
|
|
+ max={1}
|
|
|
|
|
+ onChange={onChange}
|
|
|
|
|
+ value={typeof topPValue === 'number' ? topPValue : 0}
|
|
|
|
|
+ step={0.1}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={4}>
|
|
|
|
|
+ <InputNumber
|
|
|
|
|
+ min={0}
|
|
|
|
|
+ max={1}
|
|
|
|
|
+ style={{ margin: '0 16px' }}
|
|
|
|
|
+ step={0.01}
|
|
|
|
|
+ value={topPValue}
|
|
|
|
|
+ onChange={onChange}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ );
|
|
|
|
|
+ };
|
|
|
|
|
+ const [tempValue, setTempValue] = React.useState(0.01);
|
|
|
|
|
+ // temperature
|
|
|
|
|
+ const TempDecimalStep: React.FC = () => {
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ const onChange: InputNumberProps['onChange'] = (value) => {
|
|
|
|
|
+ if (Number.isNaN(value)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ setTempValue(value as number);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <Row>
|
|
|
|
|
+ <Col span={12}>
|
|
|
|
|
+ <Slider
|
|
|
|
|
+ min={0}
|
|
|
|
|
+ max={1}
|
|
|
|
|
+ onChange={onChange}
|
|
|
|
|
+ value={typeof tempValue === 'number' ? tempValue : 0}
|
|
|
|
|
+ step={0.01}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ <Col span={4}>
|
|
|
|
|
+ <InputNumber
|
|
|
|
|
+ min={0}
|
|
|
|
|
+ max={1}
|
|
|
|
|
+ style={{ margin: '0 16px' }}
|
|
|
|
|
+ step={0.01}
|
|
|
|
|
+ value={tempValue}
|
|
|
|
|
+ onChange={onChange}
|
|
|
|
|
+ />
|
|
|
|
|
+ </Col>
|
|
|
|
|
+ </Row>
|
|
|
|
|
+ );
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ type ModelList = {
|
|
|
|
|
+ label: string,
|
|
|
|
|
+ value: string,
|
|
|
|
|
+ }[];
|
|
|
|
|
+ type KnowledgeList = {
|
|
|
|
|
+ label: string,
|
|
|
|
|
+ value: string,
|
|
|
|
|
+ }[];
|
|
|
|
|
+
|
|
|
|
|
+ const [step, setStep] = React.useState(1);
|
|
|
|
|
+ const [pageLoading, setPageLoading] = React.useState(false);
|
|
|
|
|
+ const [modelList, setModelList] = React.useState<ModelList>([]);
|
|
|
|
|
+ const [knowledgeList, setKnowledgeList] = React.useState<KnowledgeList>([]);
|
|
|
|
|
+ const [isVisible, setIsVisible] = React.useState(false);
|
|
|
|
|
+ const [isVisibleCus, setIsVisibleCus] = React.useState(false);
|
|
|
|
|
+ const [isVisibleSlice, setIsVisibleSlice] = React.useState(false);
|
|
|
|
|
+ const [isVisibleRerank, setIsVisibleRerank] = React.useState(false);
|
|
|
|
|
+
|
|
|
|
|
+ const style: React.CSSProperties = {
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ flexDirection: 'column',
|
|
|
|
|
+ gap: 8,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // const suffix = (
|
|
|
|
|
+ // <>
|
|
|
|
|
+ // <span>
|
|
|
|
|
+ // {konwValue.length} / {MAX_COUNT}
|
|
|
|
|
+ // </span>
|
|
|
|
|
+ // <DownOutlined />
|
|
|
|
|
+ // </>
|
|
|
|
|
+ // );
|
|
|
|
|
+
|
|
|
|
|
+ const location = useLocation();
|
|
|
|
|
+
|
|
|
|
|
+ const init = async (id: string) => {
|
|
|
|
|
+ await Promise.all([
|
|
|
|
|
+ api.fetchKnowlegde(),
|
|
|
|
|
+ api.fetchModelList(),
|
|
|
|
|
+ ])
|
|
|
|
|
+ if (id) {
|
|
|
|
|
+ await api.fetchDetail(id);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
React.useEffect(() => {
|
|
|
- init();
|
|
|
|
|
- return () => reset();
|
|
|
|
|
|
|
+ const id = location?.state?.id;
|
|
|
|
|
+ init(id)
|
|
|
}, []);
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
+ // 初始化一个空数组来存储input框的值
|
|
|
|
|
+ const [inputs, setInputs] = React.useState([{ question: '' }]);
|
|
|
|
|
+
|
|
|
|
|
+ // 添加新的input框的函数
|
|
|
|
|
+ const handleAddInput = () => {
|
|
|
|
|
+ // setInputs([...inputs, { question: '' }]);
|
|
|
|
|
+ setInputs([...inputs]);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 移除最后一个input框的函数
|
|
|
|
|
+ const handleRemoveInput = () => {
|
|
|
|
|
+ setInputs(inputs.slice(0, inputs.length - 1));
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const onChange: InputNumberProps['onChange'] = (value) => {
|
|
|
|
|
+ console.log('changed', value);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const onChangeShow = (checked: boolean) => {
|
|
|
|
|
+ console.log(`switch to ${checked}`);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const onChangeModel = (checked: boolean) => {
|
|
|
|
|
+ setIsVisibleRerank(!isVisibleRerank);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const onChangeCount = (value: string) => {
|
|
|
|
|
+ if (value === 'fixed') {
|
|
|
|
|
+ setIsVisibleSlice(!isVisibleSlice);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setIsVisibleSlice(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 召回方式
|
|
|
|
|
+ const onChangeRecallMethod = (e: RadioChangeEvent) => {
|
|
|
|
|
+
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 获取应用详情
|
|
|
|
|
+ const api = {
|
|
|
|
|
+ fetchDetail: async (app_id: string) => {
|
|
|
|
|
+ setPageLoading(true);
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await apis.fetchApplicationDetail(app_id)
|
|
|
|
|
+ const jsonObj = JSON.parse(res.data.detail.extraInput);
|
|
|
|
|
+ const sd = res.data.questionlist.map((item: any) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ "question": item.question,
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ // const index_type = jsonObj.recall_index_type_list.map((item: any) => {
|
|
|
|
|
+ // return {
|
|
|
|
|
+ // "knowledge_id": item.knowledge_id,
|
|
|
|
|
+ // "index_type_id": item.index_type_id,
|
|
|
|
|
+ // }
|
|
|
|
|
+ // });
|
|
|
|
|
+ const info = res.data.detail;
|
|
|
|
|
+ if (jsonObj.rerank_status === 1) {
|
|
|
|
|
+ setIsVisibleRerank(!isVisibleRerank) //模型
|
|
|
|
|
+ }
|
|
|
|
|
+ if (info.paramDesc === 'custom') {
|
|
|
|
|
+ setIsVisibleCus(!isVisibleCus); //自定义回答风格
|
|
|
|
|
+ }
|
|
|
|
|
+ if (jsonObj.slice_config_type === 'fixed') {
|
|
|
|
|
+ setIsVisibleSlice(!isVisibleSlice);
|
|
|
|
|
+ }
|
|
|
|
|
+ setTopPValue(info.topP as number);
|
|
|
|
|
+ setTempValue(info.temperature as number);
|
|
|
|
|
+
|
|
|
|
|
+ form.setFieldsValue({
|
|
|
|
|
+ name: info.name, //应用名称
|
|
|
|
|
+ desc: info.desc, //应用描述
|
|
|
|
|
+ questionList: sd, //问题列表
|
|
|
|
|
+ prompt: info.prompt, //应用提示语
|
|
|
|
|
+ max_token: info.maxToken, //应用最大token
|
|
|
|
|
+ top_p: info.topP, //topP
|
|
|
|
|
+ temperature: info.temperature, //温度
|
|
|
|
|
+ updateDate: info.updateDate, // 更新时间
|
|
|
|
|
+ param_desc: info.paramDesc, //回答风格
|
|
|
|
|
+ knowledge_ids: jsonObj.knowledge_ids, //知识库id
|
|
|
|
|
+ model: jsonObj.model, //模型名称
|
|
|
|
|
+ slice_config_type: jsonObj.slice_config_type, //切片类型
|
|
|
|
|
+ recall_method: jsonObj.recall_method, //召回方式
|
|
|
|
|
+ slice_count: jsonObj.slice_count, //切片数量
|
|
|
|
|
+ rerank_model_name: jsonObj.rerank_model_name, //模型名称
|
|
|
|
|
+ recall_slice_splicing_method: jsonObj.recall_slice_splicing_method,
|
|
|
|
|
+ rerank_status: jsonObj.rerank_status, //开启rerank
|
|
|
|
|
+ show_recall_result: jsonObj.show_recall_result, //是否展示召回结果
|
|
|
|
|
+ recall_index_type_list: jsonObj.recall_index_type_list, //知识库id
|
|
|
|
|
+ rerank_index_type_list: jsonObj.rerank_index_type_list, //知识库id
|
|
|
|
|
+ })
|
|
|
|
|
+ console.log(sd, 'sd');
|
|
|
|
|
+ setInputs(sd);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ setPageLoading(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ //获取知识库列表
|
|
|
|
|
+ fetchKnowlegde: async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await apis.fetchKnowledgeList();
|
|
|
|
|
+ const list = res.data.map((item: any) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ label: item.name,
|
|
|
|
|
+ value: item.knowledgeId,
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ setKnowledgeList(list);
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 获取模型列表
|
|
|
|
|
+ fetchModelList: async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await apis.fetchModelList();
|
|
|
|
|
+ const list = res.data.data.map((item: any) => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ label: item.modelName,
|
|
|
|
|
+ value: item.modelCode,
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ setModelList(list);
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<div className='questionAnswerInfo'>
|
|
<div className='questionAnswerInfo'>
|
|
|
- 创建/修改-问答应用
|
|
|
|
|
|
|
+ <Spin spinning={pageLoading}>
|
|
|
|
|
+ <Form
|
|
|
|
|
+ style={{ paddingTop: '20px' }}
|
|
|
|
|
+ form={form}
|
|
|
|
|
+ layout='vertical'
|
|
|
|
|
+ initialValues={{
|
|
|
|
|
+ max_token: 1024
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <div style={{ display: step === 1 ? 'block' : 'none' }} className='questionAnswerInfo-content'>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='问答应用名称'
|
|
|
|
|
+ name='name'
|
|
|
|
|
+ rules={[{ required: true, message: '问答应用名称不能为空' }]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Input placeholder="应用名称" style={{ width: 646, padding: 8 }} />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='问答应用描述'
|
|
|
|
|
+ name='desc'
|
|
|
|
|
+ rules={[{ required: true, message: '问答应用描述不能为空' }]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <TextArea
|
|
|
|
|
+ showCount
|
|
|
|
|
+ maxLength={500}
|
|
|
|
|
+ placeholder="请输入描述"
|
|
|
|
|
+ style={{ height: 120, resize: 'none', width: 646 }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <h4>添加预设问题</h4>
|
|
|
|
|
+ {
|
|
|
|
|
+ inputs.length === 0 &&
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <label>问题 {Index}</label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ style={{ width: 300, padding: 8, marginLeft: 20 }} name='question' />
|
|
|
|
|
+ <PlusCircleOutlined style={{ marginLeft: 20 }} onClick={handleAddInput} />
|
|
|
|
|
+ <MinusCircleOutlined style={{ marginLeft: 20 }} onClick={handleRemoveInput} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ {inputs.length > 0 &&
|
|
|
|
|
+ inputs.map((input, index) => (
|
|
|
|
|
+ <div style={{ paddingTop: 10 }} key={index}>
|
|
|
|
|
+ <label>问题 {index}</label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ style={{ width: 300, padding: 8, marginLeft: 20 }}
|
|
|
|
|
+ value={input.question}
|
|
|
|
|
+ />
|
|
|
|
|
+ <PlusCircleOutlined style={{ marginLeft: 20 }} onClick={handleAddInput} />
|
|
|
|
|
+ <MinusCircleOutlined style={{ marginLeft: 20 }} onClick={handleRemoveInput} />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <br />
|
|
|
|
|
+ <Button type='primary' onClick={() => {
|
|
|
|
|
+ form.validateFields(['name', 'desc']).then(async (values) => {
|
|
|
|
|
+ setStep(2);
|
|
|
|
|
+ setInputs(inputs);
|
|
|
|
|
+ console.log(inputs, 'inputs');
|
|
|
|
|
+ }).catch((error) => {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ });
|
|
|
|
|
+ }} >
|
|
|
|
|
+ 下一步
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div style={{ display: step === 2 ? 'block' : 'none' }} className='questionAnswerInfo-content'>
|
|
|
|
|
+ <div style={{ paddingTop: '20px', display: 'flex', justifyContent: 'flex-end' }}>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ style={{ background: '#f5f5f5' }}
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ setStep(1);
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ 上一步
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ type='primary'
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ form.validateFields().then(async (values) => {
|
|
|
|
|
+ const data = values;
|
|
|
|
|
+ // const question = [{}];
|
|
|
|
|
+ // if(inputs){
|
|
|
|
|
+ // inputs.map((item, index) => {
|
|
|
|
|
+ // const questionInfo = {
|
|
|
|
|
+ // question: item.question,
|
|
|
|
|
+ // }
|
|
|
|
|
+ // question.push(questionInfo);
|
|
|
|
|
+ // });
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ const info = {
|
|
|
|
|
+ name: values.name,
|
|
|
|
|
+ desc: values.desc,
|
|
|
|
|
+ prompt: values.prompt,
|
|
|
|
|
+ temperature: tempValue,
|
|
|
|
|
+ top_p: topPValue,
|
|
|
|
|
+ knowledge_ids: values.knowledge_ids,
|
|
|
|
|
+ param_desc: values.param_desc,
|
|
|
|
|
+ max_token: values.max_token,
|
|
|
|
|
+ questionList: inputs,
|
|
|
|
|
+ knowledge_info: {
|
|
|
|
|
+ model: values.model, // 默认模型名称
|
|
|
|
|
+ knowledge_ids: values.knowledge_ids, // 知识库id列表
|
|
|
|
|
+ slice_config_type: values.slice_config_type, // 切片类型,默认为 fixed
|
|
|
|
|
+ recall_method: values.recall_method, // 召回方式,默认为 embedding
|
|
|
|
|
+ recall_index_type_list: values.recall_index_type_list, // 索引配置类型列表,默认为空数组
|
|
|
|
|
+ slice_count: values.slice_count, // 切片数量,默认为空字符串
|
|
|
|
|
+ rerank_status: values.rerank_status ? 1 : 0, // 是否开启rerank,默认关闭
|
|
|
|
|
+ rerank_model_name: values.rerank_model_name, // 模型名称,默认为空字符串
|
|
|
|
|
+ show_recall_result: values.show_recall_result, // 是否展示召回结果,默认为空字符串
|
|
|
|
|
+ recall_slice_splicing_method: values.recall_slice_splicing_method // 召回切片拼接方式,默认为空字符串
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ const id = location?.state?.id;
|
|
|
|
|
+ if (id) {
|
|
|
|
|
+ // 编辑应用
|
|
|
|
|
+ console.log(info, 'value');
|
|
|
|
|
+ await apis.modifyApplicationApi(id, info);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 创建应用
|
|
|
|
|
+ await await apis.createApplicationApi(info);
|
|
|
|
|
+ }
|
|
|
|
|
+ router.navigate({ pathname: '/questionAnswer' });
|
|
|
|
|
+ console.log(info, 'info');
|
|
|
|
|
+ }).catch((error) => {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ });
|
|
|
|
|
+ }}
|
|
|
|
|
+ >发布应用</Button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <Splitter style={{ height: '100%', boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}>
|
|
|
|
|
+ <Splitter.Panel defaultSize="40%">
|
|
|
|
|
+ <div>Prompt编写</div>
|
|
|
|
|
+ <div style={{ height: '30%' }}>
|
|
|
|
|
+ <TextArea
|
|
|
|
|
+ autoSize
|
|
|
|
|
+ disabled
|
|
|
|
|
+ placeholder=" 编写Prompt过程中可以引入2项变量:{{知识}} 代表知识库中检索到的知识内容, {{用户}}代表用户输入的内容。您可以在编写Prompt过程中将变量拼接在合适的位置。 "
|
|
|
|
|
+ style={{ width: '90%' }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <Divider plain></Divider>
|
|
|
|
|
+ <div >
|
|
|
|
|
+ <FormItem name='prompt'>
|
|
|
|
|
+ <TextArea
|
|
|
|
|
+ placeholder="提示词"
|
|
|
|
|
+ style={{ width: '90%', height: '100%' }}
|
|
|
|
|
+ autoSize
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Splitter.Panel>
|
|
|
|
|
+ <Splitter.Panel defaultSize="60%">
|
|
|
|
|
+ <div style={{ background: '#f5f5f5', width: '100%', height: '100%' }}>
|
|
|
|
|
+ <div style={{ paddingTop: '80px' }}>欢迎使用 数字监理员</div>
|
|
|
|
|
+ <div style={{ paddingTop: '40px' }}>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='引用知识库'
|
|
|
|
|
+ name='knowledge_ids'
|
|
|
|
|
+ rules={[{ required: true, message: '知识库不能为空' }]}>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ mode='multiple'
|
|
|
|
|
+ maxCount={MAX_COUNT}
|
|
|
|
|
+ // suffixIcon={suffix}
|
|
|
|
|
+ style={{ width: '300px' }}
|
|
|
|
|
+ placeholder='请选择知识库'
|
|
|
|
|
+ >
|
|
|
|
|
+ {
|
|
|
|
|
+ knowledgeList.map((item, index) => {
|
|
|
|
|
+ return <Option value={item.value} key={index}>
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </Option>
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='调用模型'
|
|
|
|
|
+ name="model"
|
|
|
|
|
+ rules={[{ required: true, message: '模型不能为空' }]}>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ style={{ width: '30%' }}
|
|
|
|
|
+ placeholder='请选择模型'
|
|
|
|
|
+ allowClear={true}
|
|
|
|
|
+ >
|
|
|
|
|
+ {
|
|
|
|
|
+ modelList.map((item, index) => {
|
|
|
|
|
+ return <Option value={item.value} key={index}>
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </Option>
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='max token'
|
|
|
|
|
+ name='max_token'
|
|
|
|
|
+ rules={[{ required: true, message: 'max token不能为空' }]}>
|
|
|
|
|
+ <InputNumber
|
|
|
|
|
+ style={{ width: '100px' }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ {
|
|
|
|
|
+ !isVisible &&
|
|
|
|
|
+ <a onClick={() => {
|
|
|
|
|
+ setIsVisible(!isVisible);
|
|
|
|
|
+ }}>
|
|
|
|
|
+ 更多设置
|
|
|
|
|
+ </a>
|
|
|
|
|
+ }
|
|
|
|
|
+ {isVisible &&
|
|
|
|
|
+ <div>
|
|
|
|
|
+ {isVisibleCus &&
|
|
|
|
|
+ <Space style={{ width: '100%' }} direction="vertical">
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='Top-p'
|
|
|
|
|
+ name='top_p'
|
|
|
|
|
+ >
|
|
|
|
|
+ <TopPDecimalStep />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='Temperature'
|
|
|
|
|
+ name='temperature'
|
|
|
|
|
+ >
|
|
|
|
|
+ <TempDecimalStep />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+
|
|
|
|
|
+ </Space >
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='回答风格'
|
|
|
|
|
+ name='param_desc'
|
|
|
|
|
+ rules={[{ required: true, message: '回答风格不能为空' }]}>
|
|
|
|
|
+ <Radio.Group buttonStyle="solid">
|
|
|
|
|
+ <Radio.Button value='strict'>严谨</Radio.Button>
|
|
|
|
|
+ <Radio.Button value='moderate'>适中</Radio.Button>
|
|
|
|
|
+ <Radio.Button value='flexib'>发散</Radio.Button>
|
|
|
|
|
+ <Radio.Button value='custom'
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ setIsVisibleCus(!isVisibleCus);
|
|
|
|
|
+ }}
|
|
|
|
|
+ >自定义
|
|
|
|
|
+ </Radio.Button>
|
|
|
|
|
+ </Radio.Group>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='展示引用知识'
|
|
|
|
|
+ name='show_recall_result' >
|
|
|
|
|
+ <Switch onChange={onChangeShow} />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='召回方式'
|
|
|
|
|
+ name='recall_method'
|
|
|
|
|
+ rules={[{ required: true, message: '召回方式不能为空' }]}>
|
|
|
|
|
+
|
|
|
|
|
+ <Radio.Group
|
|
|
|
|
+ style={style}
|
|
|
|
|
+ onChange={onChangeRecallMethod}
|
|
|
|
|
+ options={[
|
|
|
|
|
+ { value: 'embedding', label: '向量化检索' },
|
|
|
|
|
+ { value: 'keyword', label: '关键词检索' },
|
|
|
|
|
+ { value: 'mixed', label: '混合检索' },
|
|
|
|
|
+ ]}
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+
|
|
|
|
|
+ <p>重排方式</p>
|
|
|
|
|
+
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='Rerank模型'
|
|
|
|
|
+ name='rerank_status'
|
|
|
|
|
+ valuePropName='checked'
|
|
|
|
|
+ >
|
|
|
|
|
+ <Switch onChange={onChangeModel} />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ {isVisibleRerank &&
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='模型选择'
|
|
|
|
|
+ name='rerank_model_name'
|
|
|
|
|
+ >
|
|
|
|
|
+ <Select
|
|
|
|
|
+ style={{ width: '200px' }}
|
|
|
|
|
+ placeholder='请选择模型'
|
|
|
|
|
+ defaultValue={'默认rerank模型'}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Option value='rerank'>默认rerank模型</Option>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='召回切片数量'
|
|
|
|
|
+ name='slice_config_type'
|
|
|
|
|
+ rules={[{ required: true, message: '召回方式不能为空' }]}>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ style={{ width: '100%' }}
|
|
|
|
|
+ placeholder='请选择'
|
|
|
|
|
+ onChange={onChangeCount}>
|
|
|
|
|
+ <Option value="fixed">手动设置</Option>
|
|
|
|
|
+ <Option value="customized">自动设置</Option>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+
|
|
|
|
|
+ {isVisibleSlice &&
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='召回切片数'
|
|
|
|
|
+ name='slice_count'
|
|
|
|
|
+ rules={[{ required: true, message: '切片数不能为空' }]}>
|
|
|
|
|
+ <InputNumber max={1024} changeOnWheel />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ <FormItem
|
|
|
|
|
+ label='召回切片拼接方式'
|
|
|
|
|
+ name='recall_slice_splicing_method'
|
|
|
|
|
+ >
|
|
|
|
|
+ <TextArea
|
|
|
|
|
+ rows={4}
|
|
|
|
|
+ placeholder="请输入内容"
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Splitter.Panel>
|
|
|
|
|
+ </Splitter>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </Form>
|
|
|
|
|
+ </Spin>
|
|
|
</div>
|
|
</div>
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|