|
|
@@ -0,0 +1,738 @@
|
|
|
+import * as React from 'react';
|
|
|
+import { useLocation } from 'react-router-dom';
|
|
|
+import { observer } from 'mobx-react';
|
|
|
+import './style.less';
|
|
|
+import {
|
|
|
+ Button, Input, Form, Divider, Splitter, Select, InputNumber, InputNumberProps,
|
|
|
+ Radio, Switch, Row, Col, Slider, Space, RadioChangeEvent,
|
|
|
+ Spin, message
|
|
|
+} 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 [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}
|
|
|
+ value = {topPValue}
|
|
|
+ step={0.1}
|
|
|
+ />
|
|
|
+ </Col>
|
|
|
+ <Col span={4}>
|
|
|
+ <InputNumber
|
|
|
+ min={0}
|
|
|
+ max={1}
|
|
|
+ style={{ margin: '0 16px', width: '100px' }}
|
|
|
+ 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}
|
|
|
+ value={tempValue}
|
|
|
+ step={0.01}
|
|
|
+ />
|
|
|
+ </Col>
|
|
|
+ <Col span={4}>
|
|
|
+ <InputNumber
|
|
|
+ min={0}
|
|
|
+ max={1}
|
|
|
+ style={{ margin: '0 16px', width: '100px' }}
|
|
|
+ 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 [name, setName] = React.useState('');
|
|
|
+
|
|
|
+ const style: React.CSSProperties = {
|
|
|
+ display: 'flex',
|
|
|
+ flexDirection: 'column',
|
|
|
+ gap: 8,
|
|
|
+ width: 300,
|
|
|
+ };
|
|
|
+
|
|
|
+ const location = useLocation();
|
|
|
+
|
|
|
+ const init = async (id: string) => {
|
|
|
+ await Promise.all([
|
|
|
+ api.fetchKnowlegde(),
|
|
|
+ // api.fetchModelList(),
|
|
|
+ ])
|
|
|
+ if (id) {
|
|
|
+ await api.fetchDetail(id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ React.useEffect(() => {
|
|
|
+ const id = location?.state?.id;
|
|
|
+ init(id)
|
|
|
+ }, []);
|
|
|
+
|
|
|
+ // 定义一个状态来存储输入框数组
|
|
|
+ const [inputs, setInputs] = React.useState([{ id: 1, value: '' }]);
|
|
|
+
|
|
|
+ // 添加新输入框的函数
|
|
|
+ const addInput = () => {
|
|
|
+ const newId = inputs.length + 1; // 生成新的唯一ID
|
|
|
+ setInputs([...inputs, { id: newId, value: '' }]);
|
|
|
+ };
|
|
|
+
|
|
|
+ const delInput = () => {
|
|
|
+ const newId = inputs.length - 1; // 生成新的唯一ID
|
|
|
+ setInputs(inputs.slice(0, inputs.length - 1));
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理输入变更的函数
|
|
|
+ const handleChange = (id: number, value: string) => {
|
|
|
+ setInputs(inputs.map(input => (input.id === id ? { ...input, value } : input)));
|
|
|
+ };
|
|
|
+
|
|
|
+ // 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.fetchTakaiApplicationDetail(app_id)
|
|
|
+ console.log(res.data);
|
|
|
+ const sd = res.data.questionlist.map((item: any, index: number) => {
|
|
|
+ return {
|
|
|
+ "id": index + 1,
|
|
|
+ "value": item.question,
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ const info = res.data.detail;
|
|
|
+
|
|
|
+ setTopPValue(info.topP as number);
|
|
|
+ setTempValue(info.temperature as number);
|
|
|
+ setName(info.name);
|
|
|
+
|
|
|
+ interface Item2 {
|
|
|
+ index_type_id: number,
|
|
|
+ knowledge_id: string
|
|
|
+ }
|
|
|
+
|
|
|
+ interface Item {
|
|
|
+ show_recall_result: boolean,
|
|
|
+ recall_method: string,
|
|
|
+ rerank_status: boolean,
|
|
|
+ slice_config_type: string,
|
|
|
+ slice_count: number,
|
|
|
+ recall_slice_splicing_method: string,
|
|
|
+ param_desc: string,
|
|
|
+ rerank_model_name: string,
|
|
|
+ rerank_index_type_list: [Item2],
|
|
|
+ recall_index_type_list: [Item2]
|
|
|
+ }
|
|
|
+
|
|
|
+ const data_info: Item = JSON.parse(info.knowledgeInfo);
|
|
|
+
|
|
|
+ if (data_info.param_desc === 'custom') {
|
|
|
+
|
|
|
+ setIsVisibleCus(!isVisibleCus); //自定义回答风格
|
|
|
+ }
|
|
|
+ if (data_info.rerank_status === true) {
|
|
|
+ setIsVisibleRerank(!isVisibleRerank) //模型
|
|
|
+ }
|
|
|
+ //召回切片数量
|
|
|
+ if (data_info.slice_config_type === 'fixed') {
|
|
|
+ setIsVisibleSlice(!isVisibleSlice);
|
|
|
+ } else {
|
|
|
+ setIsVisibleSlice(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ form.setFieldsValue({
|
|
|
+ id: info.id,
|
|
|
+ name: info.name, //应用名称
|
|
|
+ desc: info.desc, //应用描述
|
|
|
+ prompt: info.prompt, //应用提示语
|
|
|
+ top_p: info.topP as number, //topP
|
|
|
+ temperature: info.temperature as number, //温度
|
|
|
+ knowledge_ids: info.knowledgeIds,
|
|
|
+ model: info.model,
|
|
|
+ icon_color: info.icon_color,
|
|
|
+ icon_type: info.icon_type,
|
|
|
+ questionList: sd, //问题列表
|
|
|
+ max_token: info.maxToken, //应用最大token
|
|
|
+ updateDate: info.updateDate, // 更新时间
|
|
|
+
|
|
|
+ param_desc: data_info.param_desc, //回答风格
|
|
|
+ show_recall_result: data_info.show_recall_result, //是否展示召回结果
|
|
|
+ recall_method: data_info.recall_method, //召回方式
|
|
|
+ rerank_status: data_info.rerank_status, //开启rerank
|
|
|
+ rerank_model_name: data_info.rerank_model_name, //模型名称
|
|
|
+ slice_config_type: data_info.slice_config_type, // 召回切片数量
|
|
|
+ slice_count: data_info.slice_count, // 切片数量
|
|
|
+ recall_slice_splicing_method: data_info.recall_slice_splicing_method, // 切片内容
|
|
|
+
|
|
|
+ // rerank_status = 1 rerank_index_type_list
|
|
|
+ // recall_method = 'embedding' || 'mixed' recall_index_type_list
|
|
|
+ //recall_index_type_list: info.recall_index_type_list, //知识库id
|
|
|
+ //rerank_index_type_list: info.rerank_index_type_list, //知识库id
|
|
|
+ })
|
|
|
+ if (sd.length > 0) {
|
|
|
+ setInputs(sd);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
+ } finally {
|
|
|
+ setPageLoading(false);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ //获取知识库列表
|
|
|
+ fetchKnowlegde: async () => {
|
|
|
+ try {
|
|
|
+ const res = await apis.fetchTakaiKnowledgeList();
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ const handleRedioClick = (value: string) => {
|
|
|
+ setIsVisibleCus(false);
|
|
|
+ if (value === 'strict') {
|
|
|
+ setTopPValue(0.5);
|
|
|
+ setTempValue(0.10);
|
|
|
+ } else if (value === 'moderate') {
|
|
|
+ setTopPValue(0.7);
|
|
|
+ setTempValue(0.50);
|
|
|
+ } else if (value === 'flexib') {
|
|
|
+ setTopPValue(0.9);
|
|
|
+ setTempValue(0.90);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className='questionAnswerInfo'>
|
|
|
+ <Spin spinning={pageLoading}>
|
|
|
+ <Form
|
|
|
+ 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>
|
|
|
+ <div>
|
|
|
+ {
|
|
|
+ inputs.map(input => (
|
|
|
+ <div key={input.id} style={{ paddingTop: '10px' }}>
|
|
|
+ <label>问题 {input.id}</label>
|
|
|
+ <Input
|
|
|
+ style={{ width: 300, paddingTop: 8, marginLeft: 20 }}
|
|
|
+ type="text"
|
|
|
+ value={input.value}
|
|
|
+ onChange={e => handleChange(input.id, e.target.value)}
|
|
|
+ />
|
|
|
+ <PlusCircleOutlined style={{ marginLeft: 20 }} onClick={addInput} />
|
|
|
+ <MinusCircleOutlined style={{ marginLeft: 20 }} onClick={delInput} />
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <br />
|
|
|
+ <Button type='primary' onClick={() => {
|
|
|
+ form.validateFields(['name', 'desc']).then(async (values) => {
|
|
|
+ setStep(2);
|
|
|
+ setInputs(inputs);
|
|
|
+ }).catch((error) => {
|
|
|
+ console.error(error);
|
|
|
+ });
|
|
|
+ }} >
|
|
|
+ 下一步
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style={{ display: step === 2 ? 'block' : 'none' }} className='questionAnswerInfo-content'>
|
|
|
+ <div style={{ paddingBottom: '10px', 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;
|
|
|
+ console.log(values, 'values');
|
|
|
+ // 问题列表
|
|
|
+ const question: string[] = [];
|
|
|
+ if (inputs) {
|
|
|
+ inputs.map((item, index) => {
|
|
|
+ question.push(item.value);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ console.log(question, 'question');
|
|
|
+ interface Item {
|
|
|
+ index_type_id: number,
|
|
|
+ knowledge_id: string
|
|
|
+ }
|
|
|
+ const indexTypeList: Item[] = [];
|
|
|
+ if (values.knowledge_ids && values.knowledge_ids.length > 0) {
|
|
|
+ const index_type: Item = {
|
|
|
+ index_type_id: 0,
|
|
|
+ knowledge_id: values.knowledge_ids
|
|
|
+ };
|
|
|
+ indexTypeList.push(index_type);
|
|
|
+ }
|
|
|
+
|
|
|
+ const data_info = {
|
|
|
+ param_desc: values.param_desc, //回答风格
|
|
|
+ show_recall_result: values.show_recall_result, //是否展示召回结果
|
|
|
+ recall_method: values.recall_method, //召回方式
|
|
|
+ rerank_status: values.rerank_status, //开启rerank
|
|
|
+ rerank_model_name: values.rerank_model_name, //模型名称
|
|
|
+ slice_config_type: values.slice_config_type, // 召回切片数量
|
|
|
+ slice_count: values.slice_count, // 切片数量
|
|
|
+ recall_slice_splicing_method: values.recall_slice_splicing_method, // 切片内容
|
|
|
+ rerank_index_type_list: values.rerank_status === true ? indexTypeList : [], //知识库id
|
|
|
+ recall_index_type_list: values.recall_method === 'embedding' || 'mixed' ? indexTypeList : []
|
|
|
+ // rerank_status = 1 rerank_index_type_list
|
|
|
+ // recall_method = 'embedding' || 'embedding' recall_index_type_list
|
|
|
+ };
|
|
|
+ // const knowledgeIds: string[] = [];
|
|
|
+ // knowledgeIds.push(values.knowledge_ids);
|
|
|
+ const info = {
|
|
|
+ id: values.id,
|
|
|
+ name: values.name, //应用名称
|
|
|
+ desc: values.desc, //应用描述
|
|
|
+ prompt: values.prompt, //应用提示语
|
|
|
+ top_p: topPValue.toString(), //topP
|
|
|
+ temperature: tempValue.toString(), //温度
|
|
|
+ knowledge_ids: values.knowledge_ids,
|
|
|
+ slice_count: values.slice_count,
|
|
|
+ model: values.model,
|
|
|
+ icon_color: values.icon_color,
|
|
|
+ icon_type: values.icon_type,
|
|
|
+ questionList: question,
|
|
|
+ knowledge_info: JSON.stringify(data_info),
|
|
|
+ max_token: values.max_token, //应用最大token
|
|
|
+ };
|
|
|
+ const id = location?.state?.id;
|
|
|
+ let res = null;
|
|
|
+ if (id) {
|
|
|
+ // 编辑应用
|
|
|
+ console.log(info, 'info data');
|
|
|
+ res = await apis.modifyTakaiApplicationApi(id, info);
|
|
|
+ } else {
|
|
|
+ // 创建应用
|
|
|
+ res = await await apis.createTakaiApplicationApi(info);
|
|
|
+ }
|
|
|
+ if (res.code !== 200) {
|
|
|
+ message.error(res.data.message);
|
|
|
+ } else {
|
|
|
+ message.success('操作成功')
|
|
|
+ router.navigate({ pathname: '/takai/questionAnswer' });
|
|
|
+ }
|
|
|
+ }).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 style={{ width: '100%', height: '100%' }}>
|
|
|
+ <h2>Prompt编写</h2>
|
|
|
+ <div style={{ paddingTop: '20px' }}>
|
|
|
+ <TextArea
|
|
|
+ autoSize
|
|
|
+ readOnly
|
|
|
+ placeholder="编写Prompt过程中可以引入2项变量:{{知识}} 代表知识库中检索到的知识内容, {{用户}}代表用户输入的内容。您可以在编写Prompt过程中将变量拼接在合适的位置。插入:{{知识}} 插入:{{用户}}"
|
|
|
+ style={{ width: '100%', height: '300px' }}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <Divider plain></Divider>
|
|
|
+ <div >
|
|
|
+ <FormItem name='prompt'
|
|
|
+ rules={[{ required: true, message: '提示词不能为空' }]}>
|
|
|
+ <TextArea
|
|
|
+ placeholder="提示词"
|
|
|
+ rows={50}
|
|
|
+ />
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </Splitter.Panel>
|
|
|
+ <Splitter.Panel defaultSize="60%">
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', background: '#f5f5f5', width: '100%', height: '100%' }}>
|
|
|
+ <div style={{ width: '50%', height: '100%' }}>
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', paddingTop: '50px', fontSize: '16px' }}>
|
|
|
+ 欢迎使用 {name}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div style={{
|
|
|
+ display: 'flex', justifyContent: 'center', alignItems: 'center',
|
|
|
+ paddingTop: '20px'
|
|
|
+ }}>
|
|
|
+ <FormItem
|
|
|
+ label='引用知识库'
|
|
|
+ name='knowledge_ids'
|
|
|
+ rules={[{ required: true, message: '知识库不能为空' }]}>
|
|
|
+ <Select
|
|
|
+ // mode='multiple'
|
|
|
+ // maxCount={MAX_COUNT}
|
|
|
+ // suffixIcon={suffix}
|
|
|
+ style={{ width: '300px', height: '48px' }}
|
|
|
+ placeholder='请选择知识库'
|
|
|
+ >
|
|
|
+ {
|
|
|
+ knowledgeList.map((item, index) => {
|
|
|
+ return <Option value={item.value} key={index}>
|
|
|
+ {item.label}
|
|
|
+ </Option>
|
|
|
+ })
|
|
|
+ }
|
|
|
+ </Select>
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='调用模型'
|
|
|
+ name="model"
|
|
|
+ rules={[{ required: true, message: '模型不能为空' }]}>
|
|
|
+ <Select
|
|
|
+ placeholder='请选择模型'
|
|
|
+ allowClear={true}
|
|
|
+ style={{ width: '300px', height: '48px' }}
|
|
|
+ >
|
|
|
+ <Option value='DeepSeek-R1-Distill-Qwen-14B'>DeepSeek-R1-Distill-Qwen-14B</Option>
|
|
|
+ {/* {
|
|
|
+ modelList.map((item, index) => {
|
|
|
+ return <Option value={item.value} key={index}>
|
|
|
+ {item.label}
|
|
|
+ </Option>
|
|
|
+ })
|
|
|
+ } */}
|
|
|
+ </Select>
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='max token'
|
|
|
+ name='max_token'
|
|
|
+ rules={[{ required: true, message: 'max token不能为空' }]}>
|
|
|
+ <InputNumber
|
|
|
+ className='questionAnswerInfo-content-title'
|
|
|
+ />
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {
|
|
|
+ !isVisible &&
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <a onClick={() => {
|
|
|
+ setIsVisible(!isVisible);
|
|
|
+ }} className='questionAnswerInfo-content-title'>
|
|
|
+ 更多设置
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ {isVisible &&
|
|
|
+ <div>
|
|
|
+ {isVisibleCus &&
|
|
|
+ <Space style={{ width: '100%' }} direction="vertical">
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='Top-p'
|
|
|
+ name='topP'
|
|
|
+ style={{ width: '300px' }}
|
|
|
+ >
|
|
|
+ <TopPDecimalStep />
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='Temperature'
|
|
|
+ name='temperature'
|
|
|
+ style={{ width: '300px' }}
|
|
|
+ >
|
|
|
+ <TempDecimalStep />
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ </Space >
|
|
|
+ }
|
|
|
+
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='回答风格'
|
|
|
+ name='param_desc'
|
|
|
+ rules={[{ required: true, message: '回答风格不能为空' }]}>
|
|
|
+ <Radio.Group buttonStyle="solid"
|
|
|
+ className='questionAnswerInfo-content-title'>
|
|
|
+ <Radio.Button onClick={() => {
|
|
|
+ handleRedioClick('strict')
|
|
|
+ }} value='strict'>严谨</Radio.Button>
|
|
|
+ <Radio.Button onClick={() => {
|
|
|
+ handleRedioClick('moderate')
|
|
|
+ }} value='moderate'>适中</Radio.Button>
|
|
|
+ <Radio.Button onClick={() => {
|
|
|
+ handleRedioClick('flexib')
|
|
|
+ }} value='flexib'>发散</Radio.Button>
|
|
|
+ <Radio.Button value='custom'
|
|
|
+ onClick={() => {
|
|
|
+ setIsVisibleCus(!isVisibleCus);
|
|
|
+ setTopPValue(0.1);
|
|
|
+ setTempValue(0.01);
|
|
|
+ }}
|
|
|
+ >自定义
|
|
|
+ </Radio.Button>
|
|
|
+ </Radio.Group>
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='展示引用知识'
|
|
|
+ name='show_recall_result'
|
|
|
+ className='questionAnswerInfo-content-title'>
|
|
|
+ <Switch onChange={onChangeShow} />
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <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>
|
|
|
+ </div>
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <div className='questionAnswerInfo-content-title'>重排方式</div>
|
|
|
+ </div>
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='Rerank模型'
|
|
|
+ name='rerank_status'
|
|
|
+ valuePropName='checked'
|
|
|
+ className='questionAnswerInfo-content-title'
|
|
|
+ >
|
|
|
+ <Switch onChange={onChangeModel} />
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ {isVisibleRerank &&
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='模型选择'
|
|
|
+ name='rerank_model_name'
|
|
|
+ >
|
|
|
+ <Select
|
|
|
+ style={{ width: '300px', height: '48px' }}
|
|
|
+ placeholder='请选择模型'
|
|
|
+ // defaultValue={'rerank'}
|
|
|
+ >
|
|
|
+ <Option value='rerank'>默认rerank模型</Option>
|
|
|
+ </Select>
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='召回切片数量'
|
|
|
+ name='slice_config_type'
|
|
|
+ rules={[{ required: true, message: '召回方式不能为空' }]}>
|
|
|
+ <Select
|
|
|
+ style={{ width: '300px', height: '48px' }}
|
|
|
+ placeholder='请选择'
|
|
|
+ onChange={onChangeCount}>
|
|
|
+ <Option value="fixed">手动设置</Option>
|
|
|
+ <Option value="customized">自动设置</Option>
|
|
|
+ </Select>
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {isVisibleSlice &&
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='召回切片数'
|
|
|
+ name='slice_count'
|
|
|
+ rules={[{ required: true, message: '切片数不能为空' }]}>
|
|
|
+ <InputNumber max={1024} changeOnWheel className='questionAnswerInfo-content-title' />
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
|
|
+ <FormItem
|
|
|
+ label='召回切片拼接方式'
|
|
|
+ name='recall_slice_splicing_method'
|
|
|
+ >
|
|
|
+ <TextArea
|
|
|
+ rows={4}
|
|
|
+ className='questionAnswerInfo-content-title'
|
|
|
+ placeholder="请输入内容"
|
|
|
+ />
|
|
|
+ </FormItem>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Splitter.Panel>
|
|
|
+ </Splitter>
|
|
|
+ </div>
|
|
|
+ </Form>
|
|
|
+ </Spin>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default observer(QuestionAnswerInfo);
|