refactor: rerank item

This commit is contained in:
jialin
2024-11-22 10:00:34 +08:00
parent e05ec3d2d5
commit 419f1e07d1
15 changed files with 250 additions and 182 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ export default [
}, },
{ {
name: 'playground', name: 'playground',
icon: 'Comment', icon: 'ExperimentOutlined',
path: '/playground', path: '/playground',
routes: [ routes: [
{ {
+4
View File
@@ -132,6 +132,10 @@
gap: 8px; gap: 8px;
} }
.line-24 {
line-height: 24px;
}
.relative { .relative {
position: relative; position: relative;
} }
+4
View File
@@ -139,6 +139,10 @@ html {
} }
} }
.css-var-r11.ant-menu-css-var {
--ant-menu-item-color: var(--color-text-1);
}
.css-var-rf.ant-menu-css-var, .css-var-rf.ant-menu-css-var,
.css-var-ri.ant-menu-css-var, .css-var-ri.ant-menu-css-var,
.css-var-rh.ant-menu-css-var { .css-var-rh.ant-menu-css-var {
@@ -1,8 +1,13 @@
import useOverlayScroller from '@/hooks/use-overlay-scroller'; import useOverlayScroller from '@/hooks/use-overlay-scroller';
import useRequestToken from '@/hooks/use-request-token'; import useRequestToken from '@/hooks/use-request-token';
import { ClearOutlined, InboxOutlined } from '@ant-design/icons'; import {
ClearOutlined,
PlusOutlined,
SearchOutlined,
UploadOutlined
} from '@ant-design/icons';
import { useIntl, useSearchParams } from '@umijs/max'; import { useIntl, useSearchParams } from '@umijs/max';
import { Button, Spin } from 'antd'; import { Button, Divider, Spin, Tooltip } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import 'overlayscrollbars/overlayscrollbars.css'; import 'overlayscrollbars/overlayscrollbars.css';
import { import {
@@ -16,6 +21,7 @@ import {
import { rerankerQuery } from '../apis'; import { rerankerQuery } from '../apis';
import { MessageItem } from '../config/types'; import { MessageItem } from '../config/types';
import '../style/ground-left.less'; import '../style/ground-left.less';
import '../style/rerank.less';
import '../style/system-message-wrap.less'; import '../style/system-message-wrap.less';
import FileList from './file-list'; import FileList from './file-list';
import InputList from './input-list'; import InputList from './input-list';
@@ -52,6 +58,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
const controllerRef = useRef<any>(null); const controllerRef = useRef<any>(null);
const scroller = useRef<any>(null); const scroller = useRef<any>(null);
const currentMessageRef = useRef<any>(null); const currentMessageRef = useRef<any>(null);
const inputListRef = useRef<any>(null);
const paramsRef = useRef<any>(null); const paramsRef = useRef<any>(null);
const messageListLengthCache = useRef<number>(0); const messageListLengthCache = useRef<number>(0);
const requestToken = useRef<any>(null); const requestToken = useRef<any>(null);
@@ -128,7 +135,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
content: result.results?.map((item: any) => { content: result.results?.map((item: any) => {
return { return {
uid: item.index, uid: item.index,
text: `${item.document?.text?.slice(0, 100) || ''}`, text: `${item.document?.text?.slice(0, 500) || ''}`,
docIndex: item.index, docIndex: item.index,
title: documentList[item.index]?.name || '', title: documentList[item.index]?.name || '',
score: item.relevance_score score: item.relevance_score
@@ -177,6 +184,9 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
return preList.filter((item) => item.uid !== uid); return preList.filter((item) => item.uid !== uid);
}); });
}; };
const handleAddText = () => {
inputListRef.current?.handleAdd();
};
const handleTextListChange = ( const handleTextListChange = (
list: { text: string; uid: number | string; name: string }[] list: { text: string; uid: number | string; name: string }[]
@@ -219,53 +229,92 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
}, [textList.length, fileList.length]); }, [textList.length, fileList.length]);
return ( return (
<div className="ground-left-wrapper"> <div className="ground-left-wrapper rerank">
<div className="ground-left"> <div className="ground-left">
<div className="message-list-wrap" ref={scroller}> <div className="center">
<> <div className="documents">
<div className="content"> <h3 className="m-b-20 m-l-10 flex-between flex-center font-size-14 line-24">
<RerankMessage <span>Documents</span>
dataList={messageList} </h3>
header={ <div className="flex-between m-b-8 p-l-8">
<div className="result-header"> <div className="flex gap-10">
<span className="title">Results</span> <UploadFile
<ReferenceParams handleUpdateFileList={handleUpdateFileList}
usage={tokenResult} accept={acceptType}
showOutput={false} >
></ReferenceParams> <Tooltip title={<span>Support: {acceptType}</span>}>
</div> <Button
} size="middle"
/> icon={<UploadOutlined></UploadOutlined>}
{loading && ( >
<Spin size="small"> Upload File
<div style={{ height: '46px' }}></div> </Button>
</Spin> </Tooltip>
)} </UploadFile>
<Button size="middle" onClick={handleAddText}>
<PlusOutlined />
Add Text
</Button>
</div>
<Button
type="text"
icon={<ClearOutlined />}
size="middle"
onClick={handleClearDocuments}
></Button>
</div> </div>
</> <div className="docs-wrapper">
</div> <InputList
<div ref={inputListRef}
style={{ textList={textList}
height: 70, onChange={handleTextListChange}
paddingLeft: 1 ></InputList>
}} <div style={{ marginTop: 8 }}>
> <FileList
<UploadFile fileList={fileList}
handleUpdateFileList={handleUpdateFileList} textListCount={textList.length || 0}
accept={acceptType} onDelete={handleDeleteFile}
></FileList>
</div>
</div>
</div>
<Divider
type="vertical"
style={{ margin: 0, height: '100%' }}
></Divider>
<div
className="message-list-wrap"
ref={scroller}
style={{ paddingInline: 16 }}
> >
<div style={{ backgroundColor: 'var(--color-fill-sider)' }}> <>
<InboxOutlined className="font-size-16" /> <div className="content">
<span className="m-l-10"> <RerankMessage
Click or drag file to this area to upload dataList={messageList}
</span> header={
</div> <div className="result-header">
<span className="text-tertiary">support {acceptType}</span> <span className="title">Results</span>
</UploadFile> <ReferenceParams
usage={tokenResult}
showOutput={false}
></ReferenceParams>
</div>
}
/>
{loading && (
<Spin size="small">
<div style={{ height: '46px' }}></div>
</Spin>
)}
</div>
</>
</div>
</div> </div>
<div className="ground-left-footer"> <div className="ground-left-footer">
<MessageInput <MessageInput
scope="reranker" scope="reranker"
submitIcon={<SearchOutlined className="font-size-16" />}
loading={loading} loading={loading}
disabled={!parameters.model} disabled={!parameters.model}
isEmpty={true} isEmpty={true}
@@ -277,6 +326,7 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
placeholer={intl.formatMessage({ placeholer={intl.formatMessage({
id: 'playground.input.keyword.holder' id: 'playground.input.keyword.holder'
})} })}
tools={<span className="p-l-8">Query</span>}
/> />
</div> </div>
</div> </div>
@@ -284,50 +334,15 @@ const GroundReranker: React.FC<MessageProps> = forwardRef((props, ref) => {
className={classNames('params-wrapper', { className={classNames('params-wrapper', {
collapsed: collapse collapsed: collapse
})} })}
style={{ ref={paramsRef}
overflow: 'hidden',
paddingBottom: 16
}}
> >
<div className="box" style={{ paddingInline: 0, height: '100%' }}> <div className="box" style={{ padding: collapse ? 0 : '0 16px' }}>
<div style={{ padding: collapse ? 0 : '0 16px' }}> <RerankerParams
<RerankerParams setParams={setParams}
setParams={setParams} params={parameters}
params={parameters} selectedModel={selectModel}
selectedModel={selectModel} modelList={modelList}
modelList={modelList} />
/>
<h3 className="m-b-20 m-l-10 flex-between flex-center font-size-14">
<span>Documents</span>
<Button
type="text"
icon={<ClearOutlined />}
size="middle"
onClick={handleClearDocuments}
></Button>
</h3>
</div>
<div
className="docs-wrapper"
ref={paramsRef}
style={{
height: 'calc(100% - 210px)',
padding: '0 16px',
overflowY: 'auto'
}}
>
<InputList
textList={textList}
onChange={handleTextListChange}
></InputList>
<div style={{ marginTop: 8 }}>
<FileList
fileList={fileList}
textListCount={textList.length || 0}
onDelete={handleDeleteFile}
></FileList>
</div>
</div>
</div> </div>
</div> </div>
+81 -75
View File
@@ -1,60 +1,64 @@
import RowTextarea from '@/components/seal-form/row-textarea'; import RowTextarea from '@/components/seal-form/row-textarea';
import { import { DeleteOutlined } from '@ant-design/icons';
MinusCircleOutlined,
PlusCircleOutlined,
PlusOutlined
} from '@ant-design/icons';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Button, Tooltip } from 'antd'; import { Button, Tooltip } from 'antd';
import React, { useRef } from 'react'; import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import '../style/input-list.less'; import '../style/input-list.less';
interface InputListProps { interface InputListProps {
ref?: any;
textList: { text: string; uid: number | string; name: string }[]; textList: { text: string; uid: number | string; name: string }[];
onChange?: ( onChange?: (
textList: { text: string; uid: number | string; name: string }[] textList: { text: string; uid: number | string; name: string }[]
) => void; ) => void;
} }
const InputList: React.FC<InputListProps> = ({ textList, onChange }) => { const InputList: React.FC<InputListProps> = forwardRef(
const intl = useIntl(); ({ textList, onChange }, ref) => {
const messageId = useRef(0); const intl = useIntl();
const messageId = useRef(0);
const setMessageId = () => { const setMessageId = () => {
messageId.current = messageId.current + 1; messageId.current = messageId.current + 1;
}; };
const handleAdd = () => { const handleAdd = () => {
setMessageId(); setMessageId();
const dataList = [...textList]; const dataList = [...textList];
dataList.push({ dataList.push({
text: '', text: '',
uid: messageId.current, uid: messageId.current,
name: `Text ${dataList.length + 1}` name: `Text ${dataList.length + 1}`
}); });
onChange?.(dataList); onChange?.(dataList);
}; };
const handleDelete = (text: { text: string; uid: number | string }) => { const handleDelete = (text: { text: string; uid: number | string }) => {
const dataList = [...textList]; const dataList = [...textList];
const index = dataList.findIndex((item) => item.uid === text.uid); const index = dataList.findIndex((item) => item.uid === text.uid);
dataList.splice(index, 1); dataList.splice(index, 1);
onChange?.(dataList); onChange?.(dataList);
}; };
const handleTextChange = ( const handleTextChange = (
value: string, value: string,
text: { text: string; uid: number | string } text: { text: string; uid: number | string }
) => { ) => {
const dataList = [...textList]; const dataList = [...textList];
const index = dataList.findIndex((item) => item.uid === text.uid); const index = dataList.findIndex((item) => item.uid === text.uid);
dataList[index].text = value; dataList[index].text = value;
onChange?.(dataList); onChange?.(dataList);
}; };
return ( useImperativeHandle(ref, () => ({
<div className="input-list"> handleAdd,
{textList.length === 0 && ( handleDelete,
handleTextChange
}));
return (
<div className="input-list">
{/* {textList.length === 0 && (
<Button <Button
block block
onClick={handleAdd} onClick={handleAdd}
@@ -64,47 +68,49 @@ const InputList: React.FC<InputListProps> = ({ textList, onChange }) => {
<PlusOutlined /> <PlusOutlined />
Add Text Add Text
</Button> </Button>
)} )} */}
{textList.map((text, index) => { {textList.map((text, index) => {
return ( return (
<div key={text.uid} className="input-item"> <div key={text.uid} className="input-item">
<div className="input-wrap"> <div className="input-wrap">
<RowTextarea <RowTextarea
label={`${index + 1}.`} label={`${index + 1}.`}
value={text.text} value={text.text}
placeholder="Input your text" placeholder="Input your text"
onChange={(e) => handleTextChange(e.target.value, text)} onChange={(e) => handleTextChange(e.target.value, text)}
></RowTextarea> ></RowTextarea>
</div> </div>
<span className="btn-group"> <span className="btn-group">
<Tooltip
title={intl.formatMessage({ id: 'common.button.delete' })}
>
<Button
size="small"
type="text"
icon={<MinusCircleOutlined />}
onClick={() => handleDelete(text)}
></Button>
</Tooltip>
{index === textList.length - 1 && (
<Tooltip <Tooltip
title={intl.formatMessage({ id: 'common.button.add' })} title={intl.formatMessage({ id: 'common.button.delete' })}
> >
<Button <Button
danger
size="small" size="small"
type="text" type="text"
icon={<PlusCircleOutlined />} icon={<DeleteOutlined></DeleteOutlined>}
onClick={handleAdd} onClick={() => handleDelete(text)}
></Button> ></Button>
</Tooltip> </Tooltip>
)} {/* {index === textList.length - 1 && (
</span> <Tooltip
</div> title={intl.formatMessage({ id: 'common.button.add' })}
); >
})} <Button
</div> size="small"
); type="text"
}; icon={<PlusCircleOutlined />}
onClick={handleAdd}
></Button>
</Tooltip>
)} */}
</span>
</div>
);
})}
</div>
);
}
);
export default React.memo(InputList); export default React.memo(InputList);
@@ -65,8 +65,10 @@ interface MessageInputProps {
instanceId: symbol; instanceId: symbol;
})[] })[]
) => void; ) => void;
submitIcon?: React.ReactNode;
presetPrompt?: (list: CurrentMessage[]) => void; presetPrompt?: (list: CurrentMessage[]) => void;
addMessage?: (message: CurrentMessage) => void; addMessage?: (message: CurrentMessage) => void;
tools?: React.ReactNode;
loading: boolean; loading: boolean;
showModelSelection?: boolean; showModelSelection?: boolean;
disabled: boolean; disabled: boolean;
@@ -90,7 +92,10 @@ const MessageInput: React.FC<MessageInputProps> = ({
disabled, disabled,
isEmpty, isEmpty,
scope, scope,
submitIcon,
placeholer, placeholer,
tools,
shouldResetMessage = true shouldResetMessage = true
}) => { }) => {
const { TextArea } = Input; const { TextArea } = Input;
@@ -345,6 +350,7 @@ const MessageInput: React.FC<MessageInputProps> = ({
<div className="messageInput"> <div className="messageInput">
<div className="tool-bar"> <div className="tool-bar">
<div className="actions"> <div className="actions">
{tools}
{scope !== 'reranker' && ( {scope !== 'reranker' && (
<> <>
<Button <Button
@@ -441,7 +447,9 @@ const MessageInput: React.FC<MessageInputProps> = ({
size="middle" size="middle"
disabled={isDisabled} disabled={isDisabled}
> >
<SendOutlined rotate={0} className="font-size-14" /> {submitIcon ?? (
<SendOutlined rotate={0} className="font-size-14" />
)}
</Button> </Button>
</Tooltip> </Tooltip>
) : ( ) : (
@@ -163,7 +163,7 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
<div> <div>
{showModelSelector && ( {showModelSelector && (
<> <>
<h3 className="m-b-20 m-l-10 font-size-14"> <h3 className="m-b-20 m-l-10 font-size-14 line-24">
{intl.formatMessage({ id: 'playground.model' })} {intl.formatMessage({ id: 'playground.model' })}
</h3> </h3>
<Form.Item<ParamsSettingsFormProps> <Form.Item<ParamsSettingsFormProps>
@@ -184,7 +184,7 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
</Form.Item> </Form.Item>
</> </>
)} )}
<h3 className="m-b-20 m-l-10 flex-between flex-center font-size-14"> <h3 className="m-b-20 m-l-10 flex-between flex-center font-size-14 line-24">
<span>{intl.formatMessage({ id: 'playground.parameters' })}</span> <span>{intl.formatMessage({ id: 'playground.parameters' })}</span>
</h3> </h3>
<Form.Item<ParamsSettingsFormProps> <Form.Item<ParamsSettingsFormProps>
@@ -148,6 +148,7 @@ const ParamsSettings: React.FC<ParamsSettingsProps> = ({
<div> <div>
{ {
<> <>
<h3 className="m-b-20 m-l-10 font-size-14 line-24">Parameters</h3>
<Form.Item<ParamsSettingsFormProps> <Form.Item<ParamsSettingsFormProps>
name="model" name="model"
rules={[ rules={[
@@ -116,7 +116,7 @@ const UploadImg: React.FC<UploadImgProps> = ({
return ( return (
<> <>
<Upload.Dragger <Upload
ref={uploadRef} ref={uploadRef}
multiple multiple
action="/" action="/"
@@ -132,7 +132,7 @@ const UploadImg: React.FC<UploadImgProps> = ({
<Button size={size} icon={<PaperClipOutlined />} /> <Button size={size} icon={<PaperClipOutlined />} />
</Tooltip> </Tooltip>
)} )}
</Upload.Dragger> </Upload>
</> </>
); );
}; };
+18 -1
View File
@@ -3,11 +3,12 @@ import { useIntl } from '@umijs/max';
import { Button, Tooltip, Upload } from 'antd'; import { Button, Tooltip, Upload } from 'antd';
import type { UploadFile } from 'antd/es/upload'; import type { UploadFile } from 'antd/es/upload';
import { RcFile } from 'antd/es/upload'; import { RcFile } from 'antd/es/upload';
import { debounce } from 'lodash'; import { debounce, round } from 'lodash';
import React, { useCallback, useRef } from 'react'; import React, { useCallback, useRef } from 'react';
interface UploadImgProps { interface UploadImgProps {
size?: 'small' | 'middle' | 'large'; size?: 'small' | 'middle' | 'large';
height?: number;
handleUpdateImgList: ( handleUpdateImgList: (
imgList: { dataUrl: string; uid: number | string }[] imgList: { dataUrl: string; uid: number | string }[]
) => void; ) => void;
@@ -15,6 +16,7 @@ interface UploadImgProps {
const UploadImg: React.FC<UploadImgProps> = ({ const UploadImg: React.FC<UploadImgProps> = ({
handleUpdateImgList, handleUpdateImgList,
height = 100,
size = 'small' size = 'small'
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
@@ -36,6 +38,21 @@ const UploadImg: React.FC<UploadImgProps> = ({
[handleUpdateImgList, intl] [handleUpdateImgList, intl]
); );
const getImgDimensions = useCallback(
(file: any): Promise<{ ratio: number }> => {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
resolve({ ratio: round(img.width / img.height, 2) });
};
img.onerror = () => {
resolve({ ratio: 1 });
};
img.src = URL.createObjectURL(file);
});
},
[]
);
const handleChange = useCallback( const handleChange = useCallback(
async (info: any) => { async (info: any) => {
const { fileList } = info; const { fileList } = info;
+3 -1
View File
@@ -190,7 +190,9 @@ const Playground: React.FC = () => {
header={{ header={{
title: ( title: (
<div className="flex items-center"> <div className="flex items-center">
{intl.formatMessage({ id: 'menu.playground.chat' })} <span className="font-600">
{intl.formatMessage({ id: 'menu.playground.chat' })}
</span>
{ {
<Segmented <Segmented
options={optionsList} options={optionsList}
+1 -5
View File
@@ -98,11 +98,7 @@ const PlaygroundRerank: React.FC = () => {
<PageContainer <PageContainer
ghost ghost
header={{ header={{
title: ( title: intl.formatMessage({ id: 'menu.playground.rerank' }),
<div className="flex items-center">
{intl.formatMessage({ id: 'menu.playground.rerank' })}
</div>
),
breadcrumb: {} breadcrumb: {}
}} }}
extra={renderExtra()} extra={renderExtra()}
+5 -7
View File
@@ -8,10 +8,15 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 0; padding: 0;
padding-right: 6px;
cursor: pointer; cursor: pointer;
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
border-bottom: 1px solid var(--ant-color-split); border-bottom: 1px solid var(--ant-color-split);
.btn-group {
display: none;
}
&:hover { &:hover {
background-color: var(--ant-color-fill-tertiary); background-color: var(--ant-color-fill-tertiary);
@@ -28,12 +33,5 @@
.input-wrap { .input-wrap {
flex: 1; flex: 1;
} }
.btn-group {
width: 60px;
margin-left: 8px;
display: flex;
gap: 8px;
}
} }
} }
+16
View File
@@ -0,0 +1,16 @@
.ground-left-wrapper.rerank {
.center {
display: flex;
height: 100%;
.documents {
width: 390px;
padding: 16px;
padding-left: 32px;
}
.message-list-wrap {
flex: 1;
}
}
}
+2 -1
View File
@@ -7,7 +7,8 @@ export default function readHtmlContent(file: File): Promise<string> {
const parser = new DOMParser(); const parser = new DOMParser();
const doc = parser.parseFromString(fileContent, 'text/html'); const doc = parser.parseFromString(fileContent, 'text/html');
const textContent = doc.body.textContent || ''; const textContent = doc.body.textContent || '';
resolve(textContent); const cleanedTextContent = textContent.replace(/[\t\n]/g, '').trim();
resolve(cleanedTextContent);
}; };
reader.onerror = (error) => reject(error); reader.onerror = (error) => reject(error);
reader.readAsText(file); reader.readAsText(file);