Feat: Modify the data structure of the chunk in the conversation #3909 (#3955)

### What problem does this PR solve?

Feat: Modify the data structure of the chunk in the conversation #3909

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2024-12-10 16:36:16 +08:00 committed by GitHub
parent 03f00c9e6f
commit fc4e644e5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 304 additions and 202 deletions

View File

@ -63,7 +63,7 @@ const ChatApiKeyModal = ({
<Button <Button
onClick={createToken} onClick={createToken}
loading={creatingLoading} loading={creatingLoading}
disabled={tokenList.length > 0} disabled={tokenList?.length > 0}
> >
{t('createNewKey')} {t('createNewKey')}
</Button> </Button>

View File

@ -3,8 +3,7 @@ import apiDoc from '@parent/docs/references/http_api_reference.md';
import MarkdownPreview from '@uiw/react-markdown-preview'; import MarkdownPreview from '@uiw/react-markdown-preview';
import { Button, Card, Flex, Space } from 'antd'; import { Button, Card, Flex, Space } from 'antd';
import ChatApiKeyModal from '../chat-api-key-modal'; import ChatApiKeyModal from '../chat-api-key-modal';
import EmbedModal from '../embed-modal'; import { usePreviewChat } from '../hooks';
import { usePreviewChat, useShowEmbedModal } from '../hooks';
import BackendServiceApi from './backend-service-api'; import BackendServiceApi from './backend-service-api';
const ApiContent = ({ const ApiContent = ({
@ -22,10 +21,10 @@ const ApiContent = ({
hideModal: hideApiKeyModal, hideModal: hideApiKeyModal,
showModal: showApiKeyModal, showModal: showApiKeyModal,
} = useSetModalState(); } = useSetModalState();
const { embedVisible, hideEmbedModal, showEmbedModal, embedToken } = // const { embedVisible, hideEmbedModal, showEmbedModal, embedToken } =
useShowEmbedModal(idKey, id); // useShowEmbedModal(idKey);
const { handlePreview } = usePreviewChat(idKey, id); const { handlePreview } = usePreviewChat(idKey);
return ( return (
<div> <div>
@ -36,7 +35,9 @@ const ApiContent = ({
<Flex gap={8} vertical> <Flex gap={8} vertical>
<Space size={'middle'}> <Space size={'middle'}>
<Button onClick={handlePreview}>{t('preview')}</Button> <Button onClick={handlePreview}>{t('preview')}</Button>
<Button onClick={showEmbedModal}>{t('embedded')}</Button> {/* <Button onClick={() => showEmbedModal(id)}>
{t('embedded')}
</Button> */}
</Space> </Space>
</Flex> </Flex>
</Card> </Card>
@ -50,13 +51,13 @@ const ApiContent = ({
idKey={idKey} idKey={idKey}
></ChatApiKeyModal> ></ChatApiKeyModal>
)} )}
{embedVisible && ( {/* {embedVisible && (
<EmbedModal <EmbedModal
token={embedToken} token={embedToken}
visible={embedVisible} visible={embedVisible}
hideModal={hideEmbedModal} hideModal={hideEmbedModal}
></EmbedModal> ></EmbedModal>
)} )} */}
</div> </div>
); );
}; };

View File

@ -6,3 +6,7 @@
padding: 10px; padding: 10px;
background-color: #ffffff09; background-color: #ffffff09;
} }
.id {
.linkText();
}

View File

@ -1,21 +1,33 @@
import CopyToClipboard from '@/components/copy-to-clipboard'; import CopyToClipboard from '@/components/copy-to-clipboard';
import HightLightMarkdown from '@/components/highlight-markdown'; import HightLightMarkdown from '@/components/highlight-markdown';
import { SharedFrom } from '@/constants/chat';
import { useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { Card, Modal, Tabs, TabsProps } from 'antd'; import { Card, Modal, Tabs, TabsProps, Typography } from 'antd';
import styles from './index.less'; import styles from './index.less';
const { Paragraph, Link } = Typography;
const EmbedModal = ({ const EmbedModal = ({
visible, visible,
hideModal, hideModal,
token = '', token = '',
}: IModalProps<any> & { token: string }) => { form,
beta = '',
isAgent,
}: IModalProps<any> & {
token: string;
form: SharedFrom;
beta: string;
isAgent: boolean;
}) => {
const { t } = useTranslate('chat'); const { t } = useTranslate('chat');
const text = ` const text = `
~~~ html ~~~ html
<iframe <iframe
src="${location.origin}/chat/share?shared_id=${token}" src="${location.origin}/chat/share?shared_id=${token}&from=${form}&auth=${beta}"
style="width: 100%; height: 100%; min-height: 600px" style="width: 100%; height: 100%; min-height: 600px"
frameborder="0" frameborder="0"
> >
@ -63,6 +75,23 @@ const EmbedModal = ({
onCancel={hideModal} onCancel={hideModal}
> >
<Tabs defaultActiveKey="1" items={items} onChange={onChange} /> <Tabs defaultActiveKey="1" items={items} onChange={onChange} />
<div className="text-base font-medium mt-4 mb-1">
{t(isAgent ? 'flow' : 'chat', { keyPrefix: 'header' })}
<span className="ml-1 inline-block">ID</span>
</div>
<Paragraph copyable={{ text: token }} className={styles.id}>
{token}
</Paragraph>
<Link
href={
isAgent
? 'https://ragflow.io/docs/dev/http_api_reference#create-session-with-an-agent'
: 'https://ragflow.io/docs/dev/http_api_reference#create-session-with-chat-assistant'
}
target="_blank"
>
{t('howUseId')}
</Link>
</Modal> </Modal>
); );
}; };

View File

@ -6,6 +6,7 @@ import {
} from '@/hooks/common-hooks'; } from '@/hooks/common-hooks';
import { import {
useCreateSystemToken, useCreateSystemToken,
useFetchManualSystemTokenList,
useFetchSystemTokenList, useFetchSystemTokenList,
useRemoveSystemToken, useRemoveSystemToken,
} from '@/hooks/user-setting-hooks'; } from '@/hooks/user-setting-hooks';
@ -17,9 +18,7 @@ import { useCallback } from 'react';
export const useOperateApiKey = (idKey: string, dialogId?: string) => { export const useOperateApiKey = (idKey: string, dialogId?: string) => {
const { removeToken } = useRemoveSystemToken(); const { removeToken } = useRemoveSystemToken();
const { createToken, loading: creatingLoading } = useCreateSystemToken(); const { createToken, loading: creatingLoading } = useCreateSystemToken();
const { data: tokenList, loading: listLoading } = useFetchSystemTokenList({ const { data: tokenList, loading: listLoading } = useFetchSystemTokenList();
[idKey]: dialogId,
});
const showDeleteConfirm = useShowDeleteConfirm(); const showDeleteConfirm = useShowDeleteConfirm();
@ -72,49 +71,68 @@ export const useShowTokenEmptyError = () => {
return { showTokenEmptyError }; return { showTokenEmptyError };
}; };
export const useShowBetaEmptyError = () => {
const { t } = useTranslate('chat');
const showBetaEmptyError = useCallback(() => {
message.error(t('betaError'));
}, [t]);
return { showBetaEmptyError };
};
const getUrlWithToken = (token: string, from: string = 'chat') => { const getUrlWithToken = (token: string, from: string = 'chat') => {
const { protocol, host } = window.location; const { protocol, host } = window.location;
return `${protocol}//${host}/chat/share?shared_id=${token}&from=${from}`; return `${protocol}//${host}/chat/share?shared_id=${token}&from=${from}`;
}; };
const useFetchTokenListBeforeOtherStep = (idKey: string, dialogId?: string) => { const useFetchTokenListBeforeOtherStep = () => {
const { showTokenEmptyError } = useShowTokenEmptyError(); const { showTokenEmptyError } = useShowTokenEmptyError();
const { showBetaEmptyError } = useShowBetaEmptyError();
const { data: tokenList, refetch } = useFetchSystemTokenList({ const { data: tokenList, fetchSystemTokenList } =
[idKey]: dialogId, useFetchManualSystemTokenList();
});
const token = let token = '',
beta = '';
if (Array.isArray(tokenList) && tokenList.length > 0) {
token = tokenList[0].token;
beta = tokenList[0].beta;
}
token =
Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : ''; Array.isArray(tokenList) && tokenList.length > 0 ? tokenList[0].token : '';
const handleOperate = useCallback(async () => { const handleOperate = useCallback(async () => {
const ret = await refetch(); const ret = await fetchSystemTokenList();
const list = ret.data; const list = ret;
if (Array.isArray(list) && list.length > 0) { if (Array.isArray(list) && list.length > 0) {
if (!list[0].beta) {
showBetaEmptyError();
return false;
}
return list[0]?.token; return list[0]?.token;
} else { } else {
showTokenEmptyError(); showTokenEmptyError();
return false; return false;
} }
}, [showTokenEmptyError, refetch]); }, [fetchSystemTokenList, showBetaEmptyError, showTokenEmptyError]);
return { return {
token, token,
beta,
handleOperate, handleOperate,
}; };
}; };
export const useShowEmbedModal = (idKey: string, dialogId?: string) => { export const useShowEmbedModal = () => {
const { const {
visible: embedVisible, visible: embedVisible,
hideModal: hideEmbedModal, hideModal: hideEmbedModal,
showModal: showEmbedModal, showModal: showEmbedModal,
} = useSetModalState(); } = useSetModalState();
const { handleOperate, token } = useFetchTokenListBeforeOtherStep( const { handleOperate, token, beta } = useFetchTokenListBeforeOtherStep();
idKey,
dialogId,
);
const handleShowEmbedModal = useCallback(async () => { const handleShowEmbedModal = useCallback(async () => {
const succeed = await handleOperate(); const succeed = await handleOperate();
@ -128,11 +146,12 @@ export const useShowEmbedModal = (idKey: string, dialogId?: string) => {
hideEmbedModal, hideEmbedModal,
embedVisible, embedVisible,
embedToken: token, embedToken: token,
beta,
}; };
}; };
export const usePreviewChat = (idKey: string, dialogId?: string) => { export const usePreviewChat = (idKey: string) => {
const { handleOperate } = useFetchTokenListBeforeOtherStep(idKey, dialogId); const { handleOperate } = useFetchTokenListBeforeOtherStep();
const open = useCallback( const open = useCallback(
(t: string) => { (t: string) => {

View File

@ -1,8 +1,7 @@
import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg'; import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
import { MessageType } from '@/constants/chat'; import { MessageType } from '@/constants/chat';
import { useSetModalState } from '@/hooks/common-hooks'; import { useSetModalState } from '@/hooks/common-hooks';
import { IReference } from '@/interfaces/database/chat'; import { IReference, IReferenceChunk } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge';
import classNames from 'classnames'; import classNames from 'classnames';
import { memo, useCallback, useEffect, useMemo, useState } from 'react'; import { memo, useCallback, useEffect, useMemo, useState } from 'react';
@ -31,7 +30,7 @@ interface IProps extends Partial<IRemoveMessageById>, IRegenerateMessage {
sendLoading?: boolean; sendLoading?: boolean;
nickname?: string; nickname?: string;
avatar?: string; avatar?: string;
clickDocumentButton?: (documentId: string, chunk: IChunk) => void; clickDocumentButton?: (documentId: string, chunk: IReferenceChunk) => void;
index: number; index: number;
showLikeButton?: boolean; showLikeButton?: boolean;
} }

View File

@ -1,14 +1,16 @@
import { useSetModalState } from '@/hooks/common-hooks'; import { useSetModalState } from '@/hooks/common-hooks';
import { IChunk } from '@/interfaces/database/knowledge'; import { IReferenceChunk } from '@/interfaces/database/chat';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
export const useClickDrawer = () => { export const useClickDrawer = () => {
const { visible, showModal, hideModal } = useSetModalState(); const { visible, showModal, hideModal } = useSetModalState();
const [selectedChunk, setSelectedChunk] = useState<IChunk>({} as IChunk); const [selectedChunk, setSelectedChunk] = useState<IReferenceChunk>(
{} as IReferenceChunk,
);
const [documentId, setDocumentId] = useState<string>(''); const [documentId, setDocumentId] = useState<string>('');
const clickDocumentButton = useCallback( const clickDocumentButton = useCallback(
(documentId: string, chunk: IChunk) => { (documentId: string, chunk: IReferenceChunk) => {
showModal(); showModal();
setSelectedChunk(chunk); setSelectedChunk(chunk);
setDocumentId(documentId); setDocumentId(documentId);

View File

@ -1,11 +1,12 @@
import { IModalProps } from '@/interfaces/common'; import { IModalProps } from '@/interfaces/common';
import { IReferenceChunk } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import { Drawer } from 'antd'; import { Drawer } from 'antd';
import DocumentPreviewer from '../pdf-previewer'; import DocumentPreviewer from '../pdf-previewer';
interface IProps extends IModalProps<any> { interface IProps extends IModalProps<any> {
documentId: string; documentId: string;
chunk: IChunk; chunk: IChunk | IReferenceChunk;
} }
export const PdfDrawer = ({ export const PdfDrawer = ({

View File

@ -2,7 +2,9 @@ import {
useGetChunkHighlights, useGetChunkHighlights,
useGetDocumentUrl, useGetDocumentUrl,
} from '@/hooks/document-hooks'; } from '@/hooks/document-hooks';
import { IReferenceChunk } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import FileError from '@/pages/document-viewer/file-error';
import { Skeleton } from 'antd'; import { Skeleton } from 'antd';
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { import {
@ -13,13 +15,12 @@ import {
PdfLoader, PdfLoader,
Popup, Popup,
} from 'react-pdf-highlighter'; } from 'react-pdf-highlighter';
import FileError from '@/pages/document-viewer/file-error';
import { useCatchDocumentError } from './hooks'; import { useCatchDocumentError } from './hooks';
import styles from './index.less'; import styles from './index.less';
interface IProps { interface IProps {
chunk: IChunk; chunk: IChunk | IReferenceChunk;
documentId: string; documentId: string;
visible: boolean; visible: boolean;
} }

View File

@ -504,11 +504,17 @@ export const useCreateNextSharedConversation = () => {
return { data, loading, createSharedConversation: mutateAsync }; return { data, loading, createSharedConversation: mutateAsync };
}; };
export const useFetchNextSharedConversation = (conversationId: string) => { // deprecated
export const useFetchNextSharedConversation = (
conversationId?: string | null,
) => {
const { data, isPending: loading } = useQuery({ const { data, isPending: loading } = useQuery({
queryKey: ['fetchSharedConversation'], queryKey: ['fetchSharedConversation'],
enabled: !!conversationId, enabled: !!conversationId,
queryFn: async () => { queryFn: async () => {
if (!conversationId) {
return {};
}
const { data } = await chatService.getExternalConversation( const { data } = await chatService.getExternalConversation(
null, null,
conversationId, conversationId,

View File

@ -1,3 +1,4 @@
import { IReferenceChunk } from '@/interfaces/database/chat';
import { IDocumentInfo } from '@/interfaces/database/document'; import { IDocumentInfo } from '@/interfaces/database/document';
import { IChunk } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import { IChangeParserConfigRequestBody } from '@/interfaces/request/document'; import { IChangeParserConfigRequestBody } from '@/interfaces/request/document';
@ -32,7 +33,9 @@ export const useGetDocumentUrl = (documentId?: string) => {
return getDocumentUrl; return getDocumentUrl;
}; };
export const useGetChunkHighlights = (selectedChunk: IChunk) => { export const useGetChunkHighlights = (
selectedChunk: IChunk | IReferenceChunk,
) => {
const [size, setSize] = useState({ width: 849, height: 1200 }); const [size, setSize] = useState({ width: 849, height: 1200 });
const highlights: IHighlight[] = useMemo(() => { const highlights: IHighlight[] = useMemo(() => {

View File

@ -12,6 +12,7 @@ import { PaginationProps, message } from 'antd';
import { FormInstance } from 'antd/lib'; import { FormInstance } from 'antd/lib';
import axios from 'axios'; import axios from 'axios';
import { EventSourceParserStream } from 'eventsource-parser/stream'; import { EventSourceParserStream } from 'eventsource-parser/stream';
import { omit } from 'lodash';
import { import {
ChangeEventHandler, ChangeEventHandler,
useCallback, useCallback,
@ -336,6 +337,7 @@ export const useSelectDerivedMessages = () => {
}), }),
prompt: answer.prompt, prompt: answer.prompt,
audio_binary: answer.audio_binary, audio_binary: answer.audio_binary,
...omit(answer, 'reference'),
}, },
]; ];
}); });

View File

@ -169,17 +169,34 @@ export const useFetchSystemStatus = () => {
}; };
}; };
export const useFetchSystemTokenList = (params: Record<string, any>) => { export const useFetchManualSystemTokenList = () => {
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: ['fetchManualSystemTokenList'],
mutationFn: async () => {
const { data } = await userService.listToken();
return data?.data ?? [];
},
});
return { data, loading, fetchSystemTokenList: mutateAsync };
};
export const useFetchSystemTokenList = () => {
const { const {
data, data,
isFetching: loading, isFetching: loading,
refetch, refetch,
} = useQuery<IToken[]>({ } = useQuery<IToken[]>({
queryKey: ['fetchSystemTokenList', params], queryKey: ['fetchSystemTokenList'],
initialData: [], initialData: [],
gcTime: 0, gcTime: 0,
queryFn: async () => { queryFn: async () => {
const { data } = await userService.listToken(params); const { data } = await userService.listToken();
return data?.data ?? []; return data?.data ?? [];
}, },
@ -213,6 +230,7 @@ export const useRemoveSystemToken = () => {
export const useCreateSystemToken = () => { export const useCreateSystemToken = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { const {
data, data,
isPending: loading, isPending: loading,

View File

@ -1,5 +1,4 @@
import { MessageType } from '@/constants/chat'; import { MessageType } from '@/constants/chat';
import { IChunk } from './knowledge';
export interface PromptConfig { export interface PromptConfig {
empty_response: string; empty_response: string;
@ -35,7 +34,7 @@ export interface IDialog {
description: string; description: string;
icon: string; icon: string;
id: string; id: string;
dialog_id?: string; dialog_id: string;
kb_ids: string[]; kb_ids: string[];
kb_names: string[]; kb_names: string[];
language: string; language: string;
@ -75,8 +74,21 @@ export interface Message {
audio_binary?: string; audio_binary?: string;
} }
export interface IReferenceChunk {
id: string;
content: null;
document_id: string;
document_name: string;
dataset_id: string;
image_id: string;
similarity: number;
vector_similarity: number;
term_similarity: number;
positions: number[];
}
export interface IReference { export interface IReference {
chunks: IChunk[]; chunks: IReferenceChunk[];
doc_aggs: Docagg[]; doc_aggs: Docagg[];
total: number; total: number;
} }
@ -117,6 +129,7 @@ export interface IToken {
token: string; token: string;
update_date?: any; update_date?: any;
update_time?: any; update_time?: any;
beta: string;
} }
export interface IStats { export interface IStats {

View File

@ -2,11 +2,11 @@ import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector'; import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next'; import { initReactI18next } from 'react-i18next';
import { createTranslationTable, flattenObject } from './until';
import translation_en from './en'; import translation_en from './en';
import translation_es from './es'; import translation_es from './es';
import translation_id from './id'; import translation_id from './id';
import translation_ja from './ja'; import translation_ja from './ja';
import { createTranslationTable, flattenObject } from './until';
import translation_vi from './vi'; import translation_vi from './vi';
import translation_zh from './zh'; import translation_zh from './zh';
import translation_zh_traditional from './zh-traditional'; import translation_zh_traditional from './zh-traditional';

View File

@ -432,6 +432,7 @@ The above is the content you need to summarize.`,
partialTitle: 'Partial Embed', partialTitle: 'Partial Embed',
extensionTitle: 'Chrome Extension', extensionTitle: 'Chrome Extension',
tokenError: 'Please create API Token first!', tokenError: 'Please create API Token first!',
betaError: 'The beta field of the API Token cannot be empty!',
searching: 'searching...', searching: 'searching...',
parsing: 'Parsing', parsing: 'Parsing',
uploading: 'Uploading', uploading: 'Uploading',

View File

@ -33,6 +33,7 @@ export default {
pleaseSelect: '選択してください', pleaseSelect: '選択してください',
pleaseInput: '入力してください', pleaseInput: '入力してください',
submit: '送信', submit: '送信',
japanese: '日本語',
}, },
login: { login: {
login: 'ログイン', login: 'ログイン',
@ -85,8 +86,7 @@ export default {
name: '名前', name: '名前',
namePlaceholder: '名前を入力してください', namePlaceholder: '名前を入力してください',
doc: 'ドキュメント', doc: 'ドキュメント',
datasetDescription: datasetDescription: '😉 パースが成功すると、質問と回答が可能になります。',
'😉 パースが成功すると、質問と回答が可能になります。',
addFile: 'ファイルを追加', addFile: 'ファイルを追加',
searchFiles: 'ファイルを検索', searchFiles: 'ファイルを検索',
localFiles: 'ローカルファイル', localFiles: 'ローカルファイル',
@ -157,8 +157,7 @@ export default {
topK: 'トップK', topK: 'トップK',
topKTip: `Kチャンクがリランキングモデルに供給されます。`, topKTip: `Kチャンクがリランキングモデルに供給されます。`,
delimiter: `区切り文字`, delimiter: `区切り文字`,
delimiterTip: delimiterTip: '複数文字の区切り文字をサポートしています。',
'複数文字の区切り文字をサポートしています。',
html4excel: 'ExcelをHTMLに変換', html4excel: 'ExcelをHTMLに変換',
html4excelTip: `有効にすると、スプレッドシートはHTMLテーブルとして解析されます。それ以外の場合、キーと値のペアとして解析されます。`, html4excelTip: `有効にすると、スプレッドシートはHTMLテーブルとして解析されます。それ以外の場合、キーと値のペアとして解析されます。`,
autoKeywords: '自動キーワード', autoKeywords: '自動キーワード',
@ -182,7 +181,7 @@ export default {
embeddingModelTip: embeddingModelTip:
'チャンクを埋め込みに変換するモデルです。一度チャンクが作成されると変更できません。', 'チャンクを埋め込みに変換するモデルです。一度チャンクが作成されると変更できません。',
permissionsTip: permissionsTip:
"「チーム」に設定すると、全てのチームメンバーがナレッジベースを管理できます。", '「チーム」に設定すると、全てのチームメンバーがナレッジベースを管理できます。',
chunkTokenNumberTip: chunkTokenNumberTip:
'チャンクのトークンしきい値を設定します。このしきい値を下回る段落は、次の段落と結合され、しきい値を超えた時点でチャンクが作成されます。', 'チャンクのトークンしきい値を設定します。このしきい値を下回る段落は、次の段落と結合され、しきい値を超えた時点でチャンクが作成されます。',
chunkMethod: 'チャンク方法', chunkMethod: 'チャンク方法',
@ -201,8 +200,7 @@ export default {
methodExamplesDescription: methodExamplesDescription:
'以下のスクリーンショットは明確な説明のために提供されています。', '以下のスクリーンショットは明確な説明のために提供されています。',
dialogueExamplesTitle: '会話の例', dialogueExamplesTitle: '会話の例',
methodEmpty: methodEmpty: 'ナレッジベースカテゴリの視覚的説明がここに表示されます',
'ナレッジベースカテゴリの視覚的説明がここに表示されます',
book: `<p>対応ファイル形式は<b>DOCX</b>, <b>PDF</b>, <b>TXT</b>です。</p><p> book: `<p>対応ファイル形式は<b>DOCX</b>, <b>PDF</b>, <b>TXT</b>です。</p><p>
PDF形式の書籍では<i></i></p>`, PDF形式の書籍では<i></i></p>`,
laws: `<p>対応ファイル形式は<b>DOCX</b>, <b>PDF</b>, <b>TXT</b>です。</p><p> laws: `<p>対応ファイル形式は<b>DOCX</b>, <b>PDF</b>, <b>TXT</b>です。</p><p>
@ -305,7 +303,7 @@ export default {
entityTypes: 'エンティティタイプ', entityTypes: 'エンティティタイプ',
pageRank: 'ページランク', pageRank: 'ページランク',
pageRankTip: `これは関連性スコアを高めるために使用されます。すべての取得されたチャンクの関連性スコアにこの数値が加算されます。 pageRankTip: `これは関連性スコアを高めるために使用されます。すべての取得されたチャンクの関連性スコアにこの数値が加算されます。
` `,
}, },
chunk: { chunk: {
chunk: 'チャンク', chunk: 'チャンク',
@ -360,8 +358,7 @@ export default {
{knowledge} {knowledge}
`, `,
systemMessage: '入力してください!', systemMessage: '入力してください!',
systemTip: systemTip: 'LLMが質問に答える際に従う指示を設定します。',
'LLMが質問に答える際に従う指示を設定します。',
topN: 'トップN', topN: 'トップN',
topNTip: `類似度スコアがしきい値を超えるチャンクのうち、上位N件のみがLLMに供給されます。`, topNTip: `類似度スコアがしきい値を超えるチャンクのうち、上位N件のみがLLMに供給されます。`,
variable: '変数', variable: '変数',
@ -406,7 +403,8 @@ export default {
quote: '引用を表示', quote: '引用を表示',
quoteTip: '元のテキストの出典を表示しますか?', quoteTip: '元のテキストの出典を表示しますか?',
selfRag: 'Self-RAG', selfRag: 'Self-RAG',
selfRagTip: '詳細は次を参照してくださいhttps://huggingface.co/papers/2310.11511', selfRagTip:
'詳細は次を参照してくださいhttps://huggingface.co/papers/2310.11511',
overview: 'チャットID', overview: 'チャットID',
pv: 'メッセージ数', pv: 'メッセージ数',
uv: 'アクティブユーザー数', uv: 'アクティブユーザー数',
@ -486,7 +484,7 @@ export default {
newPassword: '新しいパスワード', newPassword: '新しいパスワード',
newPasswordMessage: 'パスワードを入力してください!', newPasswordMessage: 'パスワードを入力してください!',
newPasswordDescription: newPasswordDescription:
'新しいパスワードは8文字以上でなければなりません。', '新しいパスワードは8文字以上でなければなりません。',
confirmPassword: '新しいパスワードの確認', confirmPassword: '新しいパスワードの確認',
confirmPasswordMessage: 'パスワードを確認してください!', confirmPasswordMessage: 'パスワードを確認してください!',
confirmPasswordNonMatchMessage: confirmPasswordNonMatchMessage:
@ -568,7 +566,7 @@ export default {
SparkModelNameMessage: 'Sparkモデルを選択してください', SparkModelNameMessage: 'Sparkモデルを選択してください',
addSparkAPIPassword: 'Spark APIパスワード', addSparkAPIPassword: 'Spark APIパスワード',
SparkAPIPasswordMessage: 'APIパスワードを入力してください', SparkAPIPasswordMessage: 'APIパスワードを入力してください',
addSparkAPPID: 'Spark APPID', addSparkAPPID: 'Spark APPID',
SparkAPPIDMessage: 'APPIDを入力してください', SparkAPPIDMessage: 'APPIDを入力してください',
addSparkAPISecret: 'Spark APIシークレット', addSparkAPISecret: 'Spark APIシークレット',
SparkAPISecretMessage: 'APIシークレットを入力してください', SparkAPISecretMessage: 'APIシークレットを入力してください',
@ -579,8 +577,7 @@ export default {
yiyanAKMessage: 'APIキーを入力してください', yiyanAKMessage: 'APIキーを入力してください',
addyiyanSK: 'yiyanシークレットキー', addyiyanSK: 'yiyanシークレットキー',
yiyanSKMessage: 'シークレットキーを入力してください', yiyanSKMessage: 'シークレットキーを入力してください',
FishAudioModelNameMessage: FishAudioModelNameMessage: '音声合成モデルに名前を付けてください',
'音声合成モデルに名前を付けてください',
addFishAudioAK: 'Fish Audio APIキー', addFishAudioAK: 'Fish Audio APIキー',
addFishAudioAKMessage: 'APIキーを入力してください', addFishAudioAKMessage: 'APIキーを入力してください',
addFishAudioRefID: 'FishAudio参照ID', addFishAudioRefID: 'FishAudio参照ID',
@ -700,7 +697,7 @@ export default {
relevantDescription: `LLMを使用して、上流の出力がユーザーの最新のクエリに関連しているかどうかを評価するコンポーネント。各判定結果に対して次のコンポーネントを指定してください。`, relevantDescription: `LLMを使用して、上流の出力がユーザーの最新のクエリに関連しているかどうかを評価するコンポーネント。各判定結果に対して次のコンポーネントを指定してください。`,
rewriteQuestionDescription: `ナレッジベースから関連情報を取得できなかった場合にユーザーのクエリを修正するコンポーネント。定義されたループの上限に達するまでこのプロセスを繰り返します。上流が「Relevant」、下流が「Retrieval」であることを確認してください。`, rewriteQuestionDescription: `ナレッジベースから関連情報を取得できなかった場合にユーザーのクエリを修正するコンポーネント。定義されたループの上限に達するまでこのプロセスを繰り返します。上流が「Relevant」、下流が「Retrieval」であることを確認してください。`,
messageDescription: messageDescription:
"静的メッセージを送信するコンポーネント。複数のメッセージが提供されている場合は、その中からランダムに1つを選択して送信します。下流がインターフェースコンポーネント「Answer」であることを確認してください。", '静的メッセージを送信するコンポーネント。複数のメッセージが提供されている場合は、その中からランダムに1つを選択して送信します。下流がインターフェースコンポーネント「Answer」であることを確認してください。',
keywordDescription: `ユーザーの入力からトップNの検索結果を取得するコンポーネント。使用前にTopNの値が適切に設定されていることを確認してください。`, keywordDescription: `ユーザーの入力からトップNの検索結果を取得するコンポーネント。使用前にTopNの値が適切に設定されていることを確認してください。`,
switchDescription: `前のコンポーネントの出力に基づいて条件を評価し、それに応じて実行の流れを指示するコンポーネント。ケースを定義し、各ケースのアクションまたは条件が満たされない場合のデフォルトアクションを指定することで、複雑な分岐ロジックを可能にします。`, switchDescription: `前のコンポーネントの出力に基づいて条件を評価し、それに応じて実行の流れを指示するコンポーネント。ケースを定義し、各ケースのアクションまたは条件が満たされない場合のデフォルトアクションを指定することで、複雑な分岐ロジックを可能にします。`,
wikipediaDescription: `wikipedia.orgから検索を行うコンポーネントで、TopNを使用して検索結果の数を指定します。既存のナレッジベースを補完します。`, wikipediaDescription: `wikipedia.orgから検索を行うコンポーネントで、TopNを使用して検索結果の数を指定します。既存のナレッジベースを補完します。`,
@ -800,7 +797,7 @@ export default {
news: 'ニュースと情報', news: 'ニュースと情報',
law: '法律と規制', law: '法律と規制',
contract: '契約', contract: '契約',
}, },
baiduSourceLangOptions: { baiduSourceLangOptions: {
auto: '自動検出', auto: '自動検出',
zh: '中国語', zh: '中国語',
@ -1089,4 +1086,4 @@ export default {
chat: 'チャット', chat: 'チャット',
}, },
}, },
}; };

View File

@ -414,6 +414,7 @@ export default {
partialTitle: '部分嵌入', partialTitle: '部分嵌入',
extensionTitle: 'Chrome 插件', extensionTitle: 'Chrome 插件',
tokenError: '請先創建 API Token!', tokenError: '請先創建 API Token!',
betaError: 'API Token的beta欄位不可以為空',
searching: '搜索中', searching: '搜索中',
parsing: '解析中', parsing: '解析中',
uploading: '上傳中', uploading: '上傳中',

View File

@ -431,6 +431,7 @@ export default {
partialTitle: '部分嵌入', partialTitle: '部分嵌入',
extensionTitle: 'Chrome 插件', extensionTitle: 'Chrome 插件',
tokenError: '请先创建 API Token!', tokenError: '请先创建 API Token!',
betaError: 'API Token的beta字段不可以为空',
searching: '搜索中', searching: '搜索中',
parsing: '解析中', parsing: '解析中',
uploading: '上传中', uploading: '上传中',

View File

@ -29,18 +29,20 @@ import {
useSelectDerivedConversationList, useSelectDerivedConversationList,
} from './hooks'; } from './hooks';
import EmbedModal from '@/components/api-service/embed-modal';
import { useShowEmbedModal } from '@/components/api-service/hooks';
import SvgIcon from '@/components/svg-icon'; import SvgIcon from '@/components/svg-icon';
import { useTheme } from '@/components/theme-provider'; import { useTheme } from '@/components/theme-provider';
import { SharedFrom } from '@/constants/chat';
import { import {
useClickConversationCard, useClickConversationCard,
useClickDialogCard, useClickDialogCard,
useFetchNextDialogList, useFetchNextDialogList,
useGetChatSearchParams, useGetChatSearchParams,
} from '@/hooks/chat-hooks'; } from '@/hooks/chat-hooks';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; import { useTranslate } from '@/hooks/common-hooks';
import { useSetSelectedRecord } from '@/hooks/logic-hooks'; import { useSetSelectedRecord } from '@/hooks/logic-hooks';
import { IDialog } from '@/interfaces/database/chat'; import { IDialog } from '@/interfaces/database/chat';
import ChatIdModal from './chat-id-modal';
import styles from './index.less'; import styles from './index.less';
const { Text } = Typography; const { Text } = Typography;
@ -82,13 +84,10 @@ const Chat = () => {
showDialogEditModal, showDialogEditModal,
} = useEditDialog(); } = useEditDialog();
const { t } = useTranslate('chat'); const { t } = useTranslate('chat');
const {
visible: overviewVisible,
hideModal: hideOverviewModal,
showModal: showOverviewModal,
} = useSetModalState();
const { currentRecord, setRecord } = useSetSelectedRecord<IDialog>(); const { currentRecord, setRecord } = useSetSelectedRecord<IDialog>();
const [controller, setController] = useState(new AbortController()); const [controller, setController] = useState(new AbortController());
const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
useShowEmbedModal();
const handleAppCardEnter = (id: string) => () => { const handleAppCardEnter = (id: string) => () => {
handleItemEnter(id); handleItemEnter(id);
@ -120,7 +119,7 @@ const Chat = () => {
info?.domEvent?.preventDefault(); info?.domEvent?.preventDefault();
info?.domEvent?.stopPropagation(); info?.domEvent?.stopPropagation();
setRecord(dialog); setRecord(dialog);
showOverviewModal(); showEmbedModal();
}; };
const handleRemoveConversation = const handleRemoveConversation =
@ -193,7 +192,7 @@ const Chat = () => {
label: ( label: (
<Space> <Space>
<KeyOutlined /> <KeyOutlined />
{t('overview')} {t('publish', { keyPrefix: 'flow' })}
</Space> </Space>
), ),
}, },
@ -374,14 +373,16 @@ const Chat = () => {
initialName={initialConversationName} initialName={initialConversationName}
loading={conversationRenameLoading} loading={conversationRenameLoading}
></RenameModal> ></RenameModal>
{overviewVisible && (
<ChatIdModal {embedVisible && (
visible={overviewVisible} <EmbedModal
hideModal={hideOverviewModal} visible={embedVisible}
id={currentRecord.id} hideModal={hideEmbedModal}
name={currentRecord.name} token={currentRecord.id}
idKey="dialogId" form={SharedFrom.Chat}
></ChatIdModal> beta={beta}
isAgent={false}
></EmbedModal>
)} )}
</Flex> </Flex>
); );

View File

@ -1,7 +1,6 @@
import Image from '@/components/image'; import Image from '@/components/image';
import SvgIcon from '@/components/svg-icon'; import SvgIcon from '@/components/svg-icon';
import { IReference } from '@/interfaces/database/chat'; import { IReference, IReferenceChunk } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge';
import { getExtension } from '@/utils/document-util'; import { getExtension } from '@/utils/document-util';
import { InfoCircleOutlined } from '@ant-design/icons'; import { InfoCircleOutlined } from '@ant-design/icons';
import { Button, Flex, Popover, Space } from 'antd'; import { Button, Flex, Popover, Space } from 'antd';
@ -11,6 +10,7 @@ import Markdown from 'react-markdown';
import reactStringReplace from 'react-string-replace'; import reactStringReplace from 'react-string-replace';
import SyntaxHighlighter from 'react-syntax-highlighter'; import SyntaxHighlighter from 'react-syntax-highlighter';
import rehypeKatex from 'rehype-katex'; import rehypeKatex from 'rehype-katex';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math'; import remarkMath from 'remark-math';
import { visitParents } from 'unist-util-visit-parents'; import { visitParents } from 'unist-util-visit-parents';
@ -36,7 +36,7 @@ const MarkdownContent = ({
content: string; content: string;
loading: boolean; loading: boolean;
reference: IReference; reference: IReference;
clickDocumentButton?: (documentId: string, chunk: IChunk) => void; clickDocumentButton?: (documentId: string, chunk: IReferenceChunk) => void;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { setDocumentIds, data: fileThumbnails } = const { setDocumentIds, data: fileThumbnails } =
@ -54,7 +54,7 @@ const MarkdownContent = ({
}, [reference, setDocumentIds]); }, [reference, setDocumentIds]);
const handleDocumentButtonClick = useCallback( const handleDocumentButtonClick = useCallback(
(documentId: string, chunk: IChunk, isPdf: boolean) => () => { (documentId: string, chunk: IReferenceChunk, isPdf: boolean) => () => {
if (!isPdf) { if (!isPdf) {
return; return;
} }
@ -85,15 +85,15 @@ const MarkdownContent = ({
const chunks = reference?.chunks ?? []; const chunks = reference?.chunks ?? [];
const chunkItem = chunks[chunkIndex]; const chunkItem = chunks[chunkIndex];
const document = reference?.doc_aggs?.find( const document = reference?.doc_aggs?.find(
(x) => x?.doc_id === chunkItem?.doc_id, (x) => x?.doc_id === chunkItem?.document_id,
); );
const documentId = document?.doc_id; const documentId = document?.doc_id;
const fileThumbnail = documentId ? fileThumbnails[documentId] : ''; const fileThumbnail = documentId ? fileThumbnails[documentId] : '';
const fileExtension = documentId ? getExtension(document?.doc_name) : ''; const fileExtension = documentId ? getExtension(document?.doc_name) : '';
const imageId = chunkItem?.img_id; const imageId = chunkItem?.image_id;
return ( return (
<Flex <Flex
key={chunkItem?.chunk_id} key={chunkItem?.id}
gap={10} gap={10}
className={styles.referencePopoverWrapper} className={styles.referencePopoverWrapper}
> >
@ -116,7 +116,7 @@ const MarkdownContent = ({
<Space direction={'vertical'}> <Space direction={'vertical'}>
<div <div
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(chunkItem?.content_with_weight), __html: DOMPurify.sanitize(chunkItem?.content ?? ''),
}} }}
className={styles.chunkContentText} className={styles.chunkContentText}
></div> ></div>
@ -176,7 +176,7 @@ const MarkdownContent = ({
return ( return (
<Markdown <Markdown
rehypePlugins={[rehypeWrapReference, rehypeKatex]} rehypePlugins={[rehypeWrapReference, rehypeKatex, rehypeRaw]}
remarkPlugins={[remarkGfm, remarkMath]} remarkPlugins={[remarkGfm, remarkMath]}
components={ components={
{ {

View File

@ -1,21 +1,23 @@
import MessageInput from '@/components/message-input'; import MessageInput from '@/components/message-input';
import MessageItem from '@/components/message-item'; import MessageItem from '@/components/message-item';
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
import { MessageType, SharedFrom } from '@/constants/chat'; import { MessageType, SharedFrom } from '@/constants/chat';
import { useFetchNextSharedConversation } from '@/hooks/chat-hooks';
import { useSendButtonDisabled } from '@/pages/chat/hooks'; import { useSendButtonDisabled } from '@/pages/chat/hooks';
import { Flex, Spin } from 'antd'; import { Flex, Spin } from 'antd';
import { forwardRef } from 'react'; import { forwardRef } from 'react';
import { import {
useCreateSharedConversationOnMount,
useGetSharedChatSearchParams, useGetSharedChatSearchParams,
useSendSharedMessage, useSendSharedMessage,
} from '../shared-hooks'; } from '../shared-hooks';
import { buildMessageItemReference } from '../utils'; import { buildMessageItemReference } from '../utils';
import PdfDrawer from '@/components/pdf-drawer';
import styles from './index.less'; import styles from './index.less';
const ChatContainer = () => { const ChatContainer = () => {
const { conversationId } = useCreateSharedConversationOnMount(); const { from, sharedId: conversationId } = useGetSharedChatSearchParams();
const { data } = useFetchNextSharedConversation(conversationId); const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
useClickDrawer();
const { const {
handlePressEnter, handlePressEnter,
@ -25,9 +27,13 @@ const ChatContainer = () => {
loading, loading,
ref, ref,
derivedMessages, derivedMessages,
} = useSendSharedMessage(conversationId); hasError,
} = useSendSharedMessage();
const sendDisabled = useSendButtonDisabled(value); const sendDisabled = useSendButtonDisabled(value);
const { from } = useGetSharedChatSearchParams();
if (!conversationId) {
return <div>empty</div>;
}
return ( return (
<> <>
@ -44,7 +50,7 @@ const ChatContainer = () => {
reference={buildMessageItemReference( reference={buildMessageItemReference(
{ {
message: derivedMessages, message: derivedMessages,
reference: data?.data?.reference, reference: [],
}, },
message, message,
)} )}
@ -54,6 +60,7 @@ const ChatContainer = () => {
derivedMessages?.length - 1 === i derivedMessages?.length - 1 === i
} }
index={i} index={i}
clickDocumentButton={clickDocumentButton}
></MessageItem> ></MessageItem>
); );
})} })}
@ -65,7 +72,7 @@ const ChatContainer = () => {
<MessageInput <MessageInput
isShared isShared
value={value} value={value}
disabled={false} disabled={hasError}
sendDisabled={sendDisabled} sendDisabled={sendDisabled}
conversationId={conversationId} conversationId={conversationId}
onInputChange={handleInputChange} onInputChange={handleInputChange}
@ -75,6 +82,14 @@ const ChatContainer = () => {
showUploadIcon={from === SharedFrom.Chat} showUploadIcon={from === SharedFrom.Chat}
></MessageInput> ></MessageInput>
</Flex> </Flex>
{visible && (
<PdfDrawer
visible={visible}
hideModal={hideModal}
documentId={documentId}
chunk={selectedChunk}
></PdfDrawer>
)}
</> </>
); );
}; };

View File

@ -1,83 +1,41 @@
import { MessageType, SharedFrom } from '@/constants/chat'; import { MessageType, SharedFrom } from '@/constants/chat';
import { import { useCreateNextSharedConversation } from '@/hooks/chat-hooks';
useCreateNextSharedConversation,
useFetchNextSharedConversation,
} from '@/hooks/chat-hooks';
import { import {
useSelectDerivedMessages, useSelectDerivedMessages,
useSendMessageWithSse, useSendMessageWithSse,
} from '@/hooks/logic-hooks'; } from '@/hooks/logic-hooks';
import { Message } from '@/interfaces/database/chat'; import { Message } from '@/interfaces/database/chat';
import api from '@/utils/api'; import { message } from 'antd';
import { get } from 'lodash';
import trim from 'lodash/trim'; import trim from 'lodash/trim';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { useSearchParams } from 'umi'; import { useSearchParams } from 'umi';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { useHandleMessageInputChange } from './hooks'; import { useHandleMessageInputChange } from './hooks';
export const useCreateSharedConversationOnMount = () => { const isCompletionError = (res: any) =>
const [currentQueryParameters] = useSearchParams(); res && (res?.response.status !== 200 || res?.data?.code !== 0);
const [conversationId, setConversationId] = useState('');
const { createSharedConversation: createConversation } =
useCreateNextSharedConversation();
const sharedId = currentQueryParameters.get('shared_id');
const userId = currentQueryParameters.get('user_id');
const setConversation = useCallback(async () => {
if (sharedId) {
const data = await createConversation(userId ?? undefined);
const id = data.data?.id;
if (id) {
setConversationId(id);
}
}
}, [createConversation, sharedId, userId]);
useEffect(() => {
setConversation();
}, [setConversation]);
return { conversationId };
};
export const useSelectNextSharedMessages = (conversationId: string) => {
const { data, loading } = useFetchNextSharedConversation(conversationId);
const {
derivedMessages,
ref,
setDerivedMessages,
addNewestAnswer,
addNewestQuestion,
removeLatestMessage,
} = useSelectDerivedMessages();
useEffect(() => {
setDerivedMessages(data?.data?.message);
}, [setDerivedMessages, data]);
return {
derivedMessages,
addNewestAnswer,
addNewestQuestion,
removeLatestMessage,
loading,
ref,
setDerivedMessages,
};
};
export const useSendButtonDisabled = (value: string) => { export const useSendButtonDisabled = (value: string) => {
return trim(value) === ''; return trim(value) === '';
}; };
export const useSendSharedMessage = (conversationId: string) => { export const useGetSharedChatSearchParams = () => {
const [searchParams] = useSearchParams();
return {
from: searchParams.get('from') as SharedFrom,
sharedId: searchParams.get('shared_id'),
};
};
export const useSendSharedMessage = () => {
const { from, sharedId: conversationId } = useGetSharedChatSearchParams();
const { createSharedConversation: setConversation } = const { createSharedConversation: setConversation } =
useCreateNextSharedConversation(); useCreateNextSharedConversation();
const { handleInputChange, value, setValue } = useHandleMessageInputChange(); const { handleInputChange, value, setValue } = useHandleMessageInputChange();
const { send, answer, done } = useSendMessageWithSse( const { send, answer, done } = useSendMessageWithSse(
api.completeExternalConversation, `/api/v1/${from === SharedFrom.Agent ? 'agentbots' : 'chatbots'}/${conversationId}/completions`,
); );
const { const {
derivedMessages, derivedMessages,
@ -85,24 +43,25 @@ export const useSendSharedMessage = (conversationId: string) => {
removeLatestMessage, removeLatestMessage,
addNewestAnswer, addNewestAnswer,
addNewestQuestion, addNewestQuestion,
loading, } = useSelectDerivedMessages();
} = useSelectNextSharedMessages(conversationId); const [hasError, setHasError] = useState(false);
const sendMessage = useCallback( const sendMessage = useCallback(
async (message: Message, id?: string) => { async (message: Message, id?: string) => {
const res = await send({ const res = await send({
conversation_id: id ?? conversationId, conversation_id: id ?? conversationId,
quote: false, quote: true,
messages: [...(derivedMessages ?? []), message], question: message.content,
session_id: get(derivedMessages, '0.session_id'),
}); });
if (res && (res?.response.status !== 200 || res?.data?.code !== 0)) { if (isCompletionError(res)) {
// cancel loading // cancel loading
setValue(message.content); setValue(message.content);
removeLatestMessage(); removeLatestMessage();
} }
}, },
[conversationId, derivedMessages, removeLatestMessage, setValue, send], [send, conversationId, derivedMessages, setValue, removeLatestMessage],
); );
const handleSendMessage = useCallback( const handleSendMessage = useCallback(
@ -120,6 +79,18 @@ export const useSendSharedMessage = (conversationId: string) => {
[conversationId, setConversation, sendMessage], [conversationId, setConversation, sendMessage],
); );
const fetchSessionId = useCallback(async () => {
const ret = await send({ question: '' });
if (isCompletionError(ret)) {
message.error(ret?.data.message);
setHasError(true);
}
}, [send]);
useEffect(() => {
fetchSessionId();
}, [fetchSessionId, send]);
useEffect(() => { useEffect(() => {
if (answer.answer) { if (answer.answer) {
addNewestAnswer(answer); addNewestAnswer(answer);
@ -154,16 +125,8 @@ export const useSendSharedMessage = (conversationId: string) => {
value, value,
sendLoading: !done, sendLoading: !done,
ref, ref,
loading, loading: false,
derivedMessages, derivedMessages,
}; hasError,
};
export const useGetSharedChatSearchParams = () => {
const [searchParams] = useSearchParams();
return {
from: searchParams.get('from') as SharedFrom,
sharedId: searchParams.get('shared_id'),
}; };
}; };

View File

@ -1,13 +1,15 @@
import ChatOverviewModal from '@/components/api-service/chat-overview-modal'; import EmbedModal from '@/components/api-service/embed-modal';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks'; import { useShowEmbedModal } from '@/components/api-service/hooks';
import { SharedFrom } from '@/constants/chat';
import { useTranslate } from '@/hooks/common-hooks';
import { useFetchFlow } from '@/hooks/flow-hooks'; import { useFetchFlow } from '@/hooks/flow-hooks';
import { ArrowLeftOutlined } from '@ant-design/icons'; import { ArrowLeftOutlined } from '@ant-design/icons';
import { Button, Flex, Space } from 'antd'; import { Button, Flex, Space } from 'antd';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { Link, useParams } from 'umi'; import { Link, useParams } from 'umi';
import FlowIdModal from '../flow-id-modal';
import { import {
useGetBeginNodeDataQuery, useGetBeginNodeDataQuery,
useGetBeginNodeDataQueryIsEmpty,
useSaveGraph, useSaveGraph,
useSaveGraphBeforeOpeningDebugDrawer, useSaveGraphBeforeOpeningDebugDrawer,
useWatchAgentChange, useWatchAgentChange,
@ -25,15 +27,16 @@ const FlowHeader = ({ showChatDrawer, chatDrawerVisible }: IProps) => {
const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer); const { handleRun } = useSaveGraphBeforeOpeningDebugDrawer(showChatDrawer);
const { data } = useFetchFlow(); const { data } = useFetchFlow();
const { t } = useTranslate('flow'); const { t } = useTranslate('flow');
const {
visible: overviewVisible,
hideModal: hideOverviewModal,
// showModal: showOverviewModal,
} = useSetModalState();
const { visible, hideModal, showModal } = useSetModalState();
const { id } = useParams(); const { id } = useParams();
const time = useWatchAgentChange(chatDrawerVisible); const time = useWatchAgentChange(chatDrawerVisible);
const getBeginNodeDataQuery = useGetBeginNodeDataQuery(); const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
useShowEmbedModal();
const isBeginNodeDataQueryEmpty = useGetBeginNodeDataQueryIsEmpty();
const handleShowEmbedModal = useCallback(() => {
showEmbedModal();
}, [showEmbedModal]);
const handleRunAgent = useCallback(() => { const handleRunAgent = useCallback(() => {
const query: BeginQuery[] = getBeginNodeDataQuery(); const query: BeginQuery[] = getBeginNodeDataQuery();
@ -70,23 +73,25 @@ const FlowHeader = ({ showChatDrawer, chatDrawerVisible }: IProps) => {
<Button type="primary" onClick={() => saveGraph()}> <Button type="primary" onClick={() => saveGraph()}>
<b>{t('save')}</b> <b>{t('save')}</b>
</Button> </Button>
{/* <Button type="primary" onClick={showOverviewModal} disabled> <Button
type="primary"
onClick={handleShowEmbedModal}
disabled={!isBeginNodeDataQueryEmpty}
>
<b>{t('publish')}</b> <b>{t('publish')}</b>
</Button> */}
<Button type="primary" onClick={showModal}>
<b>Agent ID</b>
</Button> </Button>
</Space> </Space>
</Flex> </Flex>
{overviewVisible && ( {embedVisible && (
<ChatOverviewModal <EmbedModal
visible={overviewVisible} visible={embedVisible}
hideModal={hideOverviewModal} hideModal={hideEmbedModal}
id={id!} token={id!}
idKey="canvasId" form={SharedFrom.Agent}
></ChatOverviewModal> beta={beta}
isAgent
></EmbedModal>
)} )}
{visible && <FlowIdModal hideModal={hideModal}></FlowIdModal>}
</> </>
); );
}; };

View File

@ -474,6 +474,20 @@ export const useGetBeginNodeDataQuery = () => {
return getBeginNodeDataQuery; return getBeginNodeDataQuery;
}; };
export const useGetBeginNodeDataQueryIsEmpty = () => {
const [isBeginNodeDataQueryEmpty, setIsBeginNodeDataQueryEmpty] =
useState(false);
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
const nodes = useGraphStore((state) => state.nodes);
useEffect(() => {
const query: BeginQuery[] = getBeginNodeDataQuery();
setIsBeginNodeDataQueryEmpty(query.length === 0);
}, [getBeginNodeDataQuery, nodes]);
return isBeginNodeDataQueryEmpty;
};
export const useSaveGraphBeforeOpeningDebugDrawer = (show: () => void) => { export const useSaveGraphBeforeOpeningDebugDrawer = (show: () => void) => {
const { saveGraph, loading } = useSaveGraph(); const { saveGraph, loading } = useSaveGraph();
const { resetFlow } = useResetFlow(); const { resetFlow } = useResetFlow();

View File

@ -47,9 +47,9 @@ const storage = {
}; };
export const getAuthorization = () => { export const getAuthorization = () => {
const sharedId = getSearchValue('shared_id'); const auth = getSearchValue('auth');
const authorization = sharedId const authorization = auth
? 'Bearer ' + sharedId ? 'Bearer ' + auth
: storage.getAuthorization() || ''; : storage.getAuthorization() || '';
return authorization; return authorization;

View File

@ -1,10 +1,12 @@
import { Images, SupportedPreviewDocumentTypes } from '@/constants/common'; import { Images, SupportedPreviewDocumentTypes } from '@/constants/common';
import { IReferenceChunk } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import { UploadFile } from 'antd'; import { UploadFile } from 'antd';
import { get } from 'lodash';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
export const buildChunkHighlights = ( export const buildChunkHighlights = (
selectedChunk: IChunk, selectedChunk: IChunk | IReferenceChunk,
size: { width: number; height: number }, size: { width: number; height: number },
) => { ) => {
return Array.isArray(selectedChunk?.positions) && return Array.isArray(selectedChunk?.positions) &&
@ -24,7 +26,11 @@ export const buildChunkHighlights = (
text: '', text: '',
emoji: '', emoji: '',
}, },
content: { text: selectedChunk.content_with_weight }, content: {
text:
get(selectedChunk, 'content_with_weight') ||
get(selectedChunk, 'content', ''),
},
position: { position: {
boundingRect: boundingRect, boundingRect: boundingRect,
rects: [boundingRect], rects: [boundingRect],