|
|
@@ -109,8 +109,14 @@ import { prettyObject } from "../utils/format";
|
|
|
import { ExportMessageModal } from "./exporter";
|
|
|
import { getClientConfig } from "../config/client";
|
|
|
import { useAllModels } from "../utils/hooks";
|
|
|
-import { Button, message, Popover, Select, Skeleton, Space } from 'antd';
|
|
|
-import { RightOutlined, CheckCircleOutlined } from '@ant-design/icons';
|
|
|
+import { Button, Collapse, Drawer, message, Popover, Select, Skeleton, Space } from 'antd';
|
|
|
+import {
|
|
|
+ FileOutlined,
|
|
|
+ FilePdfOutlined,
|
|
|
+ FileTextOutlined,
|
|
|
+ FileWordOutlined
|
|
|
+} from '@ant-design/icons';
|
|
|
+import { RightOutlined, CheckCircleOutlined, CaretRightOutlined, StarTwoTone } from '@ant-design/icons';
|
|
|
import api from "@/app/api/api";
|
|
|
|
|
|
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
|
|
|
@@ -1625,6 +1631,119 @@ function _Chat() {
|
|
|
}
|
|
|
}, [couldStop])
|
|
|
|
|
|
+ interface FileIconProps {
|
|
|
+ fileName: string;
|
|
|
+ }
|
|
|
+
|
|
|
+ const FileIcon: React.FC<FileIconProps> = (props: FileIconProps) => {
|
|
|
+ const style = {
|
|
|
+ fontSize: '30px',
|
|
|
+ color: '#3875f6',
|
|
|
+ }
|
|
|
+
|
|
|
+ let icon = <FileOutlined style={style} />
|
|
|
+ if (props.fileName) {
|
|
|
+ const suffix = props.fileName.split('.').pop() || '';
|
|
|
+ switch (suffix) {
|
|
|
+ case 'pdf':
|
|
|
+ icon = <FilePdfOutlined style={style} />
|
|
|
+ break;
|
|
|
+ case 'txt':
|
|
|
+ icon = <FileTextOutlined style={style} />
|
|
|
+ break;
|
|
|
+ case 'doc':
|
|
|
+ case 'docx':
|
|
|
+ icon = <FileWordOutlined style={style} />
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return icon;
|
|
|
+ }
|
|
|
+
|
|
|
+ const [drawerOpen, setDrawerOpen] = useState(false);
|
|
|
+ const [drawerData, setDrawerData] = useState({
|
|
|
+ knowledge_id: '',
|
|
|
+ doc_name: '',
|
|
|
+ chunk_info: {
|
|
|
+ doc_id: '',
|
|
|
+ chunk_list: [],
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ const SliceDrawer: React.FC = () => {
|
|
|
+ const [pageLoading, setPageLoading] = useState(false);
|
|
|
+ const [list, setList] = useState([]);
|
|
|
+
|
|
|
+ const init = async () => {
|
|
|
+ setPageLoading(true);
|
|
|
+ try {
|
|
|
+ const res: any = await api.post('/deepseek/api/slicePage', {
|
|
|
+ knowledge_id: drawerData.knowledge_id,
|
|
|
+ chunk_info: drawerData.chunk_info,
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 9999,
|
|
|
+ });
|
|
|
+ console.log(res.rows, 'res.rows');
|
|
|
+
|
|
|
+ setList(res.rows);
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
+ } finally {
|
|
|
+ setPageLoading(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ init()
|
|
|
+ }, [])
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Drawer
|
|
|
+ title={drawerData.doc_name}
|
|
|
+ loading={pageLoading}
|
|
|
+ open={drawerOpen}
|
|
|
+ onClose={() => {
|
|
|
+ setDrawerOpen(false);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {list.map((item: any, index) => {
|
|
|
+ const score = parseFloat(item.rerankScore);
|
|
|
+ const formattedScore = isNaN(score) ? '0.00' : score.toFixed(2);
|
|
|
+ return <div
|
|
|
+ style={{
|
|
|
+ padding: 10,
|
|
|
+ background: '#fafafa',
|
|
|
+ borderRadius: 4,
|
|
|
+ marginBottom: 10
|
|
|
+ }}
|
|
|
+ key={item.sliceId}
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ style={{
|
|
|
+ display: 'flex',
|
|
|
+ justifyContent: 'space-between',
|
|
|
+ alignItems: 'center',
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <div style={{ margin: '5px 0' }}>
|
|
|
+ 片段{index + 1}
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <StarTwoTone style={{ marginRight: 10 }} />
|
|
|
+ rerank得分{formattedScore}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ {item.sliceText}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ })}
|
|
|
+ </Drawer>
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
return (
|
|
|
<div className={styles.chat} key={session.id}>
|
|
|
{
|
|
|
@@ -1757,7 +1876,6 @@ function _Chat() {
|
|
|
messages.length > 1 ?
|
|
|
<>
|
|
|
{messages.map((message, i) => {
|
|
|
- console.log(message.sliceInfo, 'sliceInfo');
|
|
|
const isUser = message.role === "user";
|
|
|
const isContext = i < context.length;
|
|
|
const showActions =
|
|
|
@@ -1868,8 +1986,64 @@ function _Chat() {
|
|
|
}
|
|
|
{
|
|
|
message.sliceInfo &&
|
|
|
- <div>
|
|
|
- {/* 渲染切片 */}
|
|
|
+ <div style={{ marginTop: 10 }}>
|
|
|
+ <Collapse
|
|
|
+ bordered={false}
|
|
|
+ expandIcon={({ isActive }) => <CaretRightOutlined rotate={isActive ? 90 : 0} />}
|
|
|
+ items={[
|
|
|
+ {
|
|
|
+ key: '1',
|
|
|
+ label: `查询到“${message.sliceInfo.doc.length}条”相关切片`,
|
|
|
+ children: <div>
|
|
|
+ {message.sliceInfo.doc.map((item, index) => {
|
|
|
+ return <div
|
|
|
+ style={{
|
|
|
+ padding: 10,
|
|
|
+ background: '#FFFFFF',
|
|
|
+ borderRadius: 4,
|
|
|
+ display: 'flex',
|
|
|
+ justifyContent: 'space-between',
|
|
|
+ alignItems: 'center',
|
|
|
+ marginTop: index ? 10 : 0,
|
|
|
+ cursor: 'pointer',
|
|
|
+ }}
|
|
|
+ key={item.doc_id}
|
|
|
+ onClick={() => {
|
|
|
+ setDrawerData({
|
|
|
+ knowledge_id: message.sliceInfo!.knowledge_id,
|
|
|
+ doc_name: item.doc_name,
|
|
|
+ chunk_info: {
|
|
|
+ doc_id: item.doc_id,
|
|
|
+ chunk_list: item.chunk_info_list,
|
|
|
+ }
|
|
|
+ });
|
|
|
+ setDrawerOpen(true);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <div style={{ display: 'flex', alignItems: 'center' }}>
|
|
|
+ <FileIcon fileName={item.doc_name} />
|
|
|
+ <div style={{ marginLeft: 10 }}>
|
|
|
+ {item.doc_name}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div style={{
|
|
|
+ padding: 10,
|
|
|
+ background: '#FAFAFA',
|
|
|
+ borderRadius: 4,
|
|
|
+ marginLeft: 20,
|
|
|
+ }}>
|
|
|
+ {item.chunk_nums}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ })}
|
|
|
+ </div>,
|
|
|
+ }
|
|
|
+ ]}
|
|
|
+ />
|
|
|
+ {
|
|
|
+ drawerOpen &&
|
|
|
+ <SliceDrawer />
|
|
|
+ }
|
|
|
</div>
|
|
|
}
|
|
|
<div className={styles["chat-message-item"]}>
|