From 1a156e6569ba94bd3b156b6da18fdfb40daf08c4 Mon Sep 17 00:00:00 2001 From: balibabu <cike8899@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:12:31 +0800 Subject: [PATCH] feat: test document chunks (#62) --- web/src/interfaces/database/knowledge.ts | 27 +++++ .../components/knowledge-testing/index.tsx | 39 ++++++- .../components/knowledge-testing/model.ts | 72 ++++++++++++ .../testing-control/index.less | 3 + .../testing-control/index.tsx | 104 +++++++++++++----- .../testing-result/index.less | 20 ++++ .../testing-result/index.tsx | 82 ++++++++++++-- .../testing-result/select-files.tsx | 83 +++++++------- web/src/utils/api.ts | 2 +- 9 files changed, 341 insertions(+), 91 deletions(-) create mode 100644 web/src/pages/add-knowledge/components/knowledge-testing/model.ts diff --git a/web/src/interfaces/database/knowledge.ts b/web/src/interfaces/database/knowledge.ts index a98a04b..68b88da 100644 --- a/web/src/interfaces/database/knowledge.ts +++ b/web/src/interfaces/database/knowledge.ts @@ -75,3 +75,30 @@ export interface IChunk { img_id: string; important_kwd: any[]; } + +export interface ITestingChunk { + chunk_id: string; + content_ltks: string; + content_with_weight: string; + doc_id: string; + docnm_kwd: string; + img_id: string; + important_kwd: any[]; + kb_id: string; + similarity: number; + term_similarity: number; + vector: number[]; + vector_similarity: number; +} + +export interface ITestingDocument { + count: number; + doc_id: string; + doc_name: string; +} + +export interface ITestingResult { + chunks: ITestingChunk[]; + doc_aggs: Record<string, number>; + total: number; +} diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx index 5c4d22d..e3dd862 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx @@ -1,14 +1,47 @@ -import { Flex } from 'antd'; +import { Flex, Form } from 'antd'; import TestingControl from './testing-control'; import TestingResult from './testing-result'; +import { useKnowledgeBaseId } from '@/hooks/knowledgeHook'; +import { useEffect } from 'react'; +import { useDispatch } from 'umi'; import styles from './index.less'; const KnowledgeTesting = () => { + const [form] = Form.useForm(); + + const dispatch = useDispatch(); + const knowledgeBaseId = useKnowledgeBaseId(); + + const handleTesting = async () => { + const values = await form.validateFields(); + console.info(values); + const similarity_threshold = values.similarity_threshold / 100; + const vector_similarity_weight = values.vector_similarity_weight / 100; + dispatch({ + type: 'testingModel/testDocumentChunk', + payload: { + ...values, + similarity_threshold, + vector_similarity_weight, + kb_id: knowledgeBaseId, + }, + }); + }; + + useEffect(() => { + return () => { + dispatch({ type: 'testingModel/reset' }); + }; + }, [dispatch]); + return ( <Flex className={styles.testingWrapper} gap={16}> - <TestingControl></TestingControl> - <TestingResult></TestingResult> + <TestingControl + form={form} + handleTesting={handleTesting} + ></TestingControl> + <TestingResult handleTesting={handleTesting}></TestingResult> </Flex> ); }; diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/model.ts b/web/src/pages/add-knowledge/components/knowledge-testing/model.ts new file mode 100644 index 0000000..ac75807 --- /dev/null +++ b/web/src/pages/add-knowledge/components/knowledge-testing/model.ts @@ -0,0 +1,72 @@ +import { BaseState } from '@/interfaces/common'; +import { + ITestingChunk, + ITestingDocument, +} from '@/interfaces/database/knowledge'; +import kbService from '@/services/kbService'; +import { DvaModel } from 'umi'; + +export interface TestingModelState extends Pick<BaseState, 'pagination'> { + chunks: ITestingChunk[]; + documents: ITestingDocument[]; + total: number; + selectedDocumentIds: string[] | undefined; +} + +const initialState = { + chunks: [], + documents: [], + total: 0, + pagination: { + current: 1, + pageSize: 10, + }, + selectedDocumentIds: undefined, +}; + +const model: DvaModel<TestingModelState> = { + namespace: 'testingModel', + state: initialState, + reducers: { + setChunksAndDocuments(state, { payload }) { + return { + ...state, + ...payload, + }; + }, + setPagination(state, { payload }) { + return { ...state, pagination: { ...state.pagination, ...payload } }; + }, + setSelectedDocumentIds(state, { payload }) { + return { ...state, selectedDocumentIds: payload }; + }, + reset() { + return initialState; + }, + }, + effects: { + *testDocumentChunk({ payload = {} }, { call, put, select }) { + const { pagination, selectedDocumentIds }: TestingModelState = + yield select((state: any) => state.testingModel); + + const { data } = yield call(kbService.retrieval_test, { + ...payload, + doc_ids: selectedDocumentIds, + page: pagination.current, + size: pagination.pageSize, + }); + const { retcode, data: res } = data; + if (retcode === 0) { + yield put({ + type: 'setChunksAndDocuments', + payload: { + chunks: res.chunks, + documents: res.doc_aggs, + total: res.total, + }, + }); + } + }, + }, +}; +export default model; diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.less b/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.less index 42842a2..2e9f01a 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.less +++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.less @@ -2,6 +2,9 @@ width: 350px; background-color: white; padding: 30px 20px; + overflow: auto; + height: calc(100vh - 160px); + .historyTitle { padding: 30px 0 20px; } diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx index 97ef9d5..376feff 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx @@ -3,6 +3,7 @@ import { Card, Divider, Flex, + Form, Input, Slider, SliderSingleProps, @@ -11,50 +12,95 @@ import { } from 'antd'; import { DeleteOutlined, HistoryOutlined } from '@ant-design/icons'; +import { FormInstance } from 'antd/lib'; import styles from './index.less'; const list = [1, 2, 3]; const marks: SliderSingleProps['marks'] = { - 0: '0°C', - 26: '26°C', - 37: '37°C', - 100: { - style: { - color: '#f50', - }, - label: <strong>100°C</strong>, - }, + 0: '0', + 100: '1', }; -const TestingControl = () => { +type FieldType = { + similarity_threshold?: number; + vector_similarity_weight?: number; + top_k?: number; + question: string; +}; + +const formatter = (value: number | undefined) => { + return typeof value === 'number' ? value / 100 : 0; +}; + +const tooltip = { formatter }; + +interface IProps { + form: FormInstance; + handleTesting: () => Promise<any>; +} + +const TestingControl = ({ form, handleTesting }: IProps) => { + const question = Form.useWatch('question', { form, preserve: true }); + + const buttonDisabled = + !question || (typeof question === 'string' && question.trim() === ''); + return ( <section className={styles.testingControlWrapper}> <p> <b>Retrieval testing</b> </p> - <p>xxxx</p> + <p>Final step! After success, leave the rest to Infiniflow AI.</p> <Divider></Divider> <section> - <Slider range marks={marks} defaultValue={[26, 37]} /> - <Slider range marks={marks} defaultValue={[26, 37]} /> - <Card - size="small" - title="Test text" - extra={ - <Button type="primary" ghost> - Semantic Search - </Button> - } + <Form + name="testing" + layout="vertical" + form={form} + initialValues={{ + similarity_threshold: 20, + vector_similarity_weight: 30, + top_k: 1024, + }} > - <Input.TextArea autoSize={{ minRows: 8 }}></Input.TextArea> - <Flex justify={'space-between'}> - <Tag>10/200</Tag> - <Button type="primary" size="small"> - Testing - </Button> - </Flex> - </Card> + <Form.Item<FieldType> + label="Similarity threshold" + name={'similarity_threshold'} + > + <Slider marks={marks} defaultValue={0} tooltip={tooltip} /> + </Form.Item> + <Form.Item<FieldType> + label="Vector similarity weight" + name={'vector_similarity_weight'} + > + <Slider marks={marks} defaultValue={0} tooltip={tooltip} /> + </Form.Item> + <Form.Item<FieldType> label="Top k" name={'top_k'}> + <Slider marks={{ 0: 0, 2048: 2048 }} defaultValue={0} max={2048} /> + </Form.Item> + <Card size="small" title="Test text"> + <Form.Item<FieldType> + name={'question'} + rules={[ + { required: true, message: 'Please input your question!' }, + ]} + > + <Input.TextArea autoSize={{ minRows: 8 }}></Input.TextArea> + </Form.Item> + <Flex justify={'space-between'}> + <Tag>10/200</Tag> + <Button + type="primary" + size="small" + onClick={handleTesting} + disabled={buttonDisabled} + > + Testing + </Button> + </Flex> + </Card> + </Form> </section> <section> <p className={styles.historyTitle}> diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less index 2a1d8c0..46ef353 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less +++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less @@ -2,15 +2,35 @@ flex: 1; background-color: white; padding: 30px 20px; + overflow: auto; + height: calc(100vh - 160px); + display: flex; + flex-direction: column; + justify-content: space-between; .selectFilesCollapse { :global(.ant-collapse-header) { padding-left: 22px; } margin-bottom: 32px; + overflow-y: auto; } .selectFilesTitle { padding-right: 10px; } + + .similarityCircle { + width: 24px; + height: 24px; + border-radius: 50%; + background-color: rgba(244, 235, 255, 1); + font-size: 10px; + font-weight: normal; + } + + .similarityText { + font-size: 12px; + font-weight: 500; + } } diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx index 14a04ac..dbe7b22 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx @@ -1,12 +1,55 @@ import { ReactComponent as SelectedFilesCollapseIcon } from '@/assets/svg/selected-files-collapse.svg'; -import { Card, Collapse, Flex, Space } from 'antd'; +import { ITestingChunk } from '@/interfaces/database/knowledge'; +import { Card, Collapse, Flex, Pagination, PaginationProps, Space } from 'antd'; +import { useDispatch, useSelector } from 'umi'; +import { TestingModelState } from '../model'; +import styles from './index.less'; import SelectFiles from './select-files'; -import styles from './index.less'; +const similarityList: Array<{ field: keyof ITestingChunk; label: string }> = [ + { field: 'similarity', label: 'Hybrid Similarity' }, + { field: 'term_similarity', label: 'Term Similarity' }, + { field: 'vector_similarity', label: 'Vector Similarity' }, +]; + +const ChunkTitle = ({ item }: { item: ITestingChunk }) => { + return ( + <Flex gap={10}> + {similarityList.map((x) => ( + <Space key={x.field}> + <span className={styles.similarityCircle}> + {((item[x.field] as number) * 100).toFixed(2)}% + </span> + <span className={styles.similarityText}>Hybrid Similarity</span> + </Space> + ))} + </Flex> + ); +}; + +interface IProps { + handleTesting: () => Promise<any>; +} + +const TestingResult = ({ handleTesting }: IProps) => { + const { + documents, + chunks, + total, + pagination, + selectedDocumentIds, + }: TestingModelState = useSelector((state: any) => state.testingModel); + const dispatch = useDispatch(); -const list = [1, 2, 3, 4]; + const onChange: PaginationProps['onChange'] = (pageNumber, pageSize) => { + console.log('Page: ', pageNumber, pageSize); + dispatch({ + type: 'testingModel/setPagination', + payload: { current: pageNumber, pageSize }, + }); + handleTesting(); + }; -const TestingResult = () => { return ( <section className={styles.testingResultWrapper}> <Collapse @@ -23,7 +66,10 @@ const TestingResult = () => { align="center" className={styles.selectFilesTitle} > - <span>4/25 Files Selected</span> + <span> + {selectedDocumentIds?.length ?? 0}/{documents.length} Files + Selected + </span> <Space size={52}> <b>Hits</b> <b>View</b> @@ -32,21 +78,33 @@ const TestingResult = () => { ), children: ( <div> - <SelectFiles></SelectFiles> + <SelectFiles handleTesting={handleTesting}></SelectFiles> </div> ), }, ]} /> - <Flex gap={'large'} vertical> - {list.map((x) => ( - <Card key={x} title="Default size card" extra={<a href="#">More</a>}> - <p>Card content</p> - <p>Card content</p> - <p>Card content</p> + <Flex + gap={'large'} + vertical + flex={1} + className={styles.selectFilesCollapse} + > + {chunks.map((x) => ( + <Card key={x.chunk_id} title={<ChunkTitle item={x}></ChunkTitle>}> + <div>{x.content_with_weight}</div> </Card> ))} </Flex> + <Pagination + size={'small'} + showQuickJumper + current={pagination.current} + pageSize={pagination.pageSize} + total={total} + showSizeChanger + onChange={onChange} + /> </section> ); }; diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx index 0aee3dc..ab5d06c 100644 --- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx +++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx @@ -1,80 +1,71 @@ import { ReactComponent as NavigationPointerIcon } from '@/assets/svg/navigation-pointer.svg'; +import { ITestingDocument } from '@/interfaces/database/knowledge'; +import { api_host } from '@/utils/api'; import { Table, TableProps } from 'antd'; +import { useDispatch, useSelector } from 'umi'; -interface DataType { - key: string; - name: string; - hits: number; - address: string; - tags: string[]; +interface IProps { + handleTesting: () => Promise<any>; } -const SelectFiles = () => { - const columns: TableProps<DataType>['columns'] = [ +const SelectFiles = ({ handleTesting }: IProps) => { + const documents: ITestingDocument[] = useSelector( + (state: any) => state.testingModel.documents, + ); + + const dispatch = useDispatch(); + + const columns: TableProps<ITestingDocument>['columns'] = [ { title: 'Name', - dataIndex: 'name', - key: 'name', + dataIndex: 'doc_name', + key: 'doc_name', render: (text) => <p>{text}</p>, }, { title: 'Hits', - dataIndex: 'hits', - key: 'hits', + dataIndex: 'count', + key: 'count', width: 80, }, { title: 'View', key: 'view', width: 50, - render: () => <NavigationPointerIcon />, + render: (_, { doc_id }) => ( + <a + href={`${api_host}/document/get/${doc_id}`} + target="_blank" + rel="noreferrer" + > + <NavigationPointerIcon /> + </a> + ), }, ]; const rowSelection = { - onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => { - console.log( - `selectedRowKeys: ${selectedRowKeys}`, - 'selectedRows: ', - selectedRows, - ); + onChange: (selectedRowKeys: React.Key[]) => { + dispatch({ + type: 'testingModel/setSelectedDocumentIds', + payload: selectedRowKeys, + }); + handleTesting(); }, - getCheckboxProps: (record: DataType) => ({ - disabled: record.name === 'Disabled User', // Column configuration not to be checked - name: record.name, + getCheckboxProps: (record: ITestingDocument) => ({ + disabled: record.doc_name === 'Disabled User', // Column configuration not to be checked + name: record.doc_name, }), }; - const data: DataType[] = [ - { - key: '1', - name: 'John Brown', - hits: 32, - address: 'New York No. 1 Lake Park', - tags: ['nice', 'developer'], - }, - { - key: '2', - name: 'Jim Green', - hits: 42, - address: 'London No. 1 Lake Park', - tags: ['loser'], - }, - { - key: '3', - name: 'Joe Black', - hits: 32, - address: 'Sydney No. 1 Lake Park', - tags: ['cool', 'teacher'], - }, - ]; return ( <Table columns={columns} - dataSource={data} + dataSource={documents} showHeader={false} rowSelection={rowSelection} + rowKey={'doc_id'} /> ); }; diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index d7634ef..d0afb18 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -1,4 +1,4 @@ -let api_host = `http://223.111.148.200:9380/v1`; +let api_host = `http://123.60.95.134:9380/v1`; export { api_host }; -- GitLab