index.tsx 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. import * as React from 'react';
  2. import { useLocation } from 'react-router-dom';
  3. import { observer } from 'mobx-react';
  4. import './style.less';
  5. import {
  6. Button, Input, Form, Divider, Splitter, Select, InputNumber, InputNumberProps,
  7. Radio, Switch, Row, Col, Slider, Space, RadioChangeEvent,
  8. Spin, message
  9. } from 'antd';
  10. import { PlusCircleOutlined, MinusCircleOutlined } from '@ant-design/icons';
  11. import { apis } from '@/apis';
  12. import router from '@/router';
  13. import LocalStorage from '@/LocalStorage';
  14. const { TextArea } = Input;
  15. const FormItem = Form.Item;
  16. const { Option } = Select;
  17. const MAX_COUNT = 10;
  18. const Index = 1;
  19. const QuestionAnswerInfo: React.FC = () => {
  20. const [form] = Form.useForm();
  21. // top_p
  22. const [topPValue, setTopPValue] = React.useState(0.1);
  23. const TopPDecimalStep: React.FC = () => {
  24. const onChange: InputNumberProps['onChange'] = (value) => {
  25. if (Number.isNaN(value)) {
  26. return;
  27. }
  28. setTopPValue(value as number);
  29. };
  30. return (
  31. <Row>
  32. <Col span={12}>
  33. <Slider
  34. min={0}
  35. max={1}
  36. onChange={onChange}
  37. // value={typeof topPValue === 'number' ? topPValue : 0}
  38. value={topPValue}
  39. step={0.1}
  40. />
  41. </Col>
  42. <Col span={4}>
  43. <InputNumber
  44. min={0}
  45. max={1}
  46. style={{ margin: '0 16px', width: '100px' }}
  47. step={0.01}
  48. value={topPValue}
  49. onChange={onChange}
  50. />
  51. </Col>
  52. </Row>
  53. );
  54. };
  55. const [tempValue, setTempValue] = React.useState(0.01);
  56. // temperature
  57. const TempDecimalStep: React.FC = () => {
  58. const onChange: InputNumberProps['onChange'] = (value) => {
  59. if (Number.isNaN(value)) {
  60. return;
  61. }
  62. setTempValue(value as number);
  63. };
  64. return (
  65. <Row>
  66. <Col span={12}>
  67. <Slider
  68. min={0}
  69. max={1}
  70. onChange={onChange}
  71. // value={typeof tempValue === 'number' ? tempValue : 0}
  72. value={tempValue}
  73. step={0.01}
  74. />
  75. </Col>
  76. <Col span={4}>
  77. <InputNumber
  78. min={0}
  79. max={1}
  80. style={{ margin: '0 16px', width: '100px' }}
  81. step={0.01}
  82. value={tempValue}
  83. onChange={onChange}
  84. />
  85. </Col>
  86. </Row>
  87. );
  88. };
  89. type ModelList = {
  90. label: string,
  91. value: string,
  92. }[];
  93. type KnowledgeList = {
  94. label: string,
  95. value: string,
  96. }[];
  97. type AppTypeList = {
  98. label: string,
  99. value: string,
  100. }[];
  101. const [step, setStep] = React.useState(1);
  102. const [pageLoading, setPageLoading] = React.useState(false);
  103. const [modelList, setModelList] = React.useState<ModelList>([]);
  104. const [knowledgeList, setKnowledgeList] = React.useState<KnowledgeList>([]);
  105. const [isVisible, setIsVisible] = React.useState(false);
  106. const [isVisibleCus, setIsVisibleCus] = React.useState(false);
  107. const [isVisibleSlice, setIsVisibleSlice] = React.useState(false);
  108. const [isVisibleRerank, setIsVisibleRerank] = React.useState(false);
  109. const [isDeepThinkVisible, setIsDeepThinkVisible] = React.useState(false);
  110. const [name, setName] = React.useState('');
  111. const [appTypeList, setAppTypeList] = React.useState<AppTypeList>([]);
  112. const [updateFlag, setUpdateFlag] = React.useState<boolean>();
  113. const [createFlag, setCreateFlag] = React.useState<boolean>();
  114. const [appProjectList, setAppProjectList] = React.useState<AppTypeList>([]);
  115. const [isAppPro, setIsAppPro] = React.useState<boolean>(false);
  116. const [selectedAppProId, setSelectedAppProId] = React.useState<string | undefined>(undefined); // 新增状态变量用于绑定 Select 值
  117. const style: React.CSSProperties = {
  118. display: 'flex',
  119. flexDirection: 'column',
  120. gap: 8,
  121. width: 300,
  122. };
  123. const location = useLocation();
  124. const init = async (id: string) => {
  125. await Promise.all([
  126. api.fetchKnowlegde(),
  127. api.fetchAppType(),
  128. // api.fetchModelList(),
  129. api.fetchAppProType(),
  130. ])
  131. if (id) {
  132. await api.fetchDetail(id);
  133. }
  134. }
  135. React.useEffect(() => {
  136. const id = location?.state?.id;
  137. init(id);
  138. const uFlag = LocalStorage.getStatusFlag('deepseek:application:update');
  139. setUpdateFlag(uFlag);
  140. const cFlag = LocalStorage.getStatusFlag('deepseek:application:create');
  141. setCreateFlag(cFlag);
  142. }, []);
  143. // 定义一个状态来存储输入框数组
  144. const [inputs, setInputs] = React.useState([{ id: 1, value: '' }]);
  145. // 添加新输入框的函数
  146. const addInput = () => {
  147. const newId = inputs.length + 1; // 生成新的唯一ID
  148. setInputs([...inputs, { id: newId, value: '' }]);
  149. };
  150. const delInput = () => {
  151. const newId = inputs.length - 1; // 生成新的唯一ID
  152. setInputs(inputs.slice(0, inputs.length - 1));
  153. };
  154. // 处理输入变更的函数
  155. const handleChange = (id: number, value: string) => {
  156. setInputs(inputs.map(input => (input.id === id ? { ...input, value } : input)));
  157. };
  158. const handleAppChange = (typeId: number) => {
  159. if (typeId === 41) { // 根据实际值进行判断
  160. setIsAppPro(true);
  161. } else {
  162. setIsAppPro(false);
  163. }
  164. };
  165. // const onChange: InputNumberProps['onChange'] = (value) => {
  166. // console.log('changed', value);
  167. // };
  168. const onChangeShow = (checked: boolean) => {
  169. console.log(`switch to ${checked}`);
  170. };
  171. const onChangeModel = (checked: boolean) => {
  172. setIsVisibleRerank(!isVisibleRerank);
  173. };
  174. const onChangeCount = (value: string) => {
  175. if (value === 'fixed') {
  176. setIsVisibleSlice(!isVisibleSlice);
  177. } else {
  178. setIsVisibleSlice(false);
  179. }
  180. };
  181. // 召回方式
  182. const onChangeRecallMethod = (e: RadioChangeEvent) => {
  183. };
  184. // 获取应用详情
  185. const api = {
  186. fetchDetail: async (app_id: string) => {
  187. setPageLoading(true);
  188. try {
  189. const res = await apis.fetchTakaiApplicationDetail(app_id)
  190. console.log(res.data);
  191. const sd = res.data.questionlist.map((item: any, index: number) => {
  192. return {
  193. "id": index + 1,
  194. "value": item.question,
  195. }
  196. });
  197. const info = res.data.detail;
  198. setTopPValue(info.topP as number);
  199. setTempValue(info.temperature as number);
  200. setName(info.name);
  201. interface Item2 {
  202. index_type_id: number,
  203. knowledge_id: string
  204. }
  205. interface Item {
  206. show_recall_result: boolean,
  207. recall_method: string,
  208. rerank_status: boolean,
  209. slice_config_type: string,
  210. slice_count: number,
  211. recall_slice_splicing_method: string,
  212. param_desc: string,
  213. rerank_model_name: string,
  214. rerank_index_type_list: [Item2],
  215. recall_index_type_list: [Item2]
  216. }
  217. const data_info: Item = JSON.parse(info.knowledgeInfo);
  218. if (data_info.param_desc === 'custom') {
  219. setIsVisibleCus(!isVisibleCus); //自定义回答风格
  220. }
  221. if (data_info.rerank_status === true) {
  222. setIsVisibleRerank(!isVisibleRerank) //模型
  223. }
  224. //召回切片数量
  225. if (data_info.slice_config_type === 'fixed') {
  226. setIsVisibleSlice(!isVisibleSlice);
  227. } else {
  228. setIsVisibleSlice(false);
  229. }
  230. if(info.typeId === 42 || info.typeId === 43){
  231. setIsAppPro(true);
  232. }else{
  233. setIsAppPro(false);
  234. }
  235. if(info.model === 'Qwen3-30B') {
  236. setIsDeepThinkVisible(true);
  237. } else {
  238. setIsDeepThinkVisible(false);
  239. }
  240. form.setFieldsValue({
  241. id: info.id,
  242. name: info.name, //应用名称
  243. desc: info.desc, //应用描述
  244. prompt: info.prompt, //应用提示语
  245. top_p: info.topP as number, //topP
  246. temperature: info.temperature as number, //温度
  247. knowledge_ids: info.knowledgeIds,
  248. model: info.model,
  249. isDeepThink: info.isDeepThink,
  250. icon_color: info.icon_color,
  251. icon_type: info.icon_type,
  252. questionList: sd, //问题列表
  253. max_token: info.maxToken, //应用最大token
  254. updateDate: info.updateDate, // 更新时间
  255. appProId: info.typeId, //项目级应用类型
  256. typeId: info.typeId === 42 || info.typeId === 43 ? 41 : info.typeId, //应用类型
  257. param_desc: data_info.param_desc, //回答风格
  258. show_recall_result: data_info.show_recall_result, //是否展示召回结果
  259. recall_method: data_info.recall_method, //召回方式
  260. rerank_status: data_info.rerank_status, //开启rerank
  261. rerank_model_name: data_info.rerank_model_name, //模型名称
  262. slice_config_type: data_info.slice_config_type, // 召回切片数量
  263. slice_count: data_info.slice_count, // 切片数量
  264. recall_slice_splicing_method: data_info.recall_slice_splicing_method, // 切片内容
  265. // rerank_status = 1 rerank_index_type_list
  266. // recall_method = 'embedding' || 'mixed' recall_index_type_list
  267. //recall_index_type_list: info.recall_index_type_list, //知识库id
  268. //rerank_index_type_list: info.rerank_index_type_list, //知识库id
  269. })
  270. if (sd.length > 0) {
  271. setInputs(sd);
  272. }
  273. } catch (error) {
  274. console.error(error);
  275. } finally {
  276. setPageLoading(false);
  277. }
  278. },
  279. //获取知识库列表
  280. fetchKnowlegde: async () => {
  281. try {
  282. const res = await apis.fetchTakaiKnowledgeList();
  283. const list = res.data.map((item: any) => {
  284. return {
  285. label: item.name,
  286. value: item.knowledgeId,
  287. }
  288. });
  289. setKnowledgeList(list);
  290. } catch (error: any) {
  291. console.error(error);
  292. }
  293. },
  294. // 获取应用类型
  295. fetchAppType: async () => {
  296. try {
  297. const res = await apis.fetchTakaiAppTypeList('app_type');
  298. const list = res.data.map((item: any) => {
  299. return {
  300. label: item.dictLabel,
  301. value: item.dictCode,
  302. }
  303. });
  304. setAppTypeList(list);
  305. } catch (error: any) {
  306. console.error(error);
  307. }
  308. },
  309. // 获取模型列表
  310. fetchModelList: async () => {
  311. try {
  312. const res = await apis.fetchModelList();
  313. const list = res.data.data.map((item: any) => {
  314. return {
  315. label: item.modelName,
  316. value: item.modelCode,
  317. }
  318. });
  319. setModelList(list);
  320. } catch (error: any) {
  321. console.error(error);
  322. }
  323. },
  324. // 项目级应用下的类型
  325. fetchAppProType: async () => {
  326. try {
  327. const res = await apis.fetchTakaiAppTypeList('project_type');
  328. const list = res.data.map((item: any) => {
  329. return {
  330. label: item.dictLabel,
  331. value: item.dictCode,
  332. }
  333. });
  334. console.log(list, 'project_type');
  335. setAppProjectList(list);
  336. } catch (error: any) {
  337. console.error(error);
  338. }
  339. },
  340. }
  341. const handleRedioClick = (value: string) => {
  342. setIsVisibleCus(false);
  343. if (value === 'strict') {
  344. setTopPValue(0.5);
  345. setTempValue(0.01);
  346. } else if (value === 'moderate') {
  347. setTopPValue(0.7);
  348. setTempValue(0.50);
  349. } else if (value === 'flexib') {
  350. setTopPValue(0.9);
  351. setTempValue(0.90);
  352. }
  353. }
  354. // const [modelValue, setModelValue] = useState('');
  355. //
  356. // // 监听表单中模型值的变化
  357. // useEffect(() => {
  358. // const currentModel = form.getFieldValue('model');
  359. // setModelValue(currentModel);
  360. // }, [form]); // 依赖表单实例,当表单值变化时重新执行
  361. return (
  362. <div className='questionAnswerInfo'>
  363. <Spin spinning={pageLoading}>
  364. <Form
  365. form={form}
  366. layout='vertical'
  367. initialValues={{
  368. max_token: 1024
  369. }}
  370. >
  371. <div style={{ display: step === 1 ? 'block' : 'none' }} className='questionAnswerInfo-content'>
  372. <FormItem
  373. label='问答应用名称'
  374. name='name'
  375. rules={[{ required: true, message: '问答应用名称不能为空' }]}
  376. >
  377. <Input placeholder="应用名称" style={{ width: 646, padding: 8 }} />
  378. </FormItem>
  379. <FormItem
  380. label='应用类型'
  381. name='typeId'
  382. >
  383. <Select
  384. style={{ width: '300px', height: '48px' }}
  385. placeholder='请选应用类型'
  386. onChange={handleAppChange}
  387. allowClear={true}
  388. >
  389. {
  390. appTypeList.map((item, index) => {
  391. return <Option value={item.value} key={index}>
  392. {item.label}
  393. </Option>
  394. })
  395. }
  396. </Select>
  397. </FormItem>
  398. {
  399. isAppPro &&
  400. <FormItem
  401. label='类型'
  402. name='appProId'
  403. rules={[{ required: true, message: '项目类型不能为空' }]}
  404. >
  405. <Select
  406. style={{ width: '300px', height: '48px' }}
  407. placeholder='请选择项目类型'
  408. value={selectedAppProId}
  409. onChange={(value) => setSelectedAppProId(value)}
  410. >
  411. {appProjectList.map((item, index) => (
  412. <Option value={item.value} key={index}>
  413. {item.label}
  414. </Option>
  415. ))}
  416. </Select>
  417. </FormItem>
  418. }
  419. <FormItem
  420. label='问答应用描述'
  421. name='desc'
  422. rules={[{ required: true, message: '问答应用描述不能为空' }]}
  423. >
  424. <TextArea
  425. showCount
  426. maxLength={500}
  427. placeholder="请输入描述"
  428. style={{ height: 120, resize: 'none', width: 646 }}
  429. />
  430. </FormItem>
  431. <div>
  432. <h4>添加预设问题</h4>
  433. <div>
  434. {
  435. inputs.map(input => (
  436. <div key={input.id} style={{ paddingTop: '10px' }}>
  437. <label>问题 {input.id}</label>
  438. <Input
  439. style={{ width: 300, paddingTop: 8, marginLeft: 20 }}
  440. type="text"
  441. value={input.value}
  442. onChange={e => handleChange(input.id, e.target.value)}
  443. />
  444. <PlusCircleOutlined style={{ marginLeft: 20 }} onClick={addInput} />
  445. <MinusCircleOutlined style={{ marginLeft: 20 }} onClick={delInput} />
  446. </div>
  447. ))}
  448. </div>
  449. </div>
  450. <br />
  451. <Button type='primary' onClick={() => {
  452. form.validateFields(['name', 'desc', 'appProId']).then(async (values) => {
  453. setStep(2);
  454. setInputs(inputs);
  455. }).catch((error) => {
  456. console.error(error);
  457. });
  458. }} >
  459. 下一步
  460. </Button>
  461. </div>
  462. <div style={{ display: step === 2 ? 'block' : 'none' }} className='questionAnswerInfo-content'>
  463. <div style={{ paddingBottom: '10px', display: 'flex', justifyContent: 'flex-end' }}>
  464. <div>
  465. <Button
  466. style={{ background: '#f5f5f5' }}
  467. onClick={() => {
  468. setStep(1);
  469. }}
  470. >
  471. 上一步
  472. </Button>
  473. {
  474. createFlag &&
  475. <Button
  476. type='primary'
  477. onClick={() => {
  478. form.validateFields().then(async (values) => {
  479. const data = values;
  480. console.log(values, 'values');
  481. // 问题列表
  482. const question: string[] = [];
  483. if (inputs) {
  484. inputs.map((item, index) => {
  485. question.push(item.value);
  486. });
  487. }
  488. console.log(question, 'question');
  489. interface Item {
  490. index_type_id: number,
  491. knowledge_id: string
  492. }
  493. const indexTypeList: Item[] = [];
  494. if (values.knowledge_ids && values.knowledge_ids.length > 0) {
  495. const index_type: Item = {
  496. index_type_id: 0,
  497. knowledge_id: values.knowledge_ids
  498. };
  499. indexTypeList.push(index_type);
  500. }
  501. const userInfo = LocalStorage.getUserInfo();
  502. const userId = (userInfo?.id ?? '').toString();
  503. const data_info = {
  504. param_desc: values.param_desc, //回答风格
  505. show_recall_result: values.show_recall_result, //是否展示召回结果
  506. recall_method: values.recall_method, //召回方式
  507. rerank_status: values.rerank_status, //开启rerank
  508. rerank_model_name: values.rerank_model_name, //模型名称
  509. slice_config_type: values.slice_config_type, // 召回切片数量
  510. slice_count: values.slice_count, // 切片数量
  511. recall_slice_splicing_method: values.recall_slice_splicing_method, // 切片内容
  512. rerank_index_type_list: values.rerank_status === true ? indexTypeList : [], //知识库id
  513. recall_index_type_list: values.recall_method === 'embedding' || 'mixed' ? indexTypeList : []
  514. // rerank_status = 1 rerank_index_type_list
  515. // recall_method = 'embedding' || 'embedding' recall_index_type_list
  516. };
  517. // const knowledgeIds: string[] = [];
  518. // knowledgeIds.push(values.knowledge_ids);
  519. console.log(values.appProId , 'values.appProId ');
  520. const info = {
  521. id: values.id,
  522. name: values.name, //应用名称
  523. desc: values.desc, //应用描述
  524. prompt: values.prompt, //应用提示语
  525. top_p: topPValue.toString(), //topP
  526. temperature: tempValue.toString(), //温度
  527. knowledge_ids: values.knowledge_ids,
  528. slice_count: values.slice_count,
  529. model: values.model,
  530. isDeepThink:values.isDeepThink,
  531. icon_color: values.icon_color,
  532. icon_type: values.icon_type,
  533. questionList: question,
  534. knowledge_info: JSON.stringify(data_info),
  535. max_token: values.max_token, //应用最大token
  536. typeId: values.appProId === 42 || values.appProId === 43 ? values.appProId : values.typeId, // 应用类型
  537. userId: userId, // 用户id
  538. };
  539. console.log(info, 'info data');
  540. const id = location?.state?.id;
  541. let res = null;
  542. if (id) {
  543. // 编辑应用
  544. res = await apis.modifyTakaiApplicationApi(id, info);
  545. } else {
  546. // 创建应用
  547. res = await await apis.createTakaiApplicationApi(info);
  548. }
  549. console.log(res, 'create or modify');
  550. if (res.data === 9) {
  551. message.error('没有配置审核人,请联系管理员');
  552. } else if (res.data === 1) {
  553. message.success('操作成功')
  554. } else {
  555. message.error('操作失败');
  556. }
  557. router.navigate({ pathname: '/deepseek/questionAnswer' });
  558. }).catch((error) => {
  559. console.error(error);
  560. error.errorFields && error.errorFields.map((item: any) => {
  561. console.log(item, 'item');
  562. message.error(`字段 ${item.name} ${item.errors[0]}`);
  563. });
  564. });
  565. }}
  566. >提交应用</Button>
  567. }
  568. </div>
  569. </div>
  570. <Splitter style={{ height: '100%', boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}>
  571. <Splitter.Panel defaultSize="40%">
  572. <div style={{ width: '100%', height: '100%' }}>
  573. <h2>Prompt编写</h2>
  574. <div style={{ paddingTop: '20px' }}>
  575. <TextArea
  576. autoSize
  577. readOnly
  578. placeholder="编写Prompt过程中可以引入2项变量:{{知识}} 代表知识库中检索到的知识内容, {{用户}}代表用户输入的内容。您可以在编写Prompt过程中将变量拼接在合适的位置。插入:{{知识}} 插入:{{用户}}"
  579. style={{ width: '100%', height: '300px' }}
  580. />
  581. </div>
  582. <Divider plain></Divider>
  583. <div >
  584. <FormItem name='prompt'
  585. initialValue={`你是一位知识检索助手,你必须并且只能从我发送的众多知识片段中寻找能够解决用户输入问题的最优答案,并且在执行任务的过程中严格执行规定的要求。
  586. 知识片段如下:
  587. {{知识}}
  588. 规定要求:
  589. - 找到答案就仅使用知识片段中的原文回答用户的提问;
  590. - 找不到答案就用自身知识并且告诉用户该信息不是来自文档;
  591. - 所引用的文本片段中所包含的示意图占位符必须进行返回,占位符格式参考:【示意图序号_编号】
  592. - 严禁输出任何知识片段中不存在的示意图占位符;
  593. - 输出的内容必须删除其中包含的任何图注、序号等信息。例如:“进入登录页面(图1.1)”需要从文字中删除图序,回复效果为:“进入登录页面”;“如图所示1.1”,回复效果为:“如图所示”;
  594. - 格式规范
  595. - 文档中会出现包含表格的情况,表格是以图片标识符的形式呈现,表格中缺失数据时候返回空单元格;
  596. - 如果需要用到表格中的数据,以代码块语法输出表格中的数据;
  597. - 避免使用代码块语法回复信息;
  598. - 回复的开头语不要输出诸如:“我想”,“我认为”,“think”等相关语义的文本。
  599. 严格执行规定要求,不要复述问题,直接开始回答。
  600. 用户输入问题:
  601. {{用户}}`}
  602. rules={[{ required: true, message: '提示词不能为空' }]}>
  603. <TextArea
  604. placeholder="提示词"
  605. rows={50}
  606. />
  607. </FormItem>
  608. </div>
  609. </div>
  610. </Splitter.Panel>
  611. <Splitter.Panel defaultSize="60%">
  612. <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', background: '#f5f5f5', width: '100%', height: '100%' }}>
  613. <div style={{ width: '50%', height: '100%' }}>
  614. <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', paddingTop: '50px', fontSize: '16px' }}>
  615. 欢迎使用 {name}
  616. </div>
  617. <div style={{
  618. display: 'flex', justifyContent: 'center', alignItems: 'center',
  619. paddingTop: '20px'
  620. }}>
  621. <FormItem
  622. label='引用知识库'
  623. name='knowledge_ids'
  624. rules={[{ required: true, message: '知识库不能为空' }]}>
  625. <Select
  626. // mode='multiple'
  627. // maxCount={MAX_COUNT}
  628. // suffixIcon={suffix}
  629. style={{ width: '300px', height: '48px' }}
  630. placeholder='请选择知识库'
  631. >
  632. {
  633. knowledgeList.map((item, index) => {
  634. return <Option value={item.value} key={index}>
  635. {item.label}
  636. </Option>
  637. })
  638. }
  639. </Select>
  640. </FormItem>
  641. </div>
  642. <div style={{display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
  643. <FormItem
  644. label='调用模型'
  645. name="model"
  646. rules={[{required: true, message: '模型不能为空'}]}>
  647. <Select
  648. placeholder='请选择模型'
  649. allowClear={true}
  650. style={{width: '300px', height: '48px'}}
  651. onChange={(value) => {
  652. if (value === 'Qwen3-30B') {
  653. setIsDeepThinkVisible(true);
  654. } else {
  655. setIsDeepThinkVisible(false);
  656. }
  657. }}
  658. >
  659. <Option value='Qwen3-30B'>Qwen3-30B</Option>
  660. <Option value='Qwen2-72B'>Qwen2-72B</Option>
  661. </Select>
  662. </FormItem>
  663. </div>
  664. <div style={{
  665. display: isDeepThinkVisible ? 'flex' : 'none',
  666. justifyContent: 'center',
  667. alignItems: 'center'
  668. }}>
  669. <FormItem
  670. label='深度思考'
  671. name='isDeepThink'
  672. rules={[{ required: true, message: '请选择是否深度思考' }]}>
  673. <Radio.Group buttonStyle="solid" style={{width: '300px'}}>
  674. <Radio.Button value='Y'>是</Radio.Button>
  675. <Radio.Button value='N'>否</Radio.Button>
  676. </Radio.Group>
  677. </FormItem>
  678. </div>
  679. <div style={{
  680. display: 'flex',
  681. justifyContent: 'center',
  682. alignItems: 'center'
  683. }}>
  684. <FormItem
  685. label='max token'
  686. name='max_token'
  687. rules={[{required: true, message: 'max token不能为空'}]}>
  688. <InputNumber
  689. className='questionAnswerInfo-content-title'
  690. />
  691. </FormItem>
  692. </div>
  693. {
  694. !isVisible &&
  695. <div style={{
  696. display: 'flex',
  697. justifyContent: 'center',
  698. alignItems: 'center'
  699. }}>
  700. <a onClick={() => {
  701. setIsVisible(!isVisible);
  702. }} className='questionAnswerInfo-content-title'>
  703. 更多设置
  704. </a>
  705. </div>
  706. }
  707. {/* {isVisible && */}
  708. <div style={{display: isVisible ? 'block' : 'none', paddingTop: '20px'}}>
  709. {isVisibleCus &&
  710. <Space style={{width: '100%'}} direction="vertical">
  711. <div style={{
  712. display: 'flex',
  713. justifyContent: 'center',
  714. alignItems: 'center'
  715. }}>
  716. <FormItem
  717. label='Top-p'
  718. name='topP'
  719. style={{width: '300px'}}
  720. >
  721. <TopPDecimalStep/>
  722. </FormItem>
  723. </div>
  724. <div style={{
  725. display: 'flex',
  726. justifyContent: 'center',
  727. alignItems: 'center'
  728. }}>
  729. <FormItem
  730. label='Temperature'
  731. name='temperature'
  732. style={{width: '300px'}}
  733. >
  734. <TempDecimalStep/>
  735. </FormItem>
  736. </div>
  737. </Space>
  738. }
  739. <div style={{
  740. display: 'flex',
  741. justifyContent: 'center',
  742. alignItems: 'center'
  743. }}>
  744. <FormItem
  745. label='回答风格'
  746. name='param_desc'
  747. rules={[{required: true, message: '回答风格不能为空'}]}>
  748. <Radio.Group buttonStyle="solid"
  749. className='questionAnswerInfo-content-title'>
  750. <Radio.Button onClick={() => {
  751. handleRedioClick('strict')
  752. }} value='strict'>严谨</Radio.Button>
  753. <Radio.Button onClick={() => {
  754. handleRedioClick('moderate')
  755. }} value='moderate'>适中</Radio.Button>
  756. <Radio.Button onClick={() => {
  757. handleRedioClick('flexib')
  758. }} value='flexib'>发散</Radio.Button>
  759. <Radio.Button value='custom'
  760. onClick={() => {
  761. setIsVisibleCus(!isVisibleCus);
  762. setTopPValue(0.1);
  763. setTempValue(0.01);
  764. }}
  765. >自定义
  766. </Radio.Button>
  767. </Radio.Group>
  768. </FormItem>
  769. </div>
  770. <div style={{
  771. display: 'flex',
  772. justifyContent: 'center',
  773. alignItems: 'center'
  774. }}>
  775. <FormItem
  776. label='展示引用知识'
  777. name='show_recall_result'
  778. className='questionAnswerInfo-content-title'>
  779. <Switch onChange={onChangeShow}/>
  780. </FormItem>
  781. </div>
  782. <div style={{
  783. display: 'flex',
  784. justifyContent: 'center',
  785. alignItems: 'center'
  786. }}>
  787. <FormItem
  788. label='召回方式'
  789. name='recall_method'
  790. rules={[{required: true, message: '召回方式不能为空'}]}>
  791. <Radio.Group
  792. style={style}
  793. onChange={onChangeRecallMethod}
  794. options={[
  795. {value: 'embedding', label: '向量化检索'},
  796. {value: 'keyword', label: '关键词检索'},
  797. {value: 'mixed', label: '混合检索'},
  798. ]}
  799. />
  800. </FormItem>
  801. </div>
  802. <div style={{
  803. display: 'flex',
  804. justifyContent: 'center',
  805. alignItems: 'center'
  806. }}>
  807. <div className='questionAnswerInfo-content-title'>重排方式</div>
  808. </div>
  809. <div style={{
  810. display: 'flex',
  811. justifyContent: 'center',
  812. alignItems: 'center'
  813. }}>
  814. <FormItem
  815. label='Rerank模型'
  816. name='rerank_status'
  817. valuePropName='checked'
  818. className='questionAnswerInfo-content-title'
  819. >
  820. <Switch onChange={onChangeModel}/>
  821. </FormItem>
  822. </div>
  823. {isVisibleRerank &&
  824. <div style={{
  825. display: 'flex',
  826. justifyContent: 'center',
  827. alignItems: 'center'
  828. }}>
  829. <FormItem
  830. label='模型选择'
  831. name='rerank_model_name'
  832. >
  833. <Select
  834. style={{width: '300px', height: '48px'}}
  835. placeholder='请选择模型'
  836. // defaultValue={'rerank'}
  837. >
  838. <Option value='rerank'>默认rerank模型</Option>
  839. </Select>
  840. </FormItem>
  841. </div>
  842. }
  843. <div style={{
  844. display: 'flex',
  845. justifyContent: 'center',
  846. alignItems: 'center'
  847. }}>
  848. <FormItem
  849. label='召回切片数量'
  850. name='slice_config_type'
  851. rules={[{required: true, message: '召回方式不能为空'}]}>
  852. <Select
  853. style={{width: '300px', height: '48px'}}
  854. placeholder='请选择'
  855. onChange={onChangeCount}>
  856. <Option value="fixed">手动设置</Option>
  857. <Option value="customized">自动设置</Option>
  858. </Select>
  859. </FormItem>
  860. </div>
  861. {isVisibleSlice &&
  862. <div style={{
  863. display: 'flex',
  864. justifyContent: 'center',
  865. alignItems: 'center'
  866. }}>
  867. <FormItem
  868. label='召回切片数'
  869. name='slice_count'
  870. rules={[{required: true, message: '切片数不能为空'}]}>
  871. <InputNumber max={1024} changeOnWheel
  872. className='questionAnswerInfo-content-title'/>
  873. </FormItem>
  874. </div>
  875. }
  876. <div style={{
  877. display: 'flex',
  878. justifyContent: 'center',
  879. alignItems: 'center'
  880. }}>
  881. <FormItem
  882. label='召回切片拼接方式'
  883. name='recall_slice_splicing_method'
  884. >
  885. <TextArea
  886. rows={4}
  887. className='questionAnswerInfo-content-title'
  888. placeholder="请输入内容"
  889. />
  890. </FormItem>
  891. </div>
  892. </div>
  893. {/* } */}
  894. </div>
  895. </div>
  896. </Splitter.Panel>
  897. </Splitter>
  898. </div>
  899. </Form>
  900. </Spin>
  901. </div >
  902. );
  903. };
  904. export default observer(QuestionAnswerInfo);